/**
 * MLAT (Multilateration) Visualization for ESPILON C2
 * Supports Map view (Leaflet/OSM) and Plan view (Canvas)
 * Supports both GPS (lat/lon) and Local (x/y in meters) coordinates
 */

// ============================================================
// State
// ============================================================
let currentView = 'map';
let coordMode = 'gps';  // 'gps' or 'local'
let map = null;
let planCanvas = null;
let planCtx = null;
let planImage = null;

// Plan settings for local coordinate mode
let planSettings = {
    width: 50,      // meters
    height: 30,     // meters
    originX: 0,     // meters offset
    originY: 0      // meters offset
};

// Plan display options
let showGrid = true;
let showLabels = true;
let planZoom = 1.0;  // 1.0 = 100%
let panOffset = { x: 0, y: 0 };  // Pan offset in pixels
let isPanning = false;
let lastPanPos = { x: 0, y: 0 };

// Markers
let scannerMarkers = {};
let targetMarker = null;
let rangeCircles = {};

// Data
let scanners = [];
let target = null;

// ============================================================
// Map View (Leaflet) - GPS Mode
// ============================================================
function initMap() {
    if (map) return;

    const centerLat = parseFloat(document.getElementById('map-center-lat').value) || 48.8566;
    const centerLon = parseFloat(document.getElementById('map-center-lon').value) || 2.3522;
    const zoom = parseInt(document.getElementById('map-zoom').value) || 18;

    map = L.map('leaflet-map', {
        center: [centerLat, centerLon],
        zoom: zoom,
        zoomControl: true
    });

    // Dark tile layer (CartoDB Dark Matter)
    L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
        attribution: '&copy; <a href="https://carto.com/">CARTO</a>',
        subdomains: 'abcd',
        maxZoom: 20
    }).addTo(map);
}

function createScannerIcon() {
    return L.divIcon({
        className: 'scanner-marker',
        iconSize: [16, 16],
        iconAnchor: [8, 8]
    });
}

function createTargetIcon() {
    return L.divIcon({
        className: 'target-marker',
        html: `<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
            <circle cx="12" cy="12" r="10" fill="#f87171" fill-opacity="0.3"/>
            <circle cx="12" cy="12" r="6" fill="#f87171"/>
            <circle cx="12" cy="12" r="3" fill="#fff"/>
        </svg>`,
        iconSize: [24, 24],
        iconAnchor: [12, 12]
    });
}

function updateMapMarkers() {
    if (!map) return;

    // Only show GPS mode scanners on map
    const gpsFilteredScanners = scanners.filter(s => s.position && s.position.lat !== undefined);
    const currentIds = new Set(gpsFilteredScanners.map(s => s.id));

    // Remove old markers
    for (const id in scannerMarkers) {
        if (!currentIds.has(id)) {
            map.removeLayer(scannerMarkers[id]);
            delete scannerMarkers[id];
            if (rangeCircles[id]) {
                map.removeLayer(rangeCircles[id]);
                delete rangeCircles[id];
            }
        }
    }

    // Update/add scanner markers
    for (const scanner of gpsFilteredScanners) {
        const pos = scanner.position;

        if (scannerMarkers[scanner.id]) {
            scannerMarkers[scanner.id].setLatLng([pos.lat, pos.lon]);
        } else {
            scannerMarkers[scanner.id] = L.marker([pos.lat, pos.lon], {
                icon: createScannerIcon()
            }).addTo(map);

            scannerMarkers[scanner.id].bindPopup(`
                <strong>${scanner.id}</strong><br>
                RSSI: ${scanner.last_rssi || '-'} dBm<br>
                Distance: ${scanner.estimated_distance || '-'} m
            `);
        }

        // Update popup content
        scannerMarkers[scanner.id].setPopupContent(`
            <strong>${scanner.id}</strong><br>
            RSSI: ${scanner.last_rssi || '-'} dBm<br>
            Distance: ${scanner.estimated_distance || '-'} m
        `);

        // Update range circle
        if (scanner.estimated_distance) {
            if (rangeCircles[scanner.id]) {
                rangeCircles[scanner.id].setLatLng([pos.lat, pos.lon]);
                rangeCircles[scanner.id].setRadius(scanner.estimated_distance);
            } else {
                rangeCircles[scanner.id] = L.circle([pos.lat, pos.lon], {
                    radius: scanner.estimated_distance,
                    color: 'rgba(129, 140, 248, 0.4)',
                    fillColor: 'rgba(129, 140, 248, 0.1)',
                    fillOpacity: 0.3,
                    weight: 2
                }).addTo(map);
            }
        }
    }

    // Update target marker (GPS only)
    if (target && target.lat !== undefined) {
        if (targetMarker) {
            targetMarker.setLatLng([target.lat, target.lon]);
        } else {
            targetMarker = L.marker([target.lat, target.lon], {
                icon: createTargetIcon()
            }).addTo(map);
        }
    } else if (targetMarker) {
        map.removeLayer(targetMarker);
        targetMarker = null;
    }
}

