// Colors
const localColor = "#109856"
const teamColor = "#68a3e5"
const enemyColor = "#ec040b"
const bombColor = "#eda338"
const textColor = "#d1d1d1"

const DEFAULT_TEXT_SIZE = 1.2;
const DEFAULT_ENTITY_SIZE = 1.5;
const DEFAULT_ZOOM_LEVEL = 1.3;
const DEFAULT_HEALTH_BAR_SIZE = 1.0;

// Settings
let shouldZoom = false;
let rotateMap = true;
let playerCentered = true;
let showOffscreenIndicators = true;

let drawHealth = true;
let drawStats = true;
let drawNames = true;
let drawGuns = true;
let drawMoney = true;

let canvasScale = 1;
let minTextSize = 16;
let minEntitySize = 10;
let textSizeMultiplier = 1.0;
let entitySizeMultiplier = 1.0;
let healthBarSizeMultiplier = 1.0;
let playerCenteredZoom = 1.0;

const NETWORK_SETTINGS = {
    useInterpolation: true,
    interpolationAmount: 0.6,
    pingInterval: 3000,
    maxRetries: 5,
    requestTimeout: 5000,
    reconnectDelay: 1000
};

let connectionHealthy = true;
let lastResponseTime = 0;
let requestTimeoutTimer = null;
let reconnecting = false;
let retryCount = 0;

let isRequestPending = false;
let frameCounter = 0;
let fpsStartTime = 0;
let currentFps = 0;

let temporarilyDisableRotation = false;
let rotationDisabledUntilRespawn = false;
let lastKnownPositions = {};
let deadPlayers = {};
let entityInterpolationData = {};
let lastUpdateTime = 0;
let networkLatencyHistory = [];
let lastPingSent = 0;

// Special values for spectator mode
const FULL_MAP_VIEW = "FULL_MAP";
const LOCAL_PLAYER_VIEW = "YOU";

let focusedPlayerYaw = 0;
let focusedPlayerName = localStorage.getItem('selectedPlayer') || LOCAL_PLAYER_VIEW;
let focusedPlayerPos = null;
let playerList = {};
let lastFocusedPlayer = localStorage.getItem('lastFocusedPlayer') || null;

// Common
let canvas = null;
let ctx = null;

// radarflow specific
let radarData = null;
let freq = 0;
let image = null;
let map = null;
let mapName = null;
let loaded = false;
let entityData = null;
let update = false;
let localYaw = 0;
let localPlayerPos = null;

/// Radarflow zoom in
let zoomSet = false;
let safetyBound = 50;
let boundingRect = null;

// Weapon IDs
const weaponIdMap = {
    1: "DEAGLE", 2: "DUALIES", 3: "FIVE-SEVEN", 4: "GLOCK", 7: "AK-47",
    8: "AUG", 9: "AWP", 10: "FAMAS", 11: "G3SG1", 13: "GALIL", 14: "M249",
    16: "M4A4", 17: "MAC-10", 19: "P90", 23: "MP5", 24: "UMP", 25: "XM1014",
    26: "BIZON", 27: "MAG-7", 28: "NEGEV", 29: "SAWED-OFF", 30: "TEC-9",
    31: "ZEUS", 32: "P2000", 33: "MP7", 34: "MP9", 35: "NOVA", 36: "P250",
    38: "SCAR-20", 39: "SG 553", 40: "SCOUT", 60: "M4A1-S", 61: "USP-S",
    63: "CZ75", 64: "REVOLVER", 43: "FLASH", 44: "HE", 45: "SMOKE", 46: "MOLOTOV",
    47: "DECOY", 48: "INCENDIARY", 49: "C4", 0: "KNIFE"
};

// Networking
let websocket = null;
const websocketAddr = location.protocol === 'https:'
    ? `wss://${window.location.host}/ws`
    : `ws://${window.location.host}/ws`;

// Util functions
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const degreesToRadians = (degrees) => degrees * (Math.PI / 180);
const lerp = (start, end, t) => start * (1 - t) + end * t;

function lerpPosition(pos1, pos2, t) {
    if (!pos1 || !pos2) return pos2 || pos1 || null;
    return {
        x: lerp(pos1.x, pos2.x, t),
        y: lerp(pos1.y, pos2.y, t),
        z: lerp(pos1.z, pos2.z, t)
    };
}

function lerpAngle(a, b, t) {
    while (a > 360) a -= 360;
    while (a < 0) a += 360;
    while (b > 360) b -= 360;
    while (b < 0) b += 360;

    let diff = b - a;
    if (diff > 180) diff -= 360;
    if (diff < -180) diff += 360;

    return a + diff * t;
}

const pingTracker = {
    history: [],
    lastRequestTime: 0,
    maxSamples: 10,

    startRequest: function () {
        this.lastRequestTime = performance.now();
    },

    endRequest: function () {
        if (this.lastRequestTime === 0) return;

        const ping = performance.now() - this.lastRequestTime;
        this.history.push(ping);

        if (this.history.length > this.maxSamples) {
            this.history.shift();
        }

        this.lastRequestTime = 0;
    },

    getAveragePing: function () {
        if (this.history.length === 0) return 0;
        const sum = this.history.reduce((a, b) => a + b, 0);
        return sum / this.history.length;
    }
};

function updateEntityInterpolation(entityId, newData) {
    const now = performance.now();

    if (!entityInterpolationData[entityId]) {
        entityInterpolationData[entityId] = {
            current: JSON.parse(JSON.stringify(newData)),
            target: JSON.parse(JSON.stringify(newData)),
            lastUpdateTime: now
        };
        return entityInterpolationData[entityId].current;
    }

    entityInterpolationData[entityId].current = JSON.parse(JSON.stringify(entityInterpolationData[entityId].target));
    entityInterpolationData[entityId].target = JSON.parse(JSON.stringify(newData));
    entityInterpolationData[entityId].lastUpdateTime = now;

    return entityInterpolationData[entityId].current;
}

function getInterpolatedEntityData(entityId) {
    if (!NETWORK_SETTINGS.useInterpolation || !entityInterpolationData[entityId]) {
        return null;
    }

    const data = entityInterpolationData[entityId];
    const now = performance.now();
    const elapsed = now - data.lastUpdateTime;

    const pingTime = pingTracker.getAveragePing();
    const targetDuration = Math.min(200, Math.max(50, pingTime * 0.8));
    const t = Math.min(1, elapsed / targetDuration);
    const easedT = t * (2 - t);

    const result = JSON.parse(JSON.stringify(data.current));

    if (result.Player) {
        if (data.current.Player && data.target.Player) {
            if (data.current.Player.pos && data.target.Player.pos) {
                result.Player.pos = lerpPosition(
                    data.current.Player.pos,
                    data.target.Player.pos,
                    easedT * NETWORK_SETTINGS.interpolationAmount
                );
            }

            if (data.current.Player.yaw !== undefined && data.target.Player.yaw !== undefined) {
                result.Player.yaw = lerpAngle(
                    data.current.Player.yaw,
                    data.target.Player.yaw,
                    easedT * NETWORK_SETTINGS.interpolationAmount
                );
            }
        }
    } else if (result.Bomb) {
        if (data.current.Bomb && data.target.Bomb) {
            if (data.current.Bomb.pos && data.target.Bomb.pos) {
                result.Bomb.pos = lerpPosition(
                    data.current.Bomb.pos,
                    data.target.Bomb.pos,
                    easedT * NETWORK_SETTINGS.interpolationAmount
                );
            }
        }
    }

    return result;
}

