<?php
// engine.php
// Main world engine: authoritative map, player/vehicle sync, event markers,
// and communication with uix.php (HUD, minimap, phone, inventory, quests).

$playerId = isset($_GET['pid']) ? htmlspecialchars($_GET['pid']) : 'guest_' . rand(1000, 9999);
$wsUrl    = 'wss://your-game-server.example.com/ws'; // Replace with your WebSocket endpoint
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Engine</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">

<link href="https://unpkg.com/maplibre-gl@3.6.1/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://unpkg.com/maplibre-gl@3.6.1/dist/maplibre-gl.js"></script>

<style>
    html, body {
        margin: 0;
        padding: 0;
        overflow: hidden;
        width: 100%;
        height: 100%;
        background: #000;
        font-family: system-ui, sans-serif;
    }

    #world-map {
        position: absolute;
        inset: 0;
    }

    #zoom-controls {
        position: absolute;
        top: 12px;
        right: 12px;
        display: flex;
        flex-direction: column;
        gap: 6px;
        z-index: 10;
    }

    .zoom-btn {
        width: 34px;
        height: 34px;
        border-radius: 6px;
        border: none;
        background: rgba(0,0,0,0.65);
        color: #fff;
        font-size: 20px;
        cursor: pointer;
        box-shadow: 0 0 6px rgba(0,0,0,0.6);
    }
    .zoom-btn:hover {
        background: rgba(0,0,0,0.85);
    }

    /* Player marker */
    .player-marker {
        width: 18px;
        height: 18px;
        border-radius: 50%;
        background: #00ffcc;
        border: 2px solid #004433;
        box-shadow: 0 0 8px rgba(0,255,200,0.8);
    }

    /* Other players */
    .other-player-marker {
        width: 14px;
        height: 14px;
        border-radius: 50%;
        background: #00aaff;
        border: 2px solid #003355;
        box-shadow: 0 0 6px rgba(0,170,255,0.8);
    }

    .excluded-outline {
        box-shadow: 0 0 10px rgba(200,200,200,0.9);
        border-color: #ccc !important;
    }

    /* Vehicles */
    .vehicle-marker {
        width: 16px;
        height: 16px;
        border-radius: 4px;
        background: #ffcc00;
        border: 2px solid #664400;
        box-shadow: 0 0 6px rgba(255,204,0,0.8);
    }

    /* World markers (events, shops, quests) */
    .world-marker {
        width: 14px;
        height: 14px;
        border-radius: 50%;
        background: #ff3366;
        border: 2px solid #66001f;
        box-shadow: 0 0 6px rgba(255,51,102,0.8);
    }

    /* Debug overlay */
    #debug {
        position: absolute;
        top: 12px;
        left: 12px;
        padding: 8px 10px;
        background: rgba(0,0,0,0.55);
        color: #fff;
        font-size: 12px;
        border-radius: 6px;
        z-index: 10;
        pointer-events: none;
    }
</style>
</head>
<body>

<div id="world-map"></div>

<div id="zoom-controls">
    <button class="zoom-btn" id="zoom-in">+</button>
    <button class="zoom-btn" id="zoom-out">−</button>
</div>

<div id="debug">
    Connected: <span id="dbg-conn">false</span><br>
    Player: <?php echo $playerId; ?><br>
    Pos: <span id="dbg-pos">N/A</span><br>
    Speed: <span id="dbg-speed">0</span> km/h<br>
    Vehicle: <span id="dbg-veh">none</span><br>
    State: <span id="dbg-state">active</span>
</div>

<script>
/* CONFIG */
const PLAYER_ID = "<?php echo $playerId; ?>";
const WS_URL    = "<?php echo $wsUrl; ?>";

const INITIAL_CENTER = [173.283, -41.270];
const INITIAL_ZOOM   = 14;

/* STATE */
let map;
let socket;
let connected = false;

let playerState = {
    id: PLAYER_ID,
    x: INITIAL_CENTER[0],
    y: INITIAL_CENTER[1],
    heading: 0,
    speedKmh: 0,
    hp: 100,
    shield: 0,
    vehicleId: null,
    excluded: false
};

let playerMarker = null;
let otherPlayers = {};
let vehicles = {};
let worldMarkers = {};