function centerMap() {
    if (!map) return;

    const lat = parseFloat(document.getElementById('map-center-lat').value);
    const lon = parseFloat(document.getElementById('map-center-lon').value);
    const zoom = parseInt(document.getElementById('map-zoom').value);

    map.setView([lat, lon], zoom);
}

function fitMapToBounds() {
    if (!map || scanners.length === 0) return;

    const points = scanners
        .filter(s => s.position && s.position.lat !== undefined)
        .map(s => [s.position.lat, s.position.lon]);

    if (target && target.lat !== undefined) {
        points.push([target.lat, target.lon]);
    }

    if (points.length > 0) {
        map.fitBounds(points, { padding: [50, 50] });
    }
}

// ============================================================
// Plan View (Canvas) - Supports both GPS and Local coords
// ============================================================
function initPlanCanvas() {
    planCanvas = document.getElementById('plan-canvas');
    if (!planCanvas) return;

    planCtx = planCanvas.getContext('2d');
    resizePlanCanvas();
    setupPlanPanning();
    window.addEventListener('resize', resizePlanCanvas);
}

function resizePlanCanvas() {
    if (!planCanvas) return;

    const wrapper = planCanvas.parentElement;
    planCanvas.width = wrapper.clientWidth - 32;
    planCanvas.height = wrapper.clientHeight - 32;
    drawPlan();
}

function drawPlan() {
    if (!planCtx) return;

    const ctx = planCtx;
    const w = planCanvas.width;
    const h = planCanvas.height;

    // Clear (before transform)
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.fillStyle = '#06060a';
    ctx.fillRect(0, 0, w, h);

    // Apply zoom and pan transform
    const centerX = w / 2;
    const centerY = h / 2;
    ctx.setTransform(planZoom, 0, 0, planZoom,
        centerX - centerX * planZoom + panOffset.x,
        centerY - centerY * planZoom + panOffset.y);

    // Draw plan image if loaded
    if (planImage) {
        ctx.drawImage(planImage, 0, 0, w, h);
    }

    // Draw grid (always when enabled, on top of image)
    if (showGrid) {
        drawGrid(ctx, w, h, !!planImage);
    }

    // Draw range circles
    for (const scanner of scanners) {
        if (scanner.estimated_distance) {
            drawPlanRangeCircle(ctx, scanner);
        }
    }

    // Draw scanners
    for (const scanner of scanners) {
        drawPlanScanner(ctx, scanner);
    }

    // Draw target
    if (target) {
        drawPlanTarget(ctx);
    }

    // Reset transform for any UI overlay
    ctx.setTransform(1, 0, 0, 1, 0, 0);
}