function render() {
    requestAnimationFrame(render);

    const now = performance.now();
    if (!fpsStartTime) fpsStartTime = now;
    frameCounter++;

    if (now - fpsStartTime > 1000) {
        currentFps = Math.round(frameCounter * 1000 / (now - fpsStartTime));
        frameCounter = 0;
        fpsStartTime = now;
    }

    if (!isRequestPending && websocket && websocket.readyState === WebSocket.OPEN) {
        isRequestPending = true;
        pingTracker.startRequest();
        websocket.send("requestInfo");
    }

    renderFrame();
}

function sendRequest() {
    isRequestPending = true;
    pingTracker.startRequest();

    clearTimeout(requestTimeoutTimer);
    requestTimeoutTimer = setTimeout(() => {
        if (isRequestPending) {
            console.warn("[radarflow] Request timeout, retrying...");
            isRequestPending = false;

            if (retryCount < NETWORK_SETTINGS.maxRetries) {
                retryCount++;
                sendRequest();
            } else {
                retryCount = 0;
                console.error("[radarflow] Maximum retries reached, reconnecting...");
                reconnecting = true;
                if (websocket) {
                    try {
                        websocket.close();
                    } catch (e) {
                    }
                    websocket = null;
                }
                setTimeout(connect, NETWORK_SETTINGS.reconnectDelay);
            }
        }
    }, NETWORK_SETTINGS.requestTimeout);

    websocket.send("requestInfo");
}

function renderFrame() {
    fillCanvas();

    if (entityData && loaded && map && image) {
        processPlayerPositions();

        if (update) {
            updatePlayerDropdown();
            update = false;
        }

        drawImage();

        drawEntities();

        drawBombTimer();
    } else if (!loaded) {
        const fontSize = Math.max(40 * canvasScale, 16);
        ctx.font = `${fontSize}px Arial`;
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillStyle = textColor;
        ctx.fillText(websocket ? "Not on server" : "Disconnected", canvas.width / 2, canvas.height / 2);
    }

    if (drawStats) {
        const fontSize = Math.max(16 * canvasScale, 12);
        ctx.font = `${fontSize}px Arial`;
        ctx.textAlign = "left";
        ctx.fillStyle = "#00FF00";
        let rotationStatus = "Active";
        if (temporarilyDisableRotation) rotationStatus = "Manually Disabled";
        else if (rotationDisabledUntilRespawn) rotationStatus = "Disabled (Death)";

        ctx.fillText(`${currentFps} FPS | ${freq} Hz | Ping: ${Math.round(pingTracker.getAveragePing())}ms | Rotation: ${rotationStatus}`, 10, fontSize + 4);
    }
}

function processPlayerPositions() {
    if (!entityData) return;

    localPlayerPos = null;
    focusedPlayerPos = null;
    focusedPlayerYaw = 0;
    let oldPlayerList = { ...playerList };
    let newPlayerList = {};
    let wasPlayerAlive = oldPlayerList[focusedPlayerName] && !oldPlayerList[focusedPlayerName].isDead;

    entityData.forEach((data, index) => {
        const entityId = `entity_${index}`;

        if (data.Player) {
            const player = data.Player;
            if (NETWORK_SETTINGS.useInterpolation) {
                updateEntityInterpolation(entityId, data);
            }

            if (player.playerType === "Local") {
                localYaw = player.yaw;
                localPlayerPos = player.pos;
                newPlayerList[LOCAL_PLAYER_VIEW] = {
                    pos: player.pos,
                    yaw: player.yaw
                };

                lastKnownPositions[LOCAL_PLAYER_VIEW] = player.pos;
                if (deadPlayers[LOCAL_PLAYER_VIEW]) delete deadPlayers[LOCAL_PLAYER_VIEW];
            } else {
                newPlayerList[player.playerName] = {
                    pos: player.pos,
                    yaw: player.yaw
                };

                lastKnownPositions[player.playerName] = player.pos;
                if (deadPlayers[player.playerName]) delete deadPlayers[player.playerName];
            }

            if (player.playerName === focusedPlayerName ||
                (focusedPlayerName === LOCAL_PLAYER_VIEW && player.playerType === "Local")) {
                focusedPlayerPos = player.pos;
                focusedPlayerYaw = player.yaw;

                if (rotationDisabledUntilRespawn) {
                    console.log("[radarflow] Player respawned, re-enabling rotation");
                    rotationDisabledUntilRespawn = false;
                }
            }
        }
    });

    for (const playerName in oldPlayerList) {
        if (!newPlayerList[playerName] && playerName !== FULL_MAP_VIEW) {
            deadPlayers[playerName] = {
                pos: lastKnownPositions[playerName] || null,
                yaw: oldPlayerList[playerName].yaw || 0,
                lastSeen: Date.now()
            };
            
            if (playerName === focusedPlayerName && wasPlayerAlive) {
                console.log(`[radarflow] Focused player ${playerName} died, switching to full map view`);
                localStorage.setItem('lastFocusedPlayer', playerName);
                lastFocusedPlayer = playerName;
                focusedPlayerName = FULL_MAP_VIEW;
                localStorage.setItem('selectedPlayer', FULL_MAP_VIEW);
                playerCentered = false;
                update = true;
            }
        }
    }

    for (const playerName in deadPlayers) {
        if (!newPlayerList[playerName] && playerName !== FULL_MAP_VIEW) {
            if (Date.now() - deadPlayers[playerName].lastSeen < 60000) {
                newPlayerList[playerName] = {
                    pos: deadPlayers[playerName].pos,
                    yaw: deadPlayers[playerName].yaw,
                    isDead: true
                };
            }
        }
    }

    newPlayerList[FULL_MAP_VIEW] = {
        isFullMap: true
    };

    playerList = newPlayerList;

    if (focusedPlayerName === FULL_MAP_VIEW && lastFocusedPlayer) {
        const playerIsBackAlive = newPlayerList[lastFocusedPlayer] && !newPlayerList[lastFocusedPlayer].isDead;
        
        if (playerIsBackAlive) {
            console.log(`[radarflow] Last focused player ${lastFocusedPlayer} is back alive, reselecting them`);
            focusedPlayerName = lastFocusedPlayer;
            localStorage.setItem('selectedPlayer', lastFocusedPlayer);
            
            if (document.getElementById('centerCheck').checked) {
                playerCentered = true;
            }
            
            focusedPlayerPos = newPlayerList[lastFocusedPlayer].pos;
            focusedPlayerYaw = newPlayerList[lastFocusedPlayer].yaw;
            
            rotationDisabledUntilRespawn = false;
            
            const dropdown = document.getElementById('playerSelect');
            if (dropdown) {
                dropdown.value = lastFocusedPlayer;
            }
            
            update = true;
        }
    }

    // Handle focused player state
    if (focusedPlayerName === FULL_MAP_VIEW) {
        playerCentered = false;
    } else if (focusedPlayerPos === null) {
        if (playerList[focusedPlayerName]?.isDead) {
            console.log(`[radarflow] Focused player ${focusedPlayerName} is dead, switching to full map view`);
            localStorage.setItem('lastFocusedPlayer', focusedPlayerName);
            lastFocusedPlayer = focusedPlayerName;
            focusedPlayerName = FULL_MAP_VIEW;
            localStorage.setItem('selectedPlayer', FULL_MAP_VIEW);
            playerCentered = false;
            update = true;
        } 
        else if (lastKnownPositions[focusedPlayerName]) {
            console.log("[radarflow] Focused player disappeared, switching to full map view");
            localStorage.setItem('lastFocusedPlayer', focusedPlayerName);
            lastFocusedPlayer = focusedPlayerName;
            focusedPlayerName = FULL_MAP_VIEW;
            localStorage.setItem('selectedPlayer', FULL_MAP_VIEW);
            playerCentered = false;
            update = true;
        }
    }
}