/* INIT MAP */
function initMap() {
    map = new maplibregl.Map({
        container: 'world-map',
        style: './map-style.json',
        center: INITIAL_CENTER,
        zoom: INITIAL_ZOOM,
        pitch: 0,
        bearing: 0
    });

    map.on('load', () => {
        createPlayerMarker();
    });
}

function createPlayerMarker() {
    const el = document.createElement('div');
    el.className = 'player-marker';

    playerMarker = new maplibregl.Marker({
        element: el,
        rotationAlignment: 'map'
    })
    .setLngLat([playerState.x, playerState.y])
    .addTo(map);
}

function updatePlayerMarker() {
    if (!playerMarker) return;
    playerMarker.setLngLat([playerState.x, playerState.y]);
    playerMarker.setRotation(playerState.heading);
}

/* OTHER PLAYERS */
function upsertOtherPlayer(p) {
    if (p.id === PLAYER_ID) return;

    let entry = otherPlayers[p.id];
    if (!entry) {
        const el = document.createElement('div');
        el.className = 'other-player-marker';
        if (p.excluded) el.classList.add('excluded-outline');

        const marker = new maplibregl.Marker({
            element: el,
            rotationAlignment: 'map'
        })
        .setLngLat([p.x, p.y])
        .addTo(map);

        otherPlayers[p.id] = { marker, data: p };
    } else {
        entry.data = p;
        entry.marker.setLngLat([p.x, p.y]);
        entry.marker.setRotation(p.heading);

        const el = entry.marker.getElement();
        if (p.excluded) el.classList.add('excluded-outline');
        else el.classList.remove('excluded-outline');
    }
}

function removeOtherPlayer(id) {
    if (!otherPlayers[id]) return;
    otherPlayers[id].marker.remove();
    delete otherPlayers[id];
}

/* VEHICLES */
function upsertVehicle(v) {
    let entry = vehicles[v.id];
    if (!entry) {
        const el = document.createElement('div');
        el.className = 'vehicle-marker';

        const marker = new maplibregl.Marker({
            element: el,
            rotationAlignment: 'map'
        })
        .setLngLat([v.x, v.y])
        .addTo(map);

        vehicles[v.id] = { marker, data: v };
    } else {
        entry.data = v;
        entry.marker.setLngLat([v.x, v.y]);
        entry.marker.setRotation(v.heading);
    }
}

function removeVehicle(id) {
    if (!vehicles[id]) return;
    vehicles[id].marker.remove();
    delete vehicles[id];
}

/* WORLD MARKERS */
function upsertWorldMarker(m) {
    let entry = worldMarkers[m.id];
    if (!entry) {
        const el = document.createElement('div');
        el.className = 'world-marker';

        const marker = new maplibregl.Marker({ element: el })
            .setLngLat([m.x, m.y])
            .addTo(map);

        worldMarkers[m.id] = { marker, data: m };
    } else {
        entry.data = m;
        entry.marker.setLngLat([m.x, m.y]);
    }
}

function removeWorldMarker(id) {
    if (!worldMarkers[id]) return;
    worldMarkers[id].marker.remove();
    delete worldMarkers[id];
}

/* WEBSOCKET */
function initWebSocket() {
    socket = new WebSocket(WS_URL);

    socket.addEventListener('open', () => {
        connected = true;
        document.getElementById('dbg-conn').textContent = 'true';

        sendToServer({ type: 'join', playerId: PLAYER_ID });
    });

    socket.addEventListener('message', (event) => {
        let msg;
        try { msg = JSON.parse(event.data); }
        catch { return; }

        handleServerMessage(msg);
    });

    socket.addEventListener('close', () => {
        connected = false;
        document.getElementById('dbg-conn').textContent = 'false';
        setTimeout(initWebSocket, 3000);
    });
}

function sendToServer(payload) {
    if (!socket || socket.readyState !== WebSocket.OPEN) return;
    socket.send(JSON.stringify(payload));
}

