File size: 12,129 Bytes
7aa4c23
eef08ae
 
 
1dc8f2c
eef08ae
 
 
937f734
eef08ae
9d8ee08
2d81b68
f21410a
eef08ae
 
 
 
 
 
 
 
 
 
 
 
 
39367b9
 
 
ee0e123
 
7076b44
 
 
 
7aa4c23
f6318bf
eef08ae
7aa4c23
 
 
 
 
 
 
 
 
 
 
eef08ae
 
 
7aa4c23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eef08ae
ee0e123
1dc8f2c
7aa4c23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eef08ae
 
ee0e123
7aa4c23
 
 
 
ee0e123
 
 
7aa4c23
 
 
 
ee0e123
 
 
7aa4c23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee0e123
e12cb38
eef08ae
f6318bf
eef08ae
7aa4c23
 
 
 
 
 
 
 
f6318bf
7aa4c23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a4e411b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7aa4c23
 
 
ee0e123
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// Final Corrected Version: June 13, 2025
document.addEventListener('DOMContentLoaded', () => {
    // --- CONFIGURATION ---
    const config = {
        backendUrl: 'https://ar-city-explorer-backend.aiagents.workers.dev'
    };

    // --- GLOBAL STATE ---
    let poisData = [];
    let arActive = false;
    let userSettings = {
        showHistorical: true, showMenus: true, showNavigation: false, voiceResponses: true
    };

    // --- DOM ELEMENT SELECTORS ---
    const arToggle = document.getElementById('arToggle');
    const arViewport = document.getElementById('arViewport');
    const normalView = document.getElementById('normalView');
    const aiAssistant = document.getElementById('aiAssistant');
    const userInput = document.getElementById('userInput');
    const sendBtn = document.getElementById('sendBtn');
    const objectModal = document.getElementById('objectModal');
    const objectTitle = document.getElementById('objectTitle');
    const objectDescription = document.getElementById('objectDescription');
    const objectImage = document.getElementById('objectImage');
    const closeObjectModal = document.getElementById('closeObjectModal');
    const settingsBtn = document.getElementById('settingsBtn');
    const settingsModal = document.getElementById('settingsModal');
    const closeSettingsModal = document.getElementById('closeSettingsModal');
    const aerodeckSection = document.getElementById('aerodeckSection');
    const aerodeckList = document.getElementById('aerodeckList');
    // Add these lines to the list of selectors
    const glassesBtn = document.getElementById('glassesBtn');
    const glassesModal = document.getElementById('glassesModal');
    const closeGlassesModal = document.getElementById('closeGlassesModal');
    // --- FUNCTIONS ---

    function toggleARView(showAR) {
        arActive = showAR;
        if (arActive) {
            normalView.classList.add('hidden');
            arViewport.classList.remove('hidden');
            arToggle.innerHTML = '<i class="fas fa-times mr-1"></i> Exit AR';
            if (poisData.length === 0) fetchPoisAndCreateAREntities();
        } else {
            arViewport.classList.add('hidden');
            normalView.classList.remove('hidden');
            arToggle.innerHTML = '<i class="fas fa-vr-cardboard mr-1"></i> AR Mode';
        }
    }

    function fetchPoisAndCreateAREntities() {
        fetch(`${config.backendUrl}/api/pois`)
            .then(response => { if (!response.ok) throw new Error('Network error'); return response.json(); })
            .then(pois => {
                poisData = pois;
                const scene = document.querySelector('a-scene');
                if (!scene) return;
                pois.forEach(poi => {
                    const entity = document.createElement('a-entity');
                    entity.setAttribute('gps-new-entity-place', { latitude: poi.latitude, longitude: poi.longitude });
                    const box = document.createElement('a-box');
                    box.setAttribute('material', 'color: red; opacity: 0.7;');
                    box.setAttribute('scale', '10 10 10');
                    box.setAttribute('position', '0 5 0');
                    box.setAttribute('data-poi-id', poi.id);
                    entity.appendChild(box);
                    const text = document.createElement('a-text');
                    text.setAttribute('value', poi.name);
                    text.setAttribute('look-at', '[gps-new-camera]');
                    text.setAttribute('scale', '50 50 50');
                    text.setAttribute('position', '0 15 0');
                    entity.appendChild(text);
                    scene.appendChild(entity);
                });
            })
            .catch(error => {
                console.error('Failed to load POIs:', error);
                alert('Could not load city data. Check connection and backend URL.');
                toggleARView(false);
            });
    }
    
    function showObjectInfo(poiId) {
        objectTitle.textContent = 'Loading...';
        objectDescription.textContent = '';
        if (aerodeckSection) aerodeckSection.classList.add('hidden');
        fetch(`${config.backendUrl}/api/pois/${poiId}`)
            .then(response => { if (!response.ok) throw new Error('POI not found'); return response.json(); })
            .then(data => {
                objectTitle.textContent = data.name;
                objectDescription.textContent = data.description || "No description available.";
                objectImage.src = `https://via.placeholder.com/300x200?text=${encodeURIComponent(data.name)}`;
                if (data.aerodecks && data.aerodecks.length > 0) {
                    aerodeckList.innerHTML = '';
                    data.aerodecks.forEach(deck => {
                        const itemElement = document.createElement('div');
                        itemElement.className = 'flex justify-between items-center p-2 bg-gray-100 rounded';
                        const statusColor = deck.status === 'Operational' ? 'text-green-500' : 'text-orange-500';
                        itemElement.innerHTML = `<div><div class="font-medium">${deck.deck_name}</div><div class="text-sm text-gray-600">Size: ${deck.size_meters}m | Charging: ${deck.is_charging_available ? 'Yes' : 'No'}</div></div><div class="font-bold text-sm ${statusColor}">${deck.status}</div>`;
                        aerodeckList.appendChild(itemElement);
                    });
                    if (aerodeckSection) aerodeckSection.classList.remove('hidden');
                }
                if (objectModal) objectModal.classList.remove('hidden');
            })
            .catch(error => { console.error("Error fetching POI details:", error); alert("Could not load details for this location."); });
    }

    function addUserMessage(message) {
        const chatContainer = aiAssistant.querySelector('.flex-col');
        const msg = `<div class="ai-message user-message"><p>${message}</p></div>`;
        chatContainer.insertAdjacentHTML('beforeend', msg);
        aiAssistant.scrollTop = aiAssistant.scrollHeight;
    }

    function addAIMessage(message) {
        const chatContainer = aiAssistant.querySelector('.flex-col');
        const msg = `<div class="ai-message assistant-message"><div class="font-bold text-indigo-800 mb-1">AR Guide</div><p>${message}</p></div>`;
        chatContainer.insertAdjacentHTML('beforeend', msg);
        aiAssistant.scrollTop = aiAssistant.scrollHeight;
    }

    function handleSearch() {
        const searchTerm = userInput.value.trim();
        if (!searchTerm) return;
        addUserMessage(searchTerm);
        userInput.value = '';
        const searchKeywords = ['search', 'find', 'show', 'where is', 'landmark', 'park', 'beach', 'hollywood', 'map', 'navigate'];
        const isLocalSearch = searchKeywords.some(keyword => searchTerm.toLowerCase().includes(keyword));
        if (isLocalSearch) {
            const stopWords = ['show', 'me', 'find', 'is', 'a', 'the', 'for', 'where', 'search', 'of', 'at'];
            const searchTokens = searchTerm.toLowerCase().split(' ').filter(word => !stopWords.includes(word) && word.length > 2);
            if (searchTokens.length === 0) return addAIMessage("Please be more specific in your local search.");
            const results = poisData.filter(poi => {
                const poiText = (poi.name + ' ' + (poi.description || '')).toLowerCase();
                return searchTokens.some(token => {
                    if (poiText.includes(token)) return true;
                    if (token.endsWith('s')) return poiText.includes(token.slice(0, -1));
                    return false;
                });
            });
            if (results.length > 0) {
                let responseMessage = `I found ${results.length} local result(s):<ul>`;
                results.forEach(poi => { responseMessage += `<li class="mt-2 list-disc list-inside">${poi.name}</li>`; });
                responseMessage += "</ul>";
                addAIMessage(responseMessage);
            } else {
                addAIMessage(`Sorry, I couldn't find any local places matching "${searchTerm}".`);
            }
        } else {
            addAIMessage("AI is thinking...");
            fetch(`${config.backendUrl}/api/ask`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ prompt: searchTerm })
            })
            .then(response => { if (!response.ok) throw new Error('Network error'); return response.json(); })
            .then(data => {
                const chatContainer = aiAssistant.querySelector('.flex-col');
                const thinkingMessage = Array.from(chatContainer.querySelectorAll('.assistant-message')).pop();
                if (thinkingMessage && thinkingMessage.textContent.includes("AI is thinking...")) {
                    thinkingMessage.querySelector('p').innerHTML = data.response.replace(/\n/g, '<br>');
                } else {
                    addAIMessage(data.response.replace(/\n/g, '<br>'));
                }
            })
            .catch(error => {
                console.error('Error asking AI:', error);
                const chatContainer = aiAssistant.querySelector('.flex-col');
                const thinkingMessage = Array.from(chatContainer.querySelectorAll('.assistant-message')).pop();
                if (thinkingMessage && thinkingMessage.textContent.includes("AI is thinking...")) {
                     thinkingMessage.querySelector('p').innerHTML = "Sorry, I had trouble connecting to the AI.";
                } else {
                    addAIMessage("Sorry, I had trouble connecting to the AI.");
                }
            });
        }
    }

    // --- EVENT LISTENERS ---
    
    arToggle.addEventListener('click', () => toggleARView(!arActive));
    const sceneEl = document.querySelector('a-scene');
    if (sceneEl) {
        sceneEl.addEventListener('click', (event) => {
            if (event.target.hasAttribute('data-poi-id')) {
                const poiId = parseInt(event.target.getAttribute('data-poi-id'), 10);
                showObjectInfo(poiId);
            }
        });
    }
    if(closeObjectModal) closeObjectModal.addEventListener('click', () => objectModal.classList.add('hidden'));
    if(sendBtn) sendBtn.addEventListener('click', handleSearch);
    if(userInput) userInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleSearch(); });
    if(settingsBtn) settingsBtn.addEventListener('click', () => { settingsModal.classList.remove('hidden'); });
    if(closeSettingsModal) closeSettingsModal.addEventListener('click', () => { settingsModal.classList.add('hidden'); });
    if(settingsModal) settingsModal.addEventListener('click', (event) => { if (event.target === settingsModal) { settingsModal.classList.add('hidden'); } });
    const settingToggles = settingsModal ? settingsModal.querySelectorAll('input[data-setting]') : [];
    settingToggles.forEach(toggle => {
        const settingName = toggle.dataset.setting;
        if (userSettings[settingName] !== undefined) { toggle.checked = userSettings[settingName]; }
        toggle.addEventListener('change', (event) => {
            const changedSettingName = event.target.dataset.setting;
            userSettings[changedSettingName] = event.target.checked;
            // console.log('Settings updated:', userSettings); // You can re-enable this for testing
        });
    });
  // --- Glasses Modal Open/Close ---
if (glassesBtn && glassesModal && closeGlassesModal) {
    glassesBtn.addEventListener('click', () => {
        glassesModal.classList.remove('hidden');
    });

    closeGlassesModal.addEventListener('click', () => {
        glassesModal.classList.add('hidden');
    });

    glassesModal.addEventListener('click', (event) => {
        if (event.target === glassesModal) {
            glassesModal.classList.add('hidden');
        }
    });
}

    // --- INITIALIZATION ---
    fetchPoisAndCreateAREntities();
});