function drawImage() {
    if (!image || !canvas || !map) return;

    ctx.save();

    if (focusedPlayerName === FULL_MAP_VIEW || !playerCentered) {
        if (rotateMap && !temporarilyDisableRotation) {
            ctx.translate(canvas.width / 2, canvas.height / 2);
            ctx.translate(-canvas.width / 2, -canvas.height / 2);
        }
    } else if (playerCentered && focusedPlayerPos) {
        if (playerCenteredZoom !== 1.0) {
            ctx.translate(canvas.width / 2, canvas.height / 2);
            ctx.scale(playerCenteredZoom, playerCenteredZoom);
            ctx.translate(-canvas.width / 2, -canvas.height / 2);
        }

        if (rotateMap &&
            focusedPlayerPos &&
            !temporarilyDisableRotation &&
            !rotationDisabledUntilRespawn) {

            ctx.translate(canvas.width / 2, canvas.height / 2);
            ctx.rotate(degreesToRadians(focusedPlayerYaw + 270));
            ctx.translate(-canvas.width / 2, -canvas.height / 2);
        }

        const playerX = (focusedPlayerPos.x - map.pos_x) / map.scale;
        const playerY = (focusedPlayerPos.y - map.pos_y) / -map.scale;

        const playerCanvasX = (playerX / image.width) * canvas.width;
        const playerCanvasY = (playerY / image.height) * canvas.height;

        const translateX = (canvas.width / 2) - playerCanvasX;
        const translateY = (canvas.height / 2) - playerCanvasY;

        ctx.translate(translateX, translateY);
    } else if (rotateMap &&
        focusedPlayerPos &&
        !temporarilyDisableRotation &&
        !rotationDisabledUntilRespawn) {
        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate(degreesToRadians(focusedPlayerYaw + 270));
        ctx.translate(-canvas.width / 2, -canvas.height / 2);
    }

    ctx.drawImage(
        image,
        0, 0, image.width, image.height,
        0, 0, canvas.width, canvas.height
    );

    ctx.restore();
}

function toggleHealth() {
    drawHealth = !drawHealth;
    update = true;
    localStorage.setItem('drawHealth', drawHealth ? 'true' : 'false');
}

function mapAndTransformCoordinates(pos) {
    const canvasWidth = canvas.width;
    const canvasHeight = canvas.height;
    const imageWidth = image ? image.width : 1;
    const imageHeight = image ? image.height : 1;

    if (!map || !pos) return {
        pos: { x: 0, y: 0 },
        textSize: minTextSize * textSizeMultiplier
    };

    const posX = (pos.x - map.pos_x) / map.scale;
    const posY = (pos.y - map.pos_y) / -map.scale;

    let screenX = (posX / imageWidth) * canvasWidth;
    let screenY = (posY / imageHeight) * canvasHeight;

    if (playerCentered && focusedPlayerPos) {
        const playerX = (focusedPlayerPos.x - map.pos_x) / map.scale;
        const playerY = (focusedPlayerPos.y - map.pos_y) / -map.scale;

        const playerRelX = playerX / imageWidth;
        const playerRelY = playerY / imageHeight;

        const centerX = canvasWidth / 2;
        const centerY = canvasHeight / 2;

        const playerScreenX = (playerX / imageWidth) * canvasWidth;
        const playerScreenY = (playerY / imageHeight) * canvasHeight;

        const deltaX = screenX - playerScreenX;
        const deltaY = screenY - playerScreenY;

        const zoomedDeltaX = deltaX * playerCenteredZoom;
        const zoomedDeltaY = deltaY * playerCenteredZoom;

        screenX = centerX + zoomedDeltaX;
        screenY = centerY + zoomedDeltaY;

        if (rotateMap &&
            !temporarilyDisableRotation &&
            !rotationDisabledUntilRespawn) {

            const relX = screenX - centerX;
            const relY = screenY - centerY;

            const angle = degreesToRadians(focusedPlayerYaw + 270);
            const cos = Math.cos(angle);
            const sin = Math.sin(angle);

            const rotX = relX * cos - relY * sin;
            const rotY = relX * sin + relY * cos;

            screenX = rotX + centerX;
            screenY = rotY + centerY;
        }
    } else if (rotateMap &&
        focusedPlayerPos &&
        !temporarilyDisableRotation &&
        !rotationDisabledUntilRespawn) {

        const centerX = canvasWidth / 2;
        const centerY = canvasHeight / 2;

        const relX = screenX - centerX;
        const relY = screenY - centerY;

        const angle = degreesToRadians(focusedPlayerYaw + 270);
        const cos = Math.cos(angle);
        const sin = Math.sin(angle);

        const rotX = relX * cos - relY * sin;
        const rotY = relX * sin + relY * cos;

        screenX = rotX + centerX;
        screenY = rotY + centerY;
    }

    const finalTextSize = playerCentered ?
        minTextSize * textSizeMultiplier * playerCenteredZoom :
        minTextSize * textSizeMultiplier;

    return {
        pos: { x: screenX, y: screenY },
        textSize: finalTextSize
    };
}

function updateZoomLevel(value) {
    playerCenteredZoom = parseFloat(value);
    const valueDisplay = document.getElementById('zoomLevelValue');
    if (valueDisplay) valueDisplay.textContent = value;
    localStorage.setItem('playerCenteredZoom', value);
}

function toggleCentered() {
    playerCentered = !playerCentered;
    updateZoomSliderVisibility();
    if (focusedPlayerName === FULL_MAP_VIEW) {
        playerCentered = false;
        document.getElementById('centerCheck').checked = false;
    }
}

function updateZoomSliderVisibility() {
    const zoomSliderContainer = document.getElementById('zoomLevelContainer');
    if (zoomSliderContainer) {
        zoomSliderContainer.style.display = playerCentered ? 'block' : 'none';
    }
}