function drawGrid(ctx, w, h, hasImage = false) {
    // More visible grid when over image
    ctx.strokeStyle = hasImage ? 'rgba(129, 140, 248, 0.4)' : '#21262d';
    ctx.lineWidth = hasImage ? 1.5 : 1;
    ctx.font = '10px monospace';
    ctx.fillStyle = hasImage ? 'rgba(200, 200, 200, 0.9)' : '#484f58';

    if (coordMode === 'local') {
        // Draw grid based on plan size in meters
        const metersPerPixelX = planSettings.width / w;
        const metersPerPixelY = planSettings.height / h;

        // Grid every 5 meters
        const gridMeters = 5;
        const gridPixelsX = gridMeters / metersPerPixelX;
        const gridPixelsY = gridMeters / metersPerPixelY;

        // Vertical lines
        for (let x = gridPixelsX; x < w; x += gridPixelsX) {
            ctx.beginPath();
            ctx.moveTo(x, 0);
            ctx.lineTo(x, h);
            ctx.stroke();

            // Label
            if (showLabels) {
                const meters = (x * metersPerPixelX + planSettings.originX).toFixed(0);
                if (hasImage) {
                    // Background for readability
                    ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
                    ctx.fillRect(x + 1, 2, 25, 12);
                    ctx.fillStyle = 'rgba(200, 200, 200, 0.9)';
                }
                ctx.fillText(`${meters}m`, x + 2, 12);
            }
        }

        // Horizontal lines
        for (let y = gridPixelsY; y < h; y += gridPixelsY) {
            ctx.beginPath();
            ctx.moveTo(0, y);
            ctx.lineTo(w, y);
            ctx.stroke();

            // Label
            if (showLabels) {
                const meters = (planSettings.height - y * metersPerPixelY + planSettings.originY).toFixed(0);
                if (hasImage) {
                    ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
                    ctx.fillRect(1, y - 13, 25, 12);
                    ctx.fillStyle = 'rgba(200, 200, 200, 0.9)';
                }
                ctx.fillText(`${meters}m`, 2, y - 2);
            }
        }

        // Size label
        if (showLabels) {
            ctx.fillStyle = hasImage ? 'rgba(129, 140, 248, 0.9)' : '#818cf8';
            if (hasImage) {
                ctx.fillStyle = 'rgba(0, 0, 0, 0.6)';
                ctx.fillRect(w - 65, h - 16, 62, 14);
                ctx.fillStyle = 'rgba(129, 140, 248, 0.9)';
            }
            ctx.fillText(`${planSettings.width}x${planSettings.height}m`, w - 60, h - 5);
        }
    } else {
        // Simple grid for GPS mode
        const gridSize = 50;

        for (let x = gridSize; x < w; x += gridSize) {
            ctx.beginPath();
            ctx.moveTo(x, 0);
            ctx.lineTo(x, h);
            ctx.stroke();
        }

        for (let y = gridSize; y < h; y += gridSize) {
            ctx.beginPath();
            ctx.moveTo(0, y);
            ctx.lineTo(w, y);
            ctx.stroke();
        }
    }
}

function toggleGrid() {
    showGrid = !showGrid;
    document.getElementById('grid-toggle').classList.toggle('active', showGrid);
    drawPlan();
}

function toggleLabels() {
    showLabels = !showLabels;
    document.getElementById('labels-toggle').classList.toggle('active', showLabels);
    drawPlan();
}

function zoomPlan(direction) {
    const zoomStep = 0.25;
    const minZoom = 0.25;
    const maxZoom = 4.0;

    if (direction > 0) {
        planZoom = Math.min(maxZoom, planZoom + zoomStep);
    } else {
        planZoom = Math.max(minZoom, planZoom - zoomStep);
    }

    updateZoomDisplay();
    drawPlan();
}

function resetZoom() {
    planZoom = 1.0;
    panOffset = { x: 0, y: 0 };
    updateZoomDisplay();
    drawPlan();
}

function updateZoomDisplay() {
    const el = document.getElementById('zoom-level');
    if (el) {
        el.textContent = Math.round(planZoom * 100) + '%';
    }
}

function setupPlanPanning() {
    if (!planCanvas) return;

    // Mouse wheel zoom
    planCanvas.addEventListener('wheel', (e) => {
        e.preventDefault();
        const direction = e.deltaY < 0 ? 1 : -1;
        zoomPlan(direction);
    }, { passive: false });

    // Pan with mouse drag
    planCanvas.addEventListener('mousedown', (e) => {
        if (e.button === 0) {  // Left click
            isPanning = true;
            lastPanPos = { x: e.clientX, y: e.clientY };
            planCanvas.style.cursor = 'grabbing';
        }
    });

    planCanvas.addEventListener('mousemove', (e) => {
        if (isPanning) {
            const dx = e.clientX - lastPanPos.x;
            const dy = e.clientY - lastPanPos.y;
            panOffset.x += dx;
            panOffset.y += dy;
            lastPanPos = { x: e.clientX, y: e.clientY };
            drawPlan();
        }
    });

    planCanvas.addEventListener('mouseup', () => {
        isPanning = false;
        planCanvas.style.cursor = 'grab';
    });

    planCanvas.addEventListener('mouseleave', () => {
        isPanning = false;
        planCanvas.style.cursor = 'grab';
    });

    planCanvas.style.cursor = 'grab';
}

function worldToCanvas(pos) {
    const w = planCanvas.width;
    const h = planCanvas.height;

    if (coordMode === 'local' || (pos.x !== undefined && pos.lat === undefined)) {
        // Local coordinates (x, y in meters)
        const x = pos.x !== undefined ? pos.x : 0;
        const y = pos.y !== undefined ? pos.y : 0;

        const canvasX = ((x - planSettings.originX) / planSettings.width) * w;
        const canvasY = h - ((y - planSettings.originY) / planSettings.height) * h;

        return {
            x: Math.max(0, Math.min(w, canvasX)),
            y: Math.max(0, Math.min(h, canvasY))
        };
    } else {
        // GPS coordinates (lat, lon)
        const centerLat = parseFloat(document.getElementById('map-center-lat').value) || 48.8566;
        const centerLon = parseFloat(document.getElementById('map-center-lon').value) || 2.3522;
        const range = 0.002; // ~200m

        const canvasX = ((pos.lon - centerLon + range) / (2 * range)) * w;
        const canvasY = ((centerLat + range - pos.lat) / (2 * range)) * h;

        return {
            x: Math.max(0, Math.min(w, canvasX)),
            y: Math.max(0, Math.min(h, canvasY))
        };
    }
}

function distanceToPixels(distance) {
    if (coordMode === 'local') {
        // Direct conversion: distance in meters to pixels
        const pixelsPerMeter = planCanvas.width / planSettings.width;
        return distance * pixelsPerMeter;
    } else {
        // GPS mode: approximate conversion
        const range = 0.002; // degrees
        const rangeMeters = range * 111000; // ~222m
        const pixelsPerMeter = planCanvas.width / rangeMeters;
        return distance * pixelsPerMeter;
    }
}

function drawPlanRangeCircle(ctx, scanner) {
    const pos = scanner.position;
    if (!pos) return;

    // Check if position is valid for current mode
    if (coordMode === 'local' && pos.x === undefined && pos.lat !== undefined) return;
    if (coordMode === 'gps' && pos.lat === undefined && pos.x !== undefined) return;

    const canvasPos = worldToCanvas(pos);
    const radius = distanceToPixels(scanner.estimated_distance);

    ctx.beginPath();
    ctx.arc(canvasPos.x, canvasPos.y, radius, 0, Math.PI * 2);
    ctx.strokeStyle = 'rgba(129, 140, 248, 0.3)';
    ctx.lineWidth = 2;
    ctx.stroke();
}

function drawPlanScanner(ctx, scanner) {
    const pos = scanner.position;
    if (!pos) return;

    // Check if position is valid
    const hasGPS = pos.lat !== undefined;
    const hasLocal = pos.x !== undefined;

    if (!hasGPS && !hasLocal) return;

    const canvasPos = worldToCanvas(pos);

    // Dot
    ctx.beginPath();
    ctx.arc(canvasPos.x, canvasPos.y, 8, 0, Math.PI * 2);
    ctx.fillStyle = '#818cf8';
    ctx.fill();

    // Label
    ctx.font = '12px monospace';
    ctx.fillStyle = '#c9d1d9';
    ctx.textAlign = 'center';
    ctx.fillText(scanner.id, canvasPos.x, canvasPos.y - 15);

    // RSSI
    if (scanner.last_rssi !== null) {
        ctx.font = '10px monospace';
        ctx.fillStyle = '#484f58';
        ctx.fillText(`${scanner.last_rssi} dBm`, canvasPos.x, canvasPos.y + 20);
    }

    ctx.textAlign = 'left';
}

function drawPlanTarget(ctx) {
    if (!target) return;

    const hasGPS = target.lat !== undefined;
    const hasLocal = target.x !== undefined;

    if (!hasGPS && !hasLocal) return;

    const canvasPos = worldToCanvas(target);

    // Glow
    ctx.beginPath();
    ctx.arc(canvasPos.x, canvasPos.y, 20, 0, Math.PI * 2);
    ctx.fillStyle = 'rgba(248, 113, 113, 0.3)';
    ctx.fill();

    // Cross
    ctx.strokeStyle = '#f87171';
    ctx.lineWidth = 3;

    ctx.beginPath();
    ctx.moveTo(canvasPos.x - 12, canvasPos.y - 12);
    ctx.lineTo(canvasPos.x + 12, canvasPos.y + 12);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(canvasPos.x + 12, canvasPos.y - 12);
    ctx.lineTo(canvasPos.x - 12, canvasPos.y + 12);
    ctx.stroke();

    // Label
    ctx.font = 'bold 12px monospace';
    ctx.fillStyle = '#f87171';
    ctx.textAlign = 'center';
    ctx.fillText('TARGET', canvasPos.x, canvasPos.y - 25);
    ctx.textAlign = 'left';
}

// ============================================================
// Plan Image Upload & Calibration
// ============================================================
function uploadPlanImage(input) {
    const file = input.files[0];
    if (!file) return;

    const reader = new FileReader();
    reader.onload = function(e) {
        planImage = new Image();
        planImage.onload = function() {
            document.getElementById('calibrate-btn').disabled = false;
            drawPlan();
        };
        planImage.src = e.target.result;
    };
    reader.readAsDataURL(file);
}

function calibratePlan() {
    alert('Calibration: Set the plan dimensions in Plan Settings panel.\n\nThe grid will map x,y meters to your uploaded image.');
    drawPlan();
}

function clearPlan() {
    planImage = null;
    document.getElementById('calibrate-btn').disabled = true;
    drawPlan();
}

function applyPlanSettings() {
    planSettings.width = parseFloat(document.getElementById('plan-width').value) || 50;
    planSettings.height = parseFloat(document.getElementById('plan-height').value) || 30;
    planSettings.originX = parseFloat(document.getElementById('plan-origin-x').value) || 0;
    planSettings.originY = parseFloat(document.getElementById('plan-origin-y').value) || 0;
    updateSizeDisplay();
    drawPlan();
}

function adjustPlanSize(delta) {
    // Adjust both width and height proportionally
    const minSize = 10;
    const maxSize = 500;

    planSettings.width = Math.max(minSize, Math.min(maxSize, planSettings.width + delta));
    planSettings.height = Math.max(minSize, Math.min(maxSize, planSettings.height + Math.round(delta * 0.6)));

    // Update input fields in sidebar
    document.getElementById('plan-width').value = planSettings.width;
    document.getElementById('plan-height').value = planSettings.height;

    updateSizeDisplay();
    drawPlan();
}

function updateSizeDisplay() {
    const el = document.getElementById('size-display');
    if (el) {
        el.textContent = `${planSettings.width}x${planSettings.height}m`;
    }
}

// ============================================================
// View Switching
// ============================================================
function switchView(view) {
    currentView = view;

    // Update buttons
    document.querySelectorAll('.view-btn').forEach(btn => {
        btn.classList.toggle('active', btn.dataset.view === view);
    });

    // Update views
    document.getElementById('map-view').classList.toggle('active', view === 'map');
    document.getElementById('plan-view').classList.toggle('active', view === 'plan');

    // Show/hide settings panels based on view
    document.getElementById('map-settings').style.display = view === 'map' ? 'block' : 'none';
    document.getElementById('plan-settings').style.display = view === 'plan' ? 'block' : 'none';

    // Initialize view if needed
    if (view === 'map') {
        setTimeout(() => {
            if (!map) initMap();
            else map.invalidateSize();
            updateMapMarkers();
        }, 100);
    } else {
        if (!planCanvas) initPlanCanvas();
        else resizePlanCanvas();
    }
}

// ============================================================
// UI Updates
// ============================================================
function updateCoordMode(mode) {
    coordMode = mode;

    const modeDisplay = document.getElementById('coord-mode');
    const coord1Label = document.getElementById('target-coord1-label');
    const coord2Label = document.getElementById('target-coord2-label');

    if (mode === 'gps') {
        modeDisplay.textContent = 'GPS';
        coord1Label.textContent = 'Latitude';
        coord2Label.textContent = 'Longitude';
    } else {
        modeDisplay.textContent = 'Local';
        coord1Label.textContent = 'X (m)';
        coord2Label.textContent = 'Y (m)';
    }
}

function updateTargetInfo(targetData) {
    const coord1El = document.getElementById('target-coord1');
    const coord2El = document.getElementById('target-coord2');

    if (targetData && targetData.position) {
        const pos = targetData.position;

        if (pos.lat !== undefined) {
            coord1El.textContent = pos.lat.toFixed(6);
            coord2El.textContent = pos.lon.toFixed(6);
        } else if (pos.x !== undefined) {
            coord1El.textContent = pos.x.toFixed(2) + ' m';
            coord2El.textContent = pos.y.toFixed(2) + ' m';
        } else {
            coord1El.textContent = '-';
            coord2El.textContent = '-';
        }

        document.getElementById('target-confidence').textContent = ((targetData.confidence || 0) * 100).toFixed(0) + '%';
        document.getElementById('target-age').textContent = (targetData.age_seconds || 0).toFixed(1) + 's ago';

        // Store for rendering
        target = pos;
    } else {
        coord1El.textContent = '-';
        coord2El.textContent = '-';
        document.getElementById('target-confidence').textContent = '-';
        document.getElementById('target-age').textContent = '-';
        target = null;
    }
}

function updateScannerList(scannersData) {
    scanners = scannersData || [];
    const list = document.getElementById('scanner-list');
    document.getElementById('scanner-count').textContent = scanners.length;

    if (scanners.length === 0) {
        list.innerHTML = '<div class="empty">No scanners active</div>';
        return;
    }

    list.innerHTML = scanners.map(s => {
        const pos = s.position || {};
        let posStr;

        if (pos.lat !== undefined) {
            posStr = `(${pos.lat.toFixed(4)}, ${pos.lon.toFixed(4)})`;
        } else if (pos.x !== undefined) {
            posStr = `(${pos.x.toFixed(1)}m, ${pos.y.toFixed(1)}m)`;
        } else {
            posStr = '(-, -)';
        }

        return `
            <div class="scanner-item">
                <div class="scanner-id">${s.id}</div>
                <div class="scanner-details">
                    Pos: ${posStr} |
                    RSSI: ${s.last_rssi !== null ? s.last_rssi + ' dBm' : '-'} |
                    Dist: ${s.estimated_distance !== null ? s.estimated_distance + 'm' : '-'}
                </div>
            </div>
        `;
    }).join('');
}

function updateConfig(config) {
    if (!config) return;
    document.getElementById('config-rssi').value = config.rssi_at_1m || -40;
    document.getElementById('config-n').value = config.path_loss_n || 2.5;
    document.getElementById('config-smooth').value = config.smoothing_window || 5;
}

// ============================================================
// API Functions
// ============================================================
async function fetchState() {
    try {
        const res = await fetch('/api/mlat/state');
        const state = await res.json();

        // Update coordinate mode from server
        if (state.coord_mode) {
            updateCoordMode(state.coord_mode);
        }

        updateTargetInfo(state.target);
        updateScannerList(state.scanners);

        if (state.config) {
            updateConfig(state.config);
        }

        // Update visualization
        if (currentView === 'map') {
            updateMapMarkers();
        } else {
            drawPlan();
        }
    } catch (e) {
        console.error('Failed to fetch MLAT state:', e);
    }
}

async function saveConfig() {
    const config = {
        rssi_at_1m: parseFloat(document.getElementById('config-rssi').value),
        path_loss_n: parseFloat(document.getElementById('config-n').value),
        smoothing_window: parseInt(document.getElementById('config-smooth').value)
    };

    try {
        await fetch('/api/mlat/config', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(config)
        });
        console.log('Config saved');
    } catch (e) {
        console.error('Failed to save config:', e);
    }
}

async function clearData() {
    try {
        await fetch('/api/mlat/clear', { method: 'POST' });
        fetchState();
    } catch (e) {
        console.error('Failed to clear data:', e);
    }
}

// ============================================================
// Initialization
// ============================================================
document.addEventListener('DOMContentLoaded', () => {
    // Initialize map view by default
    initMap();
    initPlanCanvas();

    // Initialize displays
    updateZoomDisplay();
    updateSizeDisplay();

    // Start polling
    fetchState();
    setInterval(fetchState, 2000);
});