/* SERVER MESSAGE HANDLING */
function handleServerMessage(msg) {
    switch (msg.type) {

        case 'worldState':
            if (msg.players) msg.players.forEach(upsertOtherPlayer);
            if (msg.vehicles) msg.vehicles.forEach(upsertVehicle);
            if (msg.markers) msg.markers.forEach(upsertWorldMarker);
            break;

        case 'playerUpdate':
            if (msg.player.id === PLAYER_ID) {
                playerState = { ...playerState, ...msg.player };
                updatePlayerMarker();
                updateDebug();
                sendHudUpdate();
            } else {
                upsertOtherPlayer(msg.player);
            }
            break;

        case 'playerLeft':
            removeOtherPlayer(msg.id);
            break;

        case 'vehicleUpdate':
            upsertVehicle(msg.vehicle);
            break;

        case 'vehicleRemoved':
            removeVehicle(msg.id);
            break;

        case 'markerUpdate':
            upsertWorldMarker(msg.marker);
            break;

        case 'markerRemoved':
            removeWorldMarker(msg.id);
            break;

        case 'respawn':
            if (msg.player.id === PLAYER_ID) {
                playerState = { ...playerState, ...msg.player };
                updatePlayerMarker();
                updateDebug();
                sendHudUpdate();
            }
            break;
    }
}

/* INPUT (NO PREDICTION) */
const keys = { forward: false, backward: false, left: false, right: false, handbrake: false };

function setupInput() {
    window.addEventListener('keydown', (e) => {
        switch (e.code) {
            case 'KeyW': case 'ArrowUp': keys.forward = true; break;
            case 'KeyS': case 'ArrowDown': keys.backward = true; break;
            case 'KeyA': case 'ArrowLeft': keys.left = true; break;
            case 'KeyD': case 'ArrowRight': keys.right = true; break;
            case 'Space': keys.handbrake = true; break;
            default: return;
        }
        sendMovement();
    });

    window.addEventListener('keyup', (e) => {
        switch (e.code) {
            case 'KeyW': case 'ArrowUp': keys.forward = false; break;
            case 'KeyS': case 'ArrowDown': keys.backward = false; break;
            case 'KeyA': case 'ArrowLeft': keys.left = false; break;
            case 'KeyD': case 'ArrowRight': keys.right = false; break;
            case 'Space': keys.handbrake = false; break;
            default: return;
        }
        sendMovement();
    });
}

function sendMovement() {
    sendToServer({
        type: 'input',
        playerId: PLAYER_ID,
        input: keys
    });
}

/* ZOOM BUTTONS */
document.getElementById('zoom-in').onclick = () => {
    map.setZoom(map.getZoom() + 0.5);
    notifyHudZoom();
};
document.getElementById('zoom-out').onclick = () => {
    map.setZoom(map.getZoom() - 0.5);
    notifyHudZoom();
};

function notifyHudZoom() {
    postToHud({ type: 'worldZoomChanged', zoom: map.getZoom() });
}

/* HUD COMMUNICATION */
function postToHud(payload) {
    if (window.parent && window.parent !== window) {
        window.parent.postMessage({ source: 'engine', payload }, '*');
    }
}

function sendHudUpdate() {
    postToHud({
        type: 'hudUpdate',
        playerId: PLAYER_ID,
        position: { x: playerState.x, y: playerState.y },
        heading: playerState.heading,
        speedKmh: playerState.speedKmh,
        hp: playerState.hp,
        shield: playerState.shield,
        vehicleId: playerState.vehicleId,
        excluded: playerState.excluded
    });
}

/* RECEIVE FROM UIX */
window.addEventListener('message', (event) => {
    const data = event.data;
    if (!data || data.source !== 'uix') return;

    const p = data.payload;

    switch (p.type) {
        case 'spawnStarterVehicle':
            sendToServer({ type: 'spawnStarterVehicle', playerId: PLAYER_ID, vehicleType: p.vehicleType });
            break;

        case 'requestRespawn':
            sendToServer({ type: 'requestRespawn', playerId: PLAYER_ID });
            break;

        case 'setWorldZoom':
            map.setZoom(p.zoom);
            notifyHudZoom();
            break;

        case 'setWorldCenter':
            map.setCenter([p.center.lng, p.center.lat]);
            break;
    }
});

/* DEBUG */
function updateDebug() {
    document.getElementById('dbg-pos').textContent =
        playerState.y.toFixed(5) + ', ' + playerState.x.toFixed(5);
    document.getElementById('dbg-speed').textContent = Math.round(playerState.speedKmh);
    document.getElementById('dbg-veh').textContent = playerState.vehicleId || 'none';
    document.getElementById('dbg-state').textContent = playerState.excluded ? 'excluded' : 'active';
}

/* INIT */
window.onload = () => {
    initMap();
    initWebSocket();
    setupInput();
};
</script>

</body>
</html>