function toggleOffscreenIndicators() {
    showOffscreenIndicators = !showOffscreenIndicators;
    localStorage.setItem('showOffscreenIndicators', showOffscreenIndicators ? 'true' : 'false');
}

function drawPlayerHealth(pos, playerType, health, hasBomb) {
    if (!map) return;

    const transformed = mapAndTransformCoordinates(pos);
    const mapPos = transformed.pos;
    const textSize = transformed.textSize;

    let extraOffset = 0;
    if (drawNames) extraOffset += 15;
    if (drawGuns) extraOffset += 15;
    if (hasBomb) extraOffset += 15;
    if (drawMoney) extraOffset += 15;

    let textY = mapPos.y + 20 + extraOffset;

    let healthColor;
    if (health > 70) {
        healthColor = "#32CD32";
    } else if (health > 30) {
        healthColor = "#FFFF00";
    } else {
        healthColor = "#FF0000";
    }

    const barWidth = Math.max(60, 40 * textSizeMultiplier) * healthBarSizeMultiplier;
    const barHeight = Math.max(8, 5 * textSizeMultiplier) * healthBarSizeMultiplier;

    ctx.fillStyle = "#444444";
    ctx.fillRect(mapPos.x - barWidth / 2, textY, barWidth, barHeight);

    ctx.fillStyle = healthColor;
    const healthWidth = (health / 100) * barWidth;
    ctx.fillRect(mapPos.x - barWidth / 2, textY, healthWidth, barHeight);

    ctx.font = `bold ${textSize}px Arial`;
    ctx.textAlign = "center";
    ctx.textBaseline = "top";

    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    ctx.strokeText(`${health}HP`, mapPos.x, textY + barHeight + 2);
    ctx.fillStyle = healthColor;
    ctx.fillText(`${health}HP`, mapPos.x, textY + barHeight + 2);
}

function drawEntities() {
    if (!entityData) return;

    const clipRect = {
        x: -50,
        y: -50,
        width: canvas.width + 100,
        height: canvas.height + 100
    };

    const offscreenEnemies = [];

    entityData.forEach((entity, index) => {
        const entityId = `entity_${index}`;
        let interpolatedEntity = null;

        if (NETWORK_SETTINGS.useInterpolation) {
            interpolatedEntity = getInterpolatedEntityData(entityId);
        }

        const renderEntity = interpolatedEntity || entity;

        if (!renderEntity) return;

        let pos;
        if (renderEntity.Bomb) {
            pos = renderEntity.Bomb.pos;
        } else if (renderEntity.Player) {
            pos = renderEntity.Player.pos;
        } else {
            return;
        }

        if (!pos) return;

        const transformed = mapAndTransformCoordinates(pos);
        const mapPos = transformed.pos;

        const isVisible = mapPos.x >= clipRect.x &&
            mapPos.x <= clipRect.x + clipRect.width &&
            mapPos.y >= clipRect.y &&
            mapPos.y <= clipRect.y + clipRect.height;

        if (!isVisible && renderEntity.Player &&
            renderEntity.Player.playerType === "Enemy" &&
            playerCentered && showOffscreenIndicators) {

            offscreenEnemies.push({
                pos: mapPos,
                originalPos: pos,
                player: renderEntity.Player
            });
        }

        if (isVisible) {
            if (renderEntity.Bomb) {
                drawBomb(renderEntity.Bomb.pos, renderEntity.Bomb.isPlanted);
            } else if (renderEntity.Player) {
                const player = renderEntity.Player;
                let fillStyle = localColor;

                switch (player.playerType) {
                    case "Team": fillStyle = teamColor; break;
                    case "Enemy": fillStyle = enemyColor; break;
                }

                drawEntity(
                    player.pos,
                    fillStyle,
                    player.isDormant,
                    player.hasBomb,
                    player.yaw,
                    player.hasAwp,
                    player.playerType,
                    player.isScoped,
                    player.playerName,
                    false,
                    player.weaponId
                );

                if (!player.isDormant) {
                    if (drawNames) {
                        drawPlayerName(
                            player.pos,
                            player.playerName,
                            player.playerType,
                            player.hasAwp,
                            player.hasBomb,
                            player.isScoped
                        );
                    }

                    if (drawGuns) {
                        drawPlayerWeapon(
                            player.pos,
                            player.playerType,
                            player.weaponId
                        );
                    }

                    if (player.hasBomb) {
                        drawPlayerBomb(
                            player.pos,
                            player.playerType
                        );
                    }

                    if (drawMoney && typeof player.money === 'number') {
                        drawPlayerMoney(
                            player.pos,
                            player.playerType,
                            player.money,
                            player.hasBomb
                        );
                    }

                    if (drawHealth && typeof player.health === 'number') {
                        drawPlayerHealth(
                            player.pos,
                            player.playerType,
                            player.health,
                            player.hasBomb
                        );
                    }
                }
            }
        }
    });

    if (playerCentered && showOffscreenIndicators) {
        drawOffscreenIndicators(offscreenEnemies);
    }
}

function drawOffscreenIndicators(offscreenEnemies) {
    if (!offscreenEnemies.length) return;

    const centerX = canvas.width / 2;
    const centerY = canvas.height / 2;
    const padding = 40;

    offscreenEnemies.forEach(enemy => {
        const dx = enemy.pos.x - centerX;
        const dy = enemy.pos.y - centerY;
        let angle = Math.atan2(dy, dx);

        const indicatorRadius = Math.min(canvas.width, canvas.height) / 2 - padding;
        let indicatorX = centerX + Math.cos(angle) * indicatorRadius;
        let indicatorY = centerY + Math.sin(angle) * indicatorRadius;

        const distance = Math.sqrt(dx * dx + dy * dy);
        const maxDistance = Math.sqrt(canvas.width * canvas.width + canvas.height * canvas.height);
        const opacity = 0.4 + (1 - Math.min(distance / maxDistance, 1)) * 0.6;

        drawOffscreenIndicator(
            indicatorX,
            indicatorY,
            angle,
            enemyColor,
            opacity,
            enemy.player
        );
    });
}

