File size: 12,774 Bytes
eef08ae
 
 
1dc8f2c
eef08ae
 
 
937f734
eef08ae
9d8ee08
f21410a
 
 
 
 
eef08ae
 
 
 
 
 
 
 
 
 
 
 
 
39367b9
 
 
ee0e123
 
5f0a446
ee0e123
eef08ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1dc8f2c
eef08ae
937f734
eef08ae
ee0e123
eef08ae
 
937f734
eef08ae
 
 
 
937f734
eef08ae
 
 
 
 
 
 
 
 
 
 
937f734
 
 
 
eef08ae
ee0e123
1dc8f2c
ee0e123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eef08ae
 
ee0e123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e12cb38
eef08ae
 
 
ee0e123
 
 
 
 
 
 
 
eef08ae
 
1dc8f2c
 
 
 
f21410a
 
 
1dc8f2c
f21410a
 
 
 
 
21f4122
937f734
 
eef08ae
bf2b7a0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
252
253
254
255
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');

    // --- CORE & UI 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 = '';
        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);
                    });
                    aerodeckSection.classList.remove('hidden');
                }
                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));
    if (document.querySelector('a-scene')) {
        document.querySelector('a-scene').addEventListener('click', (event) => {
            if (event.target.hasAttribute('data-poi-id')) {
                const poiId = parseInt(event.target.getAttribute('data-poi-id'), 10);
                showObjectInfo(poiId);
            }
        });
    }
    closeObjectModal.addEventListener('click', () => objectModal.classList.add('hidden'));
    sendBtn.addEventListener('click', handleSearch);
    userInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleSearch(); });
    settingsBtn.addEventListener('click', () => { settingsModal.classList.remove('hidden'); });
    closeSettingsModal.addEventListener('click', () => { settingsModal.classList.add('hidden'); });
    settingsModal.addEventListener('click', (event) => { if (event.target === settingsModal) { settingsModal.classList.add('hidden'); } });
    const settingToggles = 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);
        });
    });

    // --- INITIALIZATION ---
    fetchPoisAndCreateAREntities();
  // --- FINAL SYSTEM CHECK ---
    const debugLog = document.getElementById('debug-log');
    if (debugLog) {
        let debugMessage = "--- FINAL SYSTEM CHECK ---\n\n";
        let testsPassed = true;

        // Test 1: Check for the settings button itself
        if (document.getElementById('settingsBtn')) {
            debugMessage += "βœ… Test 1: Settings Button HTML (id='settingsBtn') was FOUND.\n";
        } else {
            debugMessage += "❌ Test 1: Settings Button HTML (id='settingsBtn') was NOT FOUND.\n";
            testsPassed = false;
        }

        // Test 2: Check for the settings modal window
        if (document.getElementById('settingsModal')) {
            debugMessage += "βœ… Test 2: Settings Modal HTML (id='settingsModal') was FOUND.\n";
        } else {
            debugMessage += "❌ Test 2: Settings Modal HTML (id='settingsModal') was NOT FOUND.\n";
            testsPassed = false;
        }
        
        // Display the final results
        debugLog.textContent = debugMessage;
        
        if(testsPassed) {
            debugLog.style.borderColor = 'green';
            debugLog.style.color = 'green';
            debugLog.style.backgroundColor = '#DFF2BF';
            debugLog.textContent += "\nAll elements found. The button should be working.";
        }
    }
    // --- END OF SYSTEM CHECK ---
});