<?php
// ---------------------------------------------------------------------
// Simple visit logging (no password / no auth)
// ---------------------------------------------------------------------
date_default_timezone_set('Australia/Brisbane');

// IP exclusion list
$excludedIPs = [
    '103.28.160.144'
];

$ip  = $_SERVER['REMOTE_ADDR']        ?? 'unknown';
$ua  = $_SERVER['HTTP_USER_AGENT']    ?? 'unknown';

// Only log if IP not excluded
if (!in_array($ip, $excludedIPs)) {

    // Log file path: GTFS/logs/route_visits.txt relative to this file
    $logDir  = __DIR__ . '/GTFS/logs';
    $logFile = $logDir . '/route_visits.txt';

    // Ensure log directory exists
    if (!is_dir($logDir)) {
        @mkdir($logDir, 0777, true);
    }

    // Device detection
    $device = 'desktop';
    if (preg_match('/mobile|android|iphone|ipad|ipod|blackberry|phone/i', $ua)) {
        $device = 'mobile';
    }

    // GeoIP location if available
    $location = 'Unknown';
    if (function_exists('geoip_record_by_name')) {
        $record = @geoip_record_by_name($ip);
        if ($record) {
            $parts = [];
            if (!empty($record['city']))         $parts[] = $record['city'];
            if (!empty($record['region']))       $parts[] = $record['region'];
            if (!empty($record['country_name'])) $parts[] = $record['country_name'];
            if ($parts) {
                $location = implode(', ', $parts);
            }
        }
    }

    $time = date('Y-m-d H:i:s');

    // Log format
    $logLine =
        $time .
        " | IP: " . $ip .
        " | Device: " . $device .
        " | Location: " . $location .
        " | UA: " . $ua .
        PHP_EOL;

    @file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BUS ROUTE NAV (DEMO)</title>
    <link rel="icon" type="image/x-icon" href="images/brisbane_bus_favicon.ico">

    <!-- Shared app CSS -->
    <link rel="stylesheet" href="css/bus-route-nav.css">

    <!-- Local Leaflet CSS -->
    <link rel="stylesheet" href="css/leaflet.css">
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🚌 BUS ROUTE NAV (DEMO)</h1>
            <p>Simulated route navigation for most Translink SEQ bus routes</p>
        </div>

        <div class="search-section">
            <div class="form-group">
                <div class="form-field route-field">
                    <label for="routeNumber">ROUTE NUMBER</label>
                    <input type="text" id="routeNumber" placeholder="eg. 140, 453" maxlength="10">
                </div>

                <div class="form-field bus-field">
                    <label for="busNumber">BUS # (OPTIONAL)</label>
                    <input type="text" id="busNumber" placeholder="eg. W1500" maxlength="10">
                </div>

                <div class="form-field direction-field">
                    <label for="direction">DIRECTION</label>
                    <select id="direction">
                        <option value="inbound">INBOUND</option>
                        <option value="outbound">OUTBOUND</option>
                    </select>
                </div>

                <div class="form-field button-field">
                    <button class="btn" id="searchBtn" onclick="getDirections()">Get Directions</button>
                </div>
            </div>
        </div>

        <div class="content">
            <div class="error" id="errorMsg"></div>

            <div class="loading" id="loading">
                <div class="spinner"></div>
                <p>Loading directions...</p>
            </div>

            <div class="route-info" id="routeInfo">
                <h2>
                    ROUTE INFORMATION
                    <span id="infoStopCount" style="font-size:0.8em; font-weight:normal; color:#495057; margin-left:8px;"></span>
                </h2>
                <div class="route-info-grid">
                    <div class="route-info-item">
                        <label>ROUTE</label>
                        <span id="infoRoute">-</span>
                    </div>
                    <div class="route-info-item">
                        <label>DIRECTION</label>
                        <span id="infoDirection">-</span>
                    </div>
                    <div class="route-info-item">
                        <label>DESTINATION</label>
                        <span id="infoDestination">-</span>
                    </div>
                    <div class="route-info-item">
                        <label>DISTANCE (Approx)</label>
                        <span id="infoDistance">-</span>
                    </div>
                    <div class="route-info-item">
                        <label>DURATION (Approx)</label>
                        <span id="infoDuration">-</span>
                    </div>
                </div>
            </div>

            <!-- Map + controls -->
<div class="map-wrapper">
    <div id="map"></div>

    <!-- NEXT banner: will overlay on top of the map -->
    <div id="nextStopBanner" class="next-stop-banner">
        <span id="nextStopText"></span>
    </div>

    <div id="mapControls">
        <div id="speedControl">
            <label for="speedSelect">Speed:</label>
            <select id="speedSelect" onchange="onSpeedChange()">
                <option value="1000">Normal</option>
                <option value="1500">Slower</option>
                <option value="2000">Slowest</option>
            </select>
        </div>

        <button id="followToggleBtn" type="button" onclick="toggleFollowMode()">
            Free move
        </button>
    </div>
</div>


            <div class="directions-list" id="directionsList">
                <h2>ROUTE STOPS</h2>
                <div id="stepsContainer"></div>
            </div>
        </div>

        <footer>
            <center>
                © 2025 - T Jones, Park Ridge<br>
                Email: <a id="contact-email" href="#">[click to email]</a>
            </center>
        </footer>
    </div>

    <!-- Email obfuscation -->
    <script>
      (function () {
        var p1 = "tony";
        var p2 = "nocs";
        var p3 = "com";
        var p4 = "au";

        var email = p1 + "@" + p2 + "." + p3 + "." + p4;

        var link = document.getElementById("contact-email");
        if (link) {
          link.href = "mailto:" + email;
          link.textContent = email;
        }
      })();
    </script>

    <!-- Leaflet JS (map) -->
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
    <!-- GeometryUtil for offset polylines (double blue lines) -->
    <script src="https://unpkg.com/leaflet-geometryutil"></script>

    <script>
        function showError(message) {
            const errorEl = document.getElementById('errorMsg');
            errorEl.textContent = message;
            errorEl.classList.add('active');
            setTimeout(() => errorEl.classList.remove('active'), 5000);
        }

        function showLoading(show) {
            document.getElementById('loading').classList.toggle('active', show);
            document.getElementById('searchBtn').disabled = show;
        }

        // Leaflet map objects
        let map = null;
        let routeLayerGroup = null;

        // Simulated bus tracking
        let liveMarker = null;
        let liveAccuracyCircle = null;
        let liveLatLng = null;    // last known bus position (simulated)

        // Follow mode: true = always recenter on bus
        let followBus = true;

        // True once we have a loaded route – only then do we auto map-only on landscape
        let hasRouteLoaded = false;

        // Shape used for simulation
        let currentShapeCoords = [];
        let demoTimer = null;
        let demoIndex = 0;

        // Demo speed (ms between steps)
        let demoStepMs = 1000; // default = Normal

        // Current bus label for icon (e.g. W1500)
        let busLabel = '';

        // Next-stop logic (demo)
        let routeStops = [];
        let nextStopMeta = []; // per current stop, meta for next stop (distance etc.)
        let currentStopIndex = null;
        let nextStopIndex = null;
        let enableNextStopBanner = false; // only for routes with <= 50 stops

        // --- Helpers ---

        function isMobileDevice() {
            return /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
        }

        function isLandscape() {
            return window.innerWidth > window.innerHeight;
        }

        function setMapOnlyMode(on) {
            const body = document.body;
            if (on) {
                body.classList.add('map-only');
            } else {
                body.classList.remove('map-only');
            }
            if (map) {
                setTimeout(() => map.invalidateSize(), 200);
            }
        }

        function handleOrientationOrResize() {
            if (!hasRouteLoaded) return;

            if (isMobileDevice() && isLandscape()) {
                setMapOnlyMode(true);
            } else {
                setMapOnlyMode(false);
            }
        }

        function updateFollowButton() {
            const btn = document.getElementById('followToggleBtn');
            if (!btn) return;
            if (followBus) {
                btn.textContent = 'Free move';
            } else {
                btn.textContent = 'Re-centre bus';
            }
        }

        function toggleFollowMode() {
            followBus = !followBus;
            updateFollowButton();

            if (followBus && map && liveLatLng) {
                const currentZoom = map.getZoom();
                map.setView(liveLatLng, currentZoom < 14 ? 14 : currentZoom);
            }
        }

        function ensureMapInitialized() {
            if (!map) {
                map = L.map('map').setView([-27.47, 153.02], 11); // Default centre on Brisbane
                L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                    maxZoom: 19,
                    attribution: '&copy; OpenStreetMap contributors'
                }).addTo(map);
                routeLayerGroup = L.layerGroup().addTo(map);
            }
        }

        function createBusDivIcon() {
            const label = (busLabel || 'BUS').toUpperCase();
            return L.divIcon({
                className: 'bus-text-icon',
                html: `<div class="bus-icon-inner">${label}</div>`,
                iconSize: [40, 24],
                iconAnchor: [20, 12]
            });
        }

        function updateBusPosition(lat, lon, accuracyMeters) {
            ensureMapInitialized();

            const latlng = [lat, lon];
            liveLatLng = latlng;

            const icon = createBusDivIcon();

            if (!liveMarker) {
                liveMarker = L.marker(latlng, {
                    title: busLabel || 'Bus',
                    icon: icon
                }).addTo(map);
            } else {
                liveMarker.setLatLng(latlng);
                liveMarker.setIcon(icon);
            }

            if (typeof accuracyMeters === 'number') {
                if (liveAccuracyCircle) {
                    liveAccuracyCircle.setLatLng(latlng).setRadius(accuracyMeters);
                } else {
                    liveAccuracyCircle = L.circle(latlng, {
                        radius: accuracyMeters,
                        fillOpacity: 0.1,
                        stroke: false
                    }).addTo(map);
                }
            }

            if (followBus) {
                const currentZoom = map.getZoom();
                map.setView(latlng, currentZoom < 14 ? 14 : currentZoom);
            }
        }

        function stopDemoSimulation() {
            if (demoTimer !== null) {
                clearInterval(demoTimer);
                demoTimer = null;
            }
        }

        // --- NEXT STOP helpers (demo) ---

        function resetNextStopState() {
            routeStops = [];
            nextStopMeta = [];
            currentStopIndex = null;
            nextStopIndex = null;
            enableNextStopBanner = false;

            const banner = document.getElementById('nextStopBanner');
            const textEl = document.getElementById('nextStopText');
            if (banner) banner.style.display = 'none';
            if (textEl) textEl.textContent = '';
        }

        function buildNextStopMeta(stops, steps) {
            nextStopMeta = [];
            if (!Array.isArray(stops) || stops.length === 0) return;

            for (let i = 0; i < stops.length - 1; i++) {
                let distanceText = '';
                if (steps[i] && steps[i].distance) {
                    distanceText = steps[i].distance;
                }
                nextStopMeta[i] = { distanceText };
            }
        }

        function updateNextStopBanner() {
            const banner = document.getElementById('nextStopBanner');
            const textEl = document.getElementById('nextStopText');
            if (!banner || !textEl) return;

            if (!enableNextStopBanner ||
                nextStopIndex === null ||
                !routeStops[nextStopIndex]) {
                banner.style.display = 'none';
                textEl.textContent = '';
                return;
            }

            const nextStop = routeStops[nextStopIndex];
            const name = (nextStop.stop_name || 'Unknown stop').toUpperCase();
            const meta = nextStopMeta[currentStopIndex] || {};
            const distanceText = meta.distanceText || '';

            let text = 'NEXT: ' + name;
            if (distanceText) {
                text += ' (' + distanceText + ')';
            }

            banner.style.display = 'block';
            textEl.textContent = text;
        }

        function updateNextStopFromProgress() {
            if (!enableNextStopBanner || !routeStops.length || !currentShapeCoords.length) return;

            const M = currentShapeCoords.length;
            const N = routeStops.length;
            if (M <= 1 || N <= 1) return;

            const p = demoIndex / (M - 1); // progress 0..1
            const approxStop = Math.round(p * (N - 1));
            const newCurrent = Math.min(Math.max(approxStop, 0), N - 1);

            if (newCurrent !== currentStopIndex) {
                currentStopIndex = newCurrent;
                const candidateNext = currentStopIndex + 1;
                nextStopIndex = candidateNext < N ? candidateNext : null;
                updateNextStopBanner();
            }
        }

        function startDemoSimulation() {
            if (!currentShapeCoords.length) {
                console.warn('No shape coords available for demo.');
                return;
            }

            stopDemoSimulation();
            demoIndex = 0;

            followBus = true;
            updateFollowButton();

            const stepMs = demoStepMs;
            const stride = 1;

            demoTimer = setInterval(() => {
                if (demoIndex >= currentShapeCoords.length) {
                    stopDemoSimulation();
                    return;
                }

                const [lat, lon] = currentShapeCoords[demoIndex];
                updateBusPosition(lat, lon, 10);

                // Drive the NEXT banner from route progress
                updateNextStopFromProgress();

                demoIndex += stride;
            }, stepMs);
        }

        // Speed dropdown handler
        function onSpeedChange() {
            const sel = document.getElementById('speedSelect');
            if (!sel) return;

            const v = parseInt(sel.value, 10);
            if (!isNaN(v)) {
                demoStepMs = v;

                if (demoTimer !== null) {
                    stopDemoSimulation();
                    startDemoSimulation();
                }
            }
        }

        function renderMap(data) {
            ensureMapInitialized();

            routeLayerGroup.clearLayers();

            const stops = Array.isArray(data.stops) ? data.stops : [];
            const shape = Array.isArray(data.shape_points) ? data.shape_points : (data.shapePoints || []);

            const boundsPoints = [];
            currentShapeCoords = [];

            // Shape polyline + offset lines
            if (shape.length > 0) {
                const shapeCoords = shape.map(p => {
                    const lat = parseFloat(p.lat);
                    const lon = parseFloat(p.lon);
                    if (!isNaN(lat) && !isNaN(lon)) {
                        boundsPoints.push([lat, lon]);
                        return [lat, lon];
                    }
                    return null;
                }).filter(Boolean);

                currentShapeCoords = shapeCoords.slice();

                if (shapeCoords.length > 0) {
                    const shapeLatLngs = shapeCoords.map(([lat, lon]) => L.latLng(lat, lon));
                    const offsetMeters = 3; // distance for side lines

                    // Centre line
                    routeLayerGroup.addLayer(
                        L.polyline(shapeLatLngs, {
                            color: '#007BFF',
                            weight: 5
                        })
                    );

                    if (L.GeometryUtil && typeof L.GeometryUtil.offsetPolyline === 'function') {
                        const leftOffset  = L.GeometryUtil.offsetPolyline(shapeLatLngs, -offsetMeters);
                        const rightOffset = L.GeometryUtil.offsetPolyline(shapeLatLngs,  offsetMeters);

                        routeLayerGroup.addLayer(
                            L.polyline(leftOffset, {
                                color: '#007BFF',
                                weight: 3,
                                opacity: 0.9
                            })
                        );

                        routeLayerGroup.addLayer(
                            L.polyline(rightOffset, {
                                color: '#007BFF',
                                weight: 3,
                                opacity: 0.9
                            })
                        );
                    }
                }
            }

            // Stop markers
            stops.forEach((s, idx) => {
                const lat = parseFloat(s.lat);
                const lon = parseFloat(s.lon);
                if (isNaN(lat) || isNaN(lon)) return;

                boundsPoints.push([lat, lon]);

                const marker = L.circleMarker([lat, lon], {
                    radius: 6,
                    color: '#4682B4',
                    fillColor: '#7CFC00',
                    fillOpacity: 1,
                    weight: 1
                });

                const label = (idx === 0)
                    ? 'START'
                    : (idx === stops.length - 1 ? 'TERMINUS' : 'STOP');

                const popupHtml =
                    `<strong>${label}</strong><br>` +
                    `#${idx + 1} – ${s.stop_name || ''}<br>` +
                    `(${lat.toFixed(5)}, ${lon.toFixed(5)})`;

                marker.bindPopup(popupHtml);
                routeLayerGroup.addLayer(marker);
            });

            if (boundsPoints.length > 0) {
                const bounds = L.latLngBounds(boundsPoints);
                map.fitBounds(bounds, { padding: [30, 30] });
            }
        }

        function logRouteVisit(routeNumber, direction, busNumber, source) {
            const params = new URLSearchParams();
            params.append('route_number', routeNumber);
            params.append('direction', direction);
            if (busNumber) {
                params.append('bus_number', busNumber);
            }
            params.append('source', source);

            fetch('log_route_visit.php', {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: params.toString()
            }).catch(() => {});
        }

        function getDirections() {
            const routeNumberInput = document.getElementById('routeNumber');
            const routeNumber = routeNumberInput ? routeNumberInput.value.trim() : '';

            const busNumberInput = document.getElementById('busNumber');
            busLabel = busNumberInput ? busNumberInput.value.trim() : '';

            const directionSelect = document.getElementById('direction');
            const selectedDirection = directionSelect ? directionSelect.value : 'outbound';

            if (!routeNumber) {
                showError('Please enter a route number');
                if (routeNumberInput) routeNumberInput.focus();
                return;
            }

            stopDemoSimulation();
            resetNextStopState();

            document.getElementById('routeInfo').classList.remove('active');
            document.getElementById('directionsList').classList.remove('active');
            document.getElementById('errorMsg').classList.remove('active');
            showLoading(true);

            const url =
                `get_route_directions.php?route_number=${encodeURIComponent(routeNumber)}&direction=${encodeURIComponent(selectedDirection)}`;

            fetch(url)
                .then(response => {
                    if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
                    return response.text();
                })
                .then(text => {
                    let data;
                    try {
                        text = text.trim();
                        if (!text) throw new Error('Empty response from server');
                        data = JSON.parse(text);
                    } catch (e) {
                        showError('Server returned invalid JSON. First part: ' + (text.substring(0, 100) || 'Empty response'));
                        showLoading(false);
                        return;
                    }

                    showLoading(false);
                    if (!data.success) {
                        showError(data.error || 'Unknown error from server.');
                        return;
                    }

                    hasRouteLoaded = true;

                    // Show speed control once we have a route
                    const sc = document.getElementById('speedControl');
                    if (sc) {
                        sc.style.display = 'flex';
                    }

                    const routeEl = document.getElementById('infoRoute');
                    routeEl.innerHTML =
                        `<span style="color:#0047AB; font-weight:bold;">${String(data.route_number || routeNumber).toUpperCase()}</span>`;

                    const directionEl = document.getElementById('infoDirection');
                    directionEl.innerHTML =
                        `<span style="color:#006600; font-weight:bold;">${String(selectedDirection).toUpperCase()}</span>`;

                    const destEl = document.getElementById('infoDestination');
                    destEl.innerHTML =
                        `<span style="color:#8B4513; font-weight:bold;">${String(data.trip_headsign || '').toUpperCase()}</span>`;

                    document.getElementById('infoDistance').textContent = data.total_distance_text || '-';
                    document.getElementById('infoDuration').textContent = data.total_duration_text || '-';

                    const steps = Array.isArray(data.steps) ? data.steps : [];
                    const stopCount = (typeof data.stop_count === 'number' && data.stop_count > 0)
                        ? data.stop_count
                        : steps.length;

                    document.getElementById('infoStopCount').textContent =
                        stopCount ? `(${stopCount} stops)` : '';

                    document.getElementById('routeInfo').classList.add('active');

                    // NEXT STOP setup
                    routeStops = Array.isArray(data.stops) ? data.stops : [];
                    buildNextStopMeta(routeStops, steps);
                    if (routeStops.length > 0 && routeStops.length <= 50) {
                        enableNextStopBanner = true;
                        currentStopIndex = 0;
                        nextStopIndex = routeStops.length > 1 ? 1 : null;
                        updateNextStopBanner();
                    } else {
                        enableNextStopBanner = false;
                        updateNextStopBanner();
                    }

                    // Render map (shape + stops)
                    renderMap(data);

                    followBus = true;
                    updateFollowButton();

                    // Build stop list
                    const stepsContainer = document.getElementById('stepsContainer');
                    stepsContainer.innerHTML = '';

                    if (!steps.length) {
                        const div = document.createElement('div');
                        div.textContent =
                            'No steps returned from API (check that get_route_directions.php is building the steps array).';
                        stepsContainer.appendChild(div);
                    } else {
                        steps.forEach((step, index) => {
                            const instruction = step.instruction || `Segment ${index + 1}`;
                            const distance    = step.distance || '';

                            let distanceHtml = '';
                            if (distance && distance !== '–') {
                                distanceHtml =
                                    `<span><strong>Next stop:</strong> ${distance}</span>`;
                            }

                            const stepEl = document.createElement('div');
                            stepEl.className = 'direction-step';
                            stepEl.innerHTML = `
                                <div class="direction-step-number">${index + 1}</div>
                                <div class="direction-step-content">
                                    <div class="direction-step-instruction">${instruction}</div>
                                    <div class="direction-step-meta">
                                        ${distanceHtml}
                                    </div>
                                </div>
                            `;

                            stepsContainer.appendChild(stepEl);
                        });
                    }

                    document.getElementById('directionsList').classList.add('active');
                    document.getElementById('routeInfo').scrollIntoView({ behavior: 'smooth' });

                    handleOrientationOrResize();

                    // Start the simulated run along the route
                    startDemoSimulation();

                    // Log this route selection
                    logRouteVisit(routeNumber, selectedDirection, busLabel, 'demo');
                })
                .catch(error => {
                    showLoading(false);
                    showError('An error occurred: ' + error.message);
                });
        }

        document.getElementById('routeNumber').addEventListener('keypress', function (e) {
            if (e.key === 'Enter') getDirections();
        });

        window.addEventListener('resize', handleOrientationOrResize);
        window.addEventListener('orientationchange', handleOrientationOrResize);

        updateFollowButton();
    </script>
	
	
	
</body>
</html>