function drawOffscreenIndicator(x, y, angle, color, opacity, player) {
    const size = 14 * entitySizeMultiplier;

    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(angle);

    ctx.beginPath();
    ctx.moveTo(size, 0);
    ctx.lineTo(-size / 2, -size / 2);
    ctx.lineTo(-size / 2, size / 2);
    ctx.closePath();

    const r = parseInt(color.slice(1, 3), 16);
    const g = parseInt(color.slice(3, 5), 16);
    const b = parseInt(color.slice(5, 7), 16);

    ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${opacity})`;
    ctx.fill();

    ctx.strokeStyle = 'black';
    ctx.lineWidth = 1;
    ctx.stroke();

    if (player.hasAwp) {
        ctx.beginPath();
        ctx.arc(-size / 2, 0, size / 4, 0, Math.PI * 2);
        ctx.fillStyle = 'orange';
        ctx.fill();
        ctx.stroke();
    }

    if (drawHealth && typeof player.health === 'number') {
        let healthColor;
        if (player.health > 70) {
            healthColor = "#32CD32";
        } else if (player.health > 30) {
            healthColor = "#FFFF00";
        } else {
            healthColor = "#FF0000";
        }

        ctx.beginPath();
        ctx.arc(-size / 2, -size / 2, size / 4, 0, Math.PI * 2);
        ctx.fillStyle = healthColor;
        ctx.fill();
        ctx.stroke();
    }

    ctx.restore();
}

function drawBombTimer() {
    if (!radarData || !radarData.bombPlanted || radarData.bombExploded || radarData.bombDefuseTimeleft < 0) {
        return;
    }

    const maxWidth = 1024 - 128 - 128;
    const timeleft = radarData.bombDefuseTimeleft;

    const timerHeight = Math.max(16, 10 * canvasScale);
    const timerY = Math.max(16, 10 * canvasScale);
    const fontSize = Math.max(24, 18 * canvasScale);

    ctx.fillStyle = "black";
    ctx.fillRect(128, timerY, maxWidth, timerHeight);

    if (radarData.bombBeingDefused) {
        ctx.fillStyle = radarData.bombCanDefuse ? teamColor : enemyColor;
    } else {
        ctx.fillStyle = bombColor;
    }

    ctx.fillRect(130, timerY + 2, (maxWidth - 2) * (timeleft / 40), timerHeight - 4);

    ctx.font = `bold ${fontSize}px Arial`;
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillStyle = textColor;
    ctx.fillText(`${timeleft.toFixed(1)}s`, 1024 / 2, timerY + timerHeight + fontSize / 2 + 4);

    ctx.strokeStyle = "black";
    ctx.lineWidth = 2;

    ctx.beginPath();
    ctx.moveTo(128 + (maxWidth * (5 / 40)), timerY);
    ctx.lineTo(128 + (maxWidth * (5 / 40)), timerY + timerHeight);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(130 + (maxWidth - 2) * (10 / 40), timerY);
    ctx.lineTo(130 + (maxWidth - 2) * (10 / 40), timerY + timerHeight);
    ctx.stroke();

    if (radarData.bombCanDefuse) {
        ctx.strokeStyle = "green";
        ctx.beginPath();
        ctx.moveTo(130 + (maxWidth - 2) * (radarData.bombDefuseEnd / 40), timerY);
        ctx.lineTo(130 + (maxWidth - 2) * (radarData.bombDefuseEnd / 40), timerY + timerHeight);
        ctx.stroke();
    }
}

function fillCanvas() {
    ctx.fillStyle = "#0f0f0f";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
}

function updatePlayerDropdown() {
    const dropdown = document.getElementById('playerSelect');
    if (!dropdown) return;

    const currentValue = dropdown.value;
    const savedPlayer = localStorage.getItem('selectedPlayer');

    while (dropdown.options.length > 0) {
        dropdown.remove(0);
    }

    const fullMapOption = document.createElement('option');
    fullMapOption.value = FULL_MAP_VIEW;
    fullMapOption.textContent = "FULL MAP";
    dropdown.appendChild(fullMapOption);
    
    const localOption = document.createElement('option');
    localOption.value = "local";
    localOption.textContent = "YOU";
    dropdown.appendChild(localOption);

    for (const playerName in playerList) {
        if (playerName !== LOCAL_PLAYER_VIEW && playerName !== FULL_MAP_VIEW) {
            const option = document.createElement('option');
            option.value = playerName;
            
            if (playerList[playerName].isDead) {
                option.textContent = `${playerName} (DEAD)`;
                option.style.color = "#777777";
            } else {
                option.textContent = playerName;
            }
            
            dropdown.appendChild(option);
        }
    }

    if (Object.keys(playerList).includes(currentValue) || currentValue === "local" || currentValue === FULL_MAP_VIEW) {
        dropdown.value = currentValue;
    } 
    else if (savedPlayer === FULL_MAP_VIEW) {
        dropdown.value = FULL_MAP_VIEW;
        focusedPlayerName = FULL_MAP_VIEW;
        playerCentered = false;
    }
    else if (savedPlayer && Object.keys(playerList).includes(savedPlayer)) {
        dropdown.value = savedPlayer;
        focusedPlayerName = savedPlayer;
        
        if (playerList[savedPlayer]?.isDead) {
            dropdown.value = FULL_MAP_VIEW;
            focusedPlayerName = FULL_MAP_VIEW;
            localStorage.setItem('selectedPlayer', FULL_MAP_VIEW);
            playerCentered = false;
        }
    } 
    else {
        dropdown.value = FULL_MAP_VIEW;
        focusedPlayerName = FULL_MAP_VIEW;
        localStorage.setItem('selectedPlayer', FULL_MAP_VIEW);
        playerCentered = false;
    }
}

function changePlayerFocus() {
    const dropdown = document.getElementById('playerSelect');
    
    if (dropdown.value !== "local" && 
        dropdown.value !== FULL_MAP_VIEW && 
        playerList[dropdown.value]?.isDead) {
        
        console.log(`[radarflow] Attempted to focus on dead player ${dropdown.value}, switching to full map view`);
        localStorage.setItem('lastFocusedPlayer', dropdown.value);
        lastFocusedPlayer = dropdown.value;
        dropdown.value = FULL_MAP_VIEW;
        focusedPlayerName = FULL_MAP_VIEW;
        playerCentered = false;
    } 
    else if (dropdown.value === FULL_MAP_VIEW) {
        focusedPlayerName = FULL_MAP_VIEW;
        playerCentered = false;
    } 
    else {
        localStorage.removeItem('lastFocusedPlayer');
        lastFocusedPlayer = null;
        focusedPlayerName = dropdown.value === "local" ? LOCAL_PLAYER_VIEW : dropdown.value;
        
        playerCentered = document.getElementById('centerCheck').checked;
        
        if (playerList[focusedPlayerName]) {
            focusedPlayerPos = playerList[focusedPlayerName].pos;
            focusedPlayerYaw = playerList[focusedPlayerName].yaw;
        }
        
        rotationDisabledUntilRespawn = false;
    }
    
    localStorage.setItem('selectedPlayer', focusedPlayerName);
    update = true;
}

function mapCoordinates(coordinates) {
    if (!map || !coordinates) {
        return { x: 0, y: 0 };
    }

    const offset_x = (coordinates.x - map.pos_x) / map.scale;
    const offset_y = (coordinates.y - map.pos_y) / -map.scale;

    return { x: offset_x, y: offset_y };
}

function drawPlayerName(pos, playerName, playerType, hasAwp, hasBomb, isScoped) {
    if (!map) return;

    const transformed = mapAndTransformCoordinates(pos);
    const mapPos = transformed.pos;
    const textSize = transformed.textSize;

    const textY = mapPos.y + 20;

    let displayName = playerName;
    if (playerType === "Local") {
        displayName = "YOU";
        ctx.fillStyle = localColor;
    } else if (playerType === "Team") {
        ctx.fillStyle = teamColor;
    } else if (playerType === "Enemy") {
        ctx.fillStyle = enemyColor;
    }

    if (isScoped) {
        displayName += " [SCOPED]";
    }

    ctx.font = `bold ${textSize}px Arial`;
    ctx.textAlign = "center";
    ctx.textBaseline = "top";

    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    ctx.strokeText(displayName, mapPos.x, textY);
    ctx.fillText(displayName, mapPos.x, textY);
}

function drawPlayerMoney(pos, playerType, money, hasBomb) {
    if (!map) return;

    const transformed = mapAndTransformCoordinates(pos);
    const mapPos = transformed.pos;
    const textSize = transformed.textSize * 0.8;

    let extraOffset = 0;
    if (drawNames) extraOffset += 15;
    if (drawGuns) extraOffset += 15;
    if (hasBomb) extraOffset += 15;

    let textY = mapPos.y + 20 + extraOffset;

    const formattedMoney = '$' + (money || 0).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");

    if (money >= 10000) {
        ctx.fillStyle = "#32CD32";
    } else if (money >= 4500) {
        ctx.fillStyle = "#FFFF00";
    } else {
        ctx.fillStyle = "#FF4500";
    }

    ctx.font = `bold ${textSize}px Arial`;
    ctx.textAlign = "center";
    ctx.textBaseline = "top";

    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    ctx.strokeText(formattedMoney, mapPos.x, textY);
    ctx.fillText(formattedMoney, mapPos.x, textY);
}

function drawPlayerWeapon(pos, playerType, weaponId) {
    if (!map) return;

    const transformed = mapAndTransformCoordinates(pos);
    const mapPos = transformed.pos;
    const textSize = transformed.textSize * 0.8;

    const textY = mapPos.y + (drawNames ? 35 : 20);

    let weaponName = getWeaponName(weaponId);

    if (weaponId === 9) {
        ctx.fillStyle = "orange";
    } else {
        ctx.fillStyle = textColor;
    }

    ctx.font = `bold ${textSize}px Arial`;
    ctx.textAlign = "center";
    ctx.textBaseline = "top";

    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    ctx.strokeText(`[${weaponName}]`, mapPos.x, textY);
    ctx.fillText(`[${weaponName}]`, mapPos.x, textY);
}

function drawPlayerBomb(pos, playerType) {
    if (!map) return;

    const transformed = mapAndTransformCoordinates(pos);
    const mapPos = transformed.pos;
    const textSize = transformed.textSize * 0.8;

    const textY = mapPos.y + (drawNames ? (drawGuns ? 50 : 35) : (drawGuns ? 35 : 20));

    ctx.fillStyle = bombColor;
    ctx.font = `bold ${textSize}px Arial`;
    ctx.textAlign = "center";
    ctx.textBaseline = "top";

    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    ctx.strokeText("[C4]", mapPos.x, textY);
    ctx.fillText("[C4]", mapPos.x, textY);
}

function drawBomb(pos, planted) {
    if (!map) return;

    const transformed = mapAndTransformCoordinates(pos);
    const mapPos = transformed.pos;
    const size = minEntitySize * entitySizeMultiplier;

    ctx.beginPath();
    ctx.arc(mapPos.x, mapPos.y, size, 0, 2 * Math.PI);
    ctx.fillStyle = bombColor;
    ctx.fill();

    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    ctx.stroke();

    ctx.font = `bold ${Math.max(size * 1.2, minTextSize)}px Arial`;
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillStyle = "white";
    ctx.fillText("C4", mapPos.x, mapPos.y);

    ctx.closePath();

    if (planted && ((new Date().getTime() / 1000) % 1) > 0.5) {
        ctx.strokeStyle = enemyColor;
        ctx.lineWidth = 3;
        ctx.beginPath();
        ctx.arc(mapPos.x, mapPos.y, size + 4, 0, 2 * Math.PI);
        ctx.stroke();
    }
}

function drawEntity(pos, fillStyle, dormant, hasBomb, yaw, hasAwp, playerType, isScoped, playerName, isPlanted, weaponId) {
    if (!map) return;

    const transformed = mapAndTransformCoordinates(pos);
    const mapPos = transformed.pos;

    let circleRadius = minEntitySize * entitySizeMultiplier;
    const distance = circleRadius + 2;
    const radius = distance + 5;
    const arrowWidth = 35;

    const isFocusedPlayer = playerName === focusedPlayerName ||
        (focusedPlayerName === LOCAL_PLAYER_VIEW && playerType === "Local");
    
    const isDeadPlayer = playerList[playerName]?.isDead || false;

    let adjustedYaw = yaw;

    const shouldAdjustRotation = rotateMap &&
        !temporarilyDisableRotation &&
        !rotationDisabledUntilRespawn;

    if (shouldAdjustRotation) {
        if (isFocusedPlayer) {
            adjustedYaw = 90;
        } else {
            adjustedYaw = (yaw + 180) - focusedPlayerYaw + 270;
        }
    }

    if (dormant || isDeadPlayer) {
        if (isDeadPlayer) {
            ctx.beginPath();
            ctx.arc(mapPos.x, mapPos.y, circleRadius, 0, 2 * Math.PI);
            ctx.fillStyle = "#777777";
            ctx.fill();
            ctx.closePath();

            const xSize = circleRadius * 0.7;
            ctx.beginPath();
            ctx.moveTo(mapPos.x - xSize, mapPos.y - xSize);
            ctx.lineTo(mapPos.x + xSize, mapPos.y + xSize);
            ctx.moveTo(mapPos.x + xSize, mapPos.y - xSize);
            ctx.lineTo(mapPos.x - xSize, mapPos.y + xSize);
            ctx.strokeStyle = "#000000";
            ctx.lineWidth = 2;
            ctx.stroke();
            ctx.closePath();
        } else {
            ctx.font = `bold ${transformed.textSize}px Arial`;
            ctx.textAlign = "center";
            ctx.fillStyle = fillStyle;
            ctx.fillText("?", mapPos.x, mapPos.y);
        }
    } else {
        if (isFocusedPlayer) {
            ctx.beginPath();
            ctx.arc(mapPos.x, mapPos.y, circleRadius + 4, 0, 2 * Math.PI);
            ctx.fillStyle = "#FFFFFF";
            ctx.fill();
            ctx.closePath();
        }

        if (hasAwp) {
            ctx.beginPath();
            ctx.arc(mapPos.x, mapPos.y, circleRadius, 0, 2 * Math.PI);
            ctx.fillStyle = "orange";
            ctx.fill();
            circleRadius -= 2;
        }

        // Draw circle
        ctx.beginPath();
        ctx.arc(mapPos.x, mapPos.y, circleRadius, 0, 2 * Math.PI);
        ctx.fillStyle = fillStyle;
        ctx.fill();
        ctx.closePath();

        const arrowHeadX = mapPos.x + radius * Math.cos(adjustedYaw * (Math.PI / 180));
        const arrowHeadY = mapPos.y - radius * Math.sin(adjustedYaw * (Math.PI / 180));

        const arrowCornerX1 = mapPos.x + distance * Math.cos((adjustedYaw - arrowWidth) * (Math.PI / 180));
        const arrowCornerY1 = mapPos.y - distance * Math.sin((adjustedYaw - arrowWidth) * (Math.PI / 180));

        const arrowCornerX2 = mapPos.x + distance * Math.cos((adjustedYaw + arrowWidth) * (Math.PI / 180));
        const arrowCornerY2 = mapPos.y - distance * Math.sin((adjustedYaw + arrowWidth) * (Math.PI / 180));

        const cicleYaw = 90 - adjustedYaw;
        const startAngle = degreesToRadians(cicleYaw - arrowWidth) - Math.PI / 2;
        const endAngle = degreesToRadians(cicleYaw + arrowWidth) - Math.PI / 2;

        // Draw arrow
        ctx.beginPath();
        ctx.arc(mapPos.x, mapPos.y, distance, startAngle, endAngle);
        ctx.lineTo(arrowCornerX1, arrowCornerY1);
        ctx.lineTo(arrowHeadX, arrowHeadY);
        ctx.lineTo(arrowCornerX2, arrowCornerY2);
        ctx.closePath();
        ctx.fillStyle = 'white';
        ctx.fill();

        if (isScoped) {
            const lineOfSightX = arrowHeadX + 1024 * Math.cos(adjustedYaw * (Math.PI / 180));
            const lineOfSightY = arrowHeadY - 1024 * Math.sin(adjustedYaw * (Math.PI / 180));
            ctx.beginPath();
            ctx.moveTo(arrowHeadX, arrowHeadY);
            ctx.lineTo(lineOfSightX, lineOfSightY);

            ctx.strokeStyle = playerType == "Enemy" ? enemyColor : teamColor;
            ctx.lineWidth = 2;
            ctx.stroke();
        }
    }
}

function getWeaponName(weaponId) {
    if (weaponIdMap[weaponId]) {
        return weaponIdMap[weaponId];
    }

    if (weaponId >= 500) {
        return "KNIFE";
    }

    return "KNIFE";
}

function loadMap(mapName) {
    if (!mapName) return;

    console.log(`[radarflow] Loading map "${mapName}"`);
    loaded = true;

    // Load JSON data
    const jsonPath = `assets/json/${mapName}.json`;
    fetch(jsonPath)
        .then(response => {
            if (!response.ok) throw new Error(`JSON not found: ${response.status}`);
            return response.json();
        })
        .then(data => {
            console.log("[radarflow] Map data loaded");
            map = data;
            update = true;
        })
        .catch(error => {
            console.error(`[radarflow] Error loading JSON: ${error}`);
        });

    const imagePath = `assets/image/${mapName}_radar_psd.png`;
    const map_img = new Image();

    map_img.onload = () => {
        console.log("[radarflow] Map image loaded");
        image = map_img;
        update = true;
    };

    map_img.onerror = (e) => {
        console.error(`[radarflow] Error loading image: ${e}`);
    };

    map_img.src = imagePath;
}

function unloadMap() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    map = null;
    mapName = null;
    loaded = false;
    update = true;
}

function processData(data) {
    if (!data) return;

    const now = performance.now();
    lastUpdateTime = now;
    lastResponseTime = now;
    connectionHealthy = true;
    isRequestPending = false;

    radarData = data;
    freq = data.freq;
    entityData = data.entityData;

    if (data.money_reveal_enabled !== undefined) {
        const checkbox = document.getElementById("moneyReveal");
        if (checkbox) checkbox.checked = data.money_reveal_enabled;
    }

    if (data.ingame === false) {
        if (loaded) unloadMap();
    } else {
        if (!loaded && data.mapName) {
            mapName = data.mapName;
            loadMap(mapName);
        }
    }

    update = true;
}

function decompressData(data) {
    try {
        pingTracker.endRequest();

        clearTimeout(requestTimeoutTimer);
        lastResponseTime = performance.now();
        connectionHealthy = true;
        retryCount = 0;

        const rtt = pingTracker.getAveragePing();
        networkLatencyHistory.push(rtt);
        if (networkLatencyHistory.length > 10) {
            networkLatencyHistory.shift();
        }

        if (data[0] === 0x01) {
            try {
                if (typeof pako === 'undefined') {
                    console.error("[radarflow] Pako library not available");
                    return null;
                }

                const decompressed = pako.inflate(data.slice(1));
                const text = new TextDecoder().decode(decompressed);
                return JSON.parse(text);
            } catch (e) {
                console.error("[radarflow] Decompression error:", e);
                return null;
            }
        } else if (data[0] === 0x00) {
            try {
                const text = new TextDecoder().decode(data.slice(1));
                return JSON.parse(text);
            } catch (e) {
                console.error("[radarflow] Parse error:", e);
                return null;
            }
        } else {
            console.error("[radarflow] Unknown data format");
            return null;
        }
    } catch (e) {
        console.error("[radarflow] Data processing error:", e);
        isRequestPending = false;
        return null;
    }
}

function connect() {
    reconnecting = true;

    if (websocket == null) {
        console.log(`[radarflow] Connecting to ${websocketAddr}`);

        let socket = new WebSocket(websocketAddr);
        socket.binaryType = "arraybuffer";

        socket.onopen = () => {
            console.log("[radarflow] Connection established");
            lastResponseTime = performance.now();
            connectionHealthy = true;
            reconnecting = false;
            isRequestPending = false;
            retryCount = 0;

            setTimeout(() => {
                socket.send(`ping:0`);
            }, 500);

            if (!fpsStartTime) {
                requestAnimationFrame(render);
            }
        };

        socket.onmessage = (event) => {
            if (event.data === "pong") {
                lastResponseTime = performance.now();
                return;
            }

            if (event.data === "error") {
                console.error("[radarflow] Server error");
                isRequestPending = false;
                return;
            }

            if (event.data instanceof ArrayBuffer) {
                const data = new Uint8Array(event.data);
                const jsonData = decompressData(data);
                if (jsonData) processData(jsonData);
            } else if (typeof event.data === 'string') {
                try {
                    const jsonData = JSON.parse(event.data);
                    if (jsonData.action === "toggleMoneyReveal") {
                        document.getElementById("moneyReveal").checked = jsonData.enabled;
                    } else {
                        processData(jsonData);
                    }

                    lastResponseTime = performance.now();
                } catch (e) {
                    console.error("[radarflow] JSON parse error:", e);
                }
            }
        };

        socket.onclose = (event) => {
            console.log("[radarflow] Connection closed");
            websocket = null;

            if (!reconnecting) {
                unloadMap();
            }

            setTimeout(connect, NETWORK_SETTINGS.reconnectDelay);
        };

        socket.onerror = (error) => {
            console.error("[radarflow] WebSocket error:", error);
        };

        websocket = socket;
    } else {
        reconnecting = false;
    }
}

function updateTextSize(value) {
    textSizeMultiplier = parseFloat(value);
    const valueDisplay = document.getElementById('textSizeValue');
    if (valueDisplay) valueDisplay.textContent = value;
    localStorage.setItem('textSizeMultiplier', value);
}

function updateEntitySize(value) {
    entitySizeMultiplier = parseFloat(value);
    const valueDisplay = document.getElementById('entitySizeValue');
    if (valueDisplay) valueDisplay.textContent = value;
    localStorage.setItem('entitySizeMultiplier', value);
}

function updateHealthBarSize(value) {
    healthBarSizeMultiplier = parseFloat(value);
    const valueDisplay = document.getElementById('healthBarSizeValue');
    if (valueDisplay) valueDisplay.textContent = value;
    localStorage.setItem('healthBarSizeMultiplier', value);
}

function resetSizes() {
    const textSlider = document.getElementById('textSizeSlider');
    const entitySlider = document.getElementById('entitySizeSlider');
    const zoomSlider = document.getElementById('zoomLevelSlider');
    const healthBarSlider = document.getElementById('healthBarSizeSlider');

    if (textSlider) textSlider.value = DEFAULT_TEXT_SIZE.toString();
    if (entitySlider) entitySlider.value = DEFAULT_ENTITY_SIZE.toString();
    if (zoomSlider) zoomSlider.value = DEFAULT_ZOOM_LEVEL.toString();
    if (healthBarSlider) healthBarSlider.value = DEFAULT_HEALTH_BAR_SIZE.toString();

    updateTextSize(DEFAULT_TEXT_SIZE.toString());
    updateEntitySize(DEFAULT_ENTITY_SIZE.toString());
    updateZoomLevel(DEFAULT_ZOOM_LEVEL.toString());
    updateHealthBarSize(DEFAULT_HEALTH_BAR_SIZE.toString());
}

function toggleZoom() {
    shouldZoom = !shouldZoom;
}

function toggleStats() {
    drawStats = !drawStats;
}

function toggleNames() {
    drawNames = !drawNames;
}

function toggleGuns() {
    drawGuns = !drawGuns;
}

function toggleRotate() {
    rotateMap = !rotateMap;
}

function toggleMoneyReveal() {
    if (websocket && websocket.readyState === WebSocket.OPEN) {
        console.log("[radarflow] Sending toggleMoneyReveal command");
        websocket.send("toggleMoneyReveal");
    }
}

function toggleDisplayMoney() {
    drawMoney = !drawMoney;
    update = true;
    localStorage.setItem('drawMoney', drawMoney ? 'true' : 'false');
}

function togglePerformanceMode() {
    const performanceMode = document.getElementById('performanceMode').checked;

    if (performanceMode) {
        drawNames = false;
        drawGuns = false;
        drawMoney = false;
        drawHealth = false;

        NETWORK_SETTINGS.interpolationAmount = 0.85;

        document.getElementById("namesCheck").checked = false;
        document.getElementById("gunsCheck").checked = false;
        document.getElementById("moneyDisplay").checked = false;
        document.getElementById("healthCheck").checked = false;

        console.log("[radarflow] Performance mode enabled with enhanced smoothing");
    } else {
        drawNames = document.getElementById("namesCheck").checked = true;
        drawGuns = document.getElementById("gunsCheck").checked = true;
        drawMoney = document.getElementById("moneyDisplay").checked = true;
        drawHealth = document.getElementById("healthCheck").checked = true;

        NETWORK_SETTINGS.interpolationAmount = 0.7;

        console.log("[radarflow] Performance mode disabled");
    }
}

window.addEventListener('resize', () => {
    if (canvas) {
        const canvasRect = canvas.getBoundingClientRect();
        canvasScale = Math.min(canvasRect.width, canvasRect.height) / 1024;
    }
});

addEventListener("DOMContentLoaded", () => {
    const savedDrawHealth = localStorage.getItem('drawHealth');
    drawHealth = savedDrawHealth !== null ? savedDrawHealth === 'true' : true;

    const savedDrawMoney = localStorage.getItem('drawMoney');
    drawMoney = savedDrawMoney !== null ? savedDrawMoney === 'true' : true;

    const savedTextSize = localStorage.getItem('textSizeMultiplier');
    textSizeMultiplier = savedTextSize !== null ? parseFloat(savedTextSize) : DEFAULT_TEXT_SIZE;

    const savedEntitySize = localStorage.getItem('entitySizeMultiplier');
    entitySizeMultiplier = savedEntitySize !== null ? parseFloat(savedEntitySize) : DEFAULT_ENTITY_SIZE;
    
    const savedHealthBarSize = localStorage.getItem('healthBarSizeMultiplier');
    healthBarSizeMultiplier = savedHealthBarSize !== null ? parseFloat(savedHealthBarSize) : DEFAULT_HEALTH_BAR_SIZE;

    const savedOffscreenIndicators = localStorage.getItem('showOffscreenIndicators');
    showOffscreenIndicators = savedOffscreenIndicators !== null ? savedOffscreenIndicators === 'true' : true;

    const checkboxes = {
        "zoomCheck": false,
        "statsCheck": true,
        "namesCheck": true,
        "gunsCheck": true,
        "moneyDisplay": drawMoney,
        "moneyReveal": false,
        "rotateCheck": true,
        "centerCheck": true,
        "healthCheck": drawHealth,
        "offscreenCheck": showOffscreenIndicators
    };

    Object.entries(checkboxes).forEach(([id, state]) => {
        const checkbox = document.getElementById(id);
        if (checkbox) checkbox.checked = state;
    });

    const textSizeSlider = document.getElementById('textSizeSlider');
    if (textSizeSlider) {
        textSizeSlider.value = textSizeMultiplier;
        const textSizeValue = document.getElementById('textSizeValue');
        if (textSizeValue) textSizeValue.textContent = textSizeMultiplier;
    }

    const entitySizeSlider = document.getElementById('entitySizeSlider');
    if (entitySizeSlider) {
        entitySizeSlider.value = entitySizeMultiplier;
        const entitySizeValue = document.getElementById('entitySizeValue');
        if (entitySizeValue) entitySizeValue.textContent = entitySizeMultiplier;
    }

    const healthBarSizeSlider = document.getElementById('healthBarSizeSlider');
    if (healthBarSizeSlider) {
        healthBarSizeSlider.value = healthBarSizeMultiplier;
        const healthBarSizeValue = document.getElementById('healthBarSizeValue');
        if (healthBarSizeValue) healthBarSizeValue.textContent = healthBarSizeMultiplier;
    }

    const savedZoom = localStorage.getItem('playerCenteredZoom');
    playerCenteredZoom = savedZoom !== null ? parseFloat(savedZoom) : DEFAULT_ZOOM_LEVEL;

    const zoomSlider = document.getElementById('zoomLevelSlider');
    if (zoomSlider) {
        zoomSlider.value = playerCenteredZoom;
        const zoomValue = document.getElementById('zoomLevelValue');
        if (zoomValue) zoomValue.textContent = playerCenteredZoom;
    }

    updateZoomSliderVisibility();

    canvas = document.getElementById('canvas');
    if (canvas) {
        canvas.width = 1024;
        canvas.height = 1024;
        ctx = canvas.getContext('2d');

        const canvasRect = canvas.getBoundingClientRect();
        canvasScale = Math.min(canvasRect.width, canvasRect.height) / 1024;

        connect();
    } else {
        console.error("[radarflow] Canvas element not found");
    }
});