File size: 12,580 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
62afeca
f6318bf
1e8a708
 
 
62afeca
7aa4c23
 
 
 
 
 
 
 
f6318bf
1e8a708
 
7aa4c23
1e8a708
62afeca
7aa4c23
 
1e8a708
62afeca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1e8a708
62afeca
1e8a708
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7aa4c23
 
 
 
 
 
 
62afeca
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
// 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 (Corrected Version) ---
    
    // AR Mode Toggle
    if(arToggle) arToggle.addEventListener('click', () => toggleARView(!arActive));

    // Clicking on AR objects in the scene
    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);
            }
        });
    }

    // Info Modal Close Button
    if(closeObjectModal) closeObjectModal.addEventListener('click', () => objectModal.classList.add('hidden'));
    
    // Search Bar Send Button & Enter Key
    if(sendBtn) sendBtn.addEventListener('click', handleSearch);
    if(userInput) userInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleSearch(); });

    // Settings Modal Open/Close Logic
    if(settingsBtn) {
        settingsBtn.addEventListener('click', () => {
            if(settingsModal) settingsModal.classList.remove('hidden');
        });
    }
    if(closeSettingsModal) {
        closeSettingsModal.addEventListener('click', () => {
            if(settingsModal) settingsModal.classList.add('hidden');
        });
    }
    if(settingsModal) {
        settingsModal.addEventListener('click', (event) => {
            if (event.target === settingsModal) settingsModal.classList.add('hidden');
        });
    }

    // Glasses Modal Open/Close Logic
    if (glassesBtn) {
        glassesBtn.addEventListener('click', () => {
            if(glassesModal) glassesModal.classList.remove('hidden');
        });
    }
    if (closeGlassesModal) {
        closeGlassesModal.addEventListener('click', () => {
            if(glassesModal) glassesModal.classList.add('hidden');
        });
    }
    if (glassesModal) {
        glassesModal.addEventListener('click', (event) => {
            if (event.target === glassesModal) glassesModal.classList.add('hidden');
        });
    }

    // Settings Toggles Functionality
    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); // For testing
        });
    });
    // --- INITIALIZATION ---
    fetchPoisAndCreateAREntities();
});