<?php

if (!isset($segments) || !defined('APP_ROUTER')) {
    http_response_code(403);
    include $baseDir . "/error-pages/403.php";
    exit;
}

$type = $segments[1] ?? null;
$requested_geraet = htmlspecialchars($segments[2] ?? '');

$allowed_types = ["display"];

if (!in_array($type, $allowed_types)) {
    http_response_code(400);
    include $baseDir . "/error-pages/400.html";
    exit;
}

$baseDir = $_SERVER['DOCUMENT_ROOT'];

require_once $baseDir . '/../scripts/session_functions.php';

ini_wkvs_session(true);

$csrf_token = $_SESSION['csrf_token'] ?? '';

require $baseDir . '/../scripts/websocket/ws-create-token.php';

$WSaccesstype = "display";
$WSaccessPermission = "R";

require_once $baseDir . '/../scripts/db/db-verbindung-script-guest.php';
require_once $baseDir . '/../scripts/db/db-functions.php';
require_once $baseDir . '/../scripts/db/db-tables.php';

$stmt = $guest->prepare("SELECT `name`, `id` FROM $db_tabelle_disziplinen ORDER BY start_index ASC");

if (!$stmt->execute()) {
  http_response_code(500);
  exit;
}

$result = $stmt->get_result();
$disciplines = $result->fetch_all(MYSQLI_ASSOC);
$lowerDisciplines = array_change_key_case(
    array_column($disciplines, 'id', 'name'),
    CASE_LOWER
);


if (!array_key_exists($requested_geraet, $lowerDisciplines)) {
    http_response_code(400);
    $message = 'Das gesuchte Gerät kann nicht gefunden werden';
    include $baseDir . "/error-pages/400.php";
    exit;
}

if (!isset($lowerDisciplines[$requested_geraet])) {
    http_response_code(500);
    include $baseDir . "/error-pages/500.html";
    exit;
}

$geraetId = $lowerDisciplines[$requested_geraet];

$WSaccess = (string) $geraetId;


$stmt->close();

// Define a small helper function to keep the code DRY (Don't Repeat Yourself)
function sanitize_hex($color) {
    return preg_replace('/[^0-9a-fA-F#]/', '', $color);
}

$keysNeeded = [
    'wkName', 'displayColourLogo', 'displayTextColourLogo', 'displayColorScoringBg',
    'displayColorScoringBgSoft', 'displayColorScoringPanel', 'displayColorScoringPanelSoft', 'displayColorScoringBorderColor',
    'displayColorScoringShadowColor', 'displayColorScoringPanelText', 'displayColorScoringPanelTextSoft',
    'displayColorScoringPanelTextNoteL', 'displayColorScoringPanelTextNoteR', 'displayIdNoteL',
    'displayIdNoteR', 'displayCTextLogo', 'displayCTextWKName'
];

$placeholders = implode(',', array_fill(0, count($keysNeeded), '?'));

$query = "SELECT `name`, `value` FROM $db_tabelle_var WHERE `name` IN ($placeholders)";

$stmt = $guest->prepare($query);
$params = str_repeat("s", count($keysNeeded));
$stmt->bind_param($params, ...$keysNeeded);
$stmt->execute();
$result = $stmt->get_result();

$config = [];
while ($row = $result->fetch_assoc()) {
    $config[$row['name']] = $row['value'];
}

$wkName                         = $config['wkName'] ?? null;
$displayCTextDisplayLogo        = $config['displayCTextLogo'] ?? null;
$displayCTextDisplayWKName      = $config['displayCTextWKName'] ?? null;
$cleanColor                     = $config['displayColourLogo'] ?? null;
$cleanColorText                 = $config['displayTextColourLogo'] ?? null;
$displayColorScoringBg          = $config['displayColorScoringBg'] ?? null;
$displayColorScoringBgSoft      = $config['displayColorScoringBgSoft'] ?? null;
$displayColorScoringPanel       = $config['displayColorScoringPanel'] ?? null;
$displayColorScoringPanelSoft   = $config['displayColorScoringPanelSoft'] ?? null;
$displayColorScoringShadowColor = $config['displayColorScoringShadowColor'] ?? null;
$displayColorScoringBorderColor = $config['displayColorScoringBorderColor'] ?? null;
$displayColorScoringPanelText   = $config['displayColorScoringPanelText'] ?? null;
$displayColorScoringPanelTextSoft = $config['displayColorScoringPanelTextSoft'] ?? null;
$displayColorScoringPanelTextNoteL = $config['displayColorScoringPanelTextNoteL'] ?? null;
$displayColorScoringPanelTextNoteR = $config['displayColorScoringPanelTextNoteR'] ?? null;

$stmt->close();

$guest->close();

$jsonUrlconfig = '/externe-geraete/json/config.json';
$jsonUrl = '/externe-geraete/json/display-id-' . $geraetId . '.json';


?>
<!DOCTYPE html>
<html>
<head>
  <title><?= $wkName ?> Anzeigen</title>
  <meta name="robots" content="noindex">
  <link rel="stylesheet" href="/externe-geraete/css/display.css">
  <script src="/intern/js/gsap/gsap.min.js"></script>
  <script src="/intern/js/gsap/MorphSVGPlugin.min.js"></script>
  <style>
    :root { 

        /* Brand */
        --display-panelBgLogo: <?= $cleanColor ?>;
        --display-text-color: <?= $cleanColorText ?>;

        /* Backgrounds */
        --display-bg: <?= $displayColorScoringBg ?>;
        --display-bg-soft: <?= $displayColorScoringBgSoft ?>;
        --display-panel: <?= $displayColorScoringPanel ?>;
        --display-panel-soft: <?= $displayColorScoringPanelSoft ?>;

        /* Shadow */
        --display-shadow: <?= $displayColorScoringShadowColor ?>;

        /* Border */
        --display-border: <?= $displayColorScoringBorderColor ?>;
        
        /* Typography */
        --display-text-main: <?= $displayColorScoringPanelText ?>;
        --display-text-muted: <?= $displayColorScoringPanelTextSoft ?>;
        
        /* Noten */
        --display-color-note-l: <?= $displayColorScoringPanelTextNoteL ?>;
        --display-color-note-r: <?= $displayColorScoringPanelTextNoteR ?>;

    }
    </style>
</head>
<body>
    <div class="displayDiv">
        <div class="errorws">
            <svg xmlns="http://www.w3.org/2000/svg" id="errorSvg" viewBox="0 0 800 200">
                <defs>
                    <filter id="a" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="8" result="blur"/>
                    <feMerge>
                        <feMergeNode in="blur"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                    </filter>
                    <linearGradient id="myLinearGradient" x1="0%" y1="5%" x2="100%" y2="0%">
                    <stop offset="0%" stop-color="#22c55e00" />
                    <stop offset="100%" stop-color="#22c55e90" />
                    </linearGradient>
                </defs>
                <path d="M150 100h200m100 0h200" id="errorLine" opacity=".3" stroke="#3b82f6" stroke-width="4" stroke-dasharray="10,15" stroke-linecap="round"/>
                <g transform="translate(150 100)">
                    <circle r="45" fill="#1e293b" stroke="#3b82f6" stroke-width="3"/>
                    <circle r="45" fill="#1e293b" stroke="#3b82f6" stroke-width="3"/>
                    
                    <rect x="-30" y="-19" width="60" height="38" rx="2" fill="#334155"/>
                    <rect x="-28" y="-17" width="56" height="10" rx="2" fill="#555"/>
                    <rect x="-28" y="-5" width="56" height="10" rx="2" fill="#555"/>
                    <rect x="-28" y="7" width="56" height="10" rx="2" fill="#555"/>
                    <text x="-26" y="-11" fill="#94a3b8" font-size="4" font-family="monospace" letter-spacing="0.5">Test WKVS</text>

                    <text x="-26" y="-1" fill="#94a3b8" font-size="2.5" font-family="monospace" letter-spacing="0">WKVS,</text>
                    <text x="-26" y="2.5" fill="#94a3b8" font-size="2" font-family="monospace" letter-spacing="0">EP</text>
                    <rect x="0" y="-5" width="28" rx="2" height="10" fill="url(#myLinearGradient)" />

                    <text x="10" y="1" fill="#22c55e" font-size="4" font-family="monospace" letter-spacing="0.5">START</text>

                    <text x="-26" y="13" fill="#94a3b8" font-size="4" font-family="monospace" letter-spacing="0.5">D 1.40</text>
                    <text x="15" y="13" fill="#94a3b8" font-size="4" font-family="monospace" letter-spacing="0.5">9.40</text>


                    
                    <text y="70" text-anchor="middle" fill="#94a3b8" font-size="12" font-family="monospace" letter-spacing="1.5">Anzeige</text>
                </g>
                <g transform="translate(650 100)">
                    <circle r="45" fill="#1e293b" stroke="#3b82f6" stroke-width="3"/>
                    <rect x="-18" y="-15" width="36" height="10" rx="2" fill="#334155"/>
                    <rect x="-18" y="2" width="36" height="10" rx="2" fill="#334155"/>
                    <circle cx="10" cy="-10" r="2" fill="#10b981"/>
                    <circle cx="10" cy="7" r="2" fill="#10b981"/>
                    <text y="70" text-anchor="middle" fill="#94a3b8" font-size="12" font-family="monospace" letter-spacing="1.5">WKVS Server</text>
                </g>
                <path fill="#f43f5e" class="opacity0gsap" opacity=".4" d="m-14.142-70.71 5.657 5.656-5.657 5.657-5.657-5.657z" transform="translate(400 100)"/>
                <path fill="#f43f5e" class="opacity0gsap" opacity=".4" d="m42.67 39.74 5.869 1.247-1.248 5.87-5.868-1.248z" transform="translate(400 100)"/>
                <path fill="#3b82f6" class="opacity0gsap" opacity=".4" d="m-50.98 28.301 8.66 5-5 8.66-8.66-5z" transform="translate(400 100)"/>
                <g filter="url(#a)" transform="translate(400 100)">
                    <path id="cross" 
                        d="M-30,-30 L30,30 M30,-30 L-30,30"
                        stroke="#f43f5e" 
                        stroke-width="8" 
                        stroke-linecap="round" 
                        fill="none"/>
                </g>
            </svg>

            <svg xmlns="http://www.w3.org/2000/svg" id="okSvg" viewBox="0 0 800 200" style="display: none">
                <defs>
                    <filter id="a" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="8" result="blur"/>
                    <feMerge>
                        <feMergeNode in="blur"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                    </filter>
                    <linearGradient id="myLinearGradient" x1="0%" y1="5%" x2="100%" y2="0%">
                    <stop offset="0%" stop-color="#22c55e00" />
                    <stop offset="100%" stop-color="#22c55e90" />
                    </linearGradient>
                </defs>
                <path d="M150 100 h500" opacity=".3" stroke="#3b82f6" stroke-width="4" stroke-dasharray="10, 15" stroke-linecap="round"/>
                <g transform="translate(150 100)">
                    <circle r="45" fill="#1e293b" stroke="#3b82f6" stroke-width="3"/>
                    <circle r="45" fill="#1e293b" stroke="#3b82f6" stroke-width="3"/>
                    
                    <rect x="-30" y="-19" width="60" height="38" rx="2" fill="#334155"/>
                    <rect x="-28" y="-17" width="56" height="10" rx="2" fill="#555"/>
                    <rect x="-28" y="-5" width="56" height="10" rx="2" fill="#555"/>
                    <rect x="-28" y="7" width="56" height="10" rx="2" fill="#555"/>
                    <text x="-26" y="-11" fill="#94a3b8" font-size="4" font-family="monospace" letter-spacing="0.5">Test WKVS</text>

                    <text x="-26" y="-1" fill="#94a3b8" font-size="2.5" font-family="monospace" letter-spacing="0">WKVS,</text>
                    <text x="-26" y="2.5" fill="#94a3b8" font-size="2" font-family="monospace" letter-spacing="0">EP</text>
                    <rect x="0" y="-5" width="28" rx="2" height="10" fill="url(#myLinearGradient)" />

                    <text x="10" y="1" fill="#22c55e" font-size="4" font-family="monospace" letter-spacing="0.5">START</text>

                    <text x="-26" y="13" fill="#94a3b8" font-size="4" font-family="monospace" letter-spacing="0.5">D 1.40</text>
                    <text x="15" y="13" fill="#94a3b8" font-size="4" font-family="monospace" letter-spacing="0.5">9.40</text>


                    
                    <text y="70" text-anchor="middle" fill="#94a3b8" font-size="12" font-family="monospace" letter-spacing="1.5">Anzeige</text>
                </g>
                <g transform="translate(650 100)">
                    <circle r="45" fill="#1e293b" stroke="#3b82f6" stroke-width="3"/>
                    <rect x="-18" y="-15" width="36" height="10" rx="2" fill="#334155"/>
                    <rect x="-18" y="2" width="36" height="10" rx="2" fill="#334155"/>
                    <circle cx="10" cy="-10" r="2" fill="#10b981"/>
                    <circle cx="10" cy="7" r="2" fill="#10b981"/>
                    <text y="70" text-anchor="middle" fill="#94a3b8" font-size="12" font-family="monospace" letter-spacing="1.5">WKVS Server</text>
                </g>
                
                <g filter="url(#a)" transform="translate(400 100)">
                    <path d="m-15-60 15 30 m30-60 -30 60" stroke="#0e0" stroke-width="8" stroke-linecap="round"/>
                </g>
            </svg>


            <div class="divErrorTextWs">
                <p class="errortext">Keine WebSocket Verbindung</p>
                <p class="errortextSmall">Versuche Verbindung... (Versuch <span id="counterTries"></span> / <span id="counterMaxTries"></span>)</p>
            </div>
        </div>

        <div class="noWsConnection">
            <div class="rotator">
            <p>Keine WebSocket Verbindung, Syncronisation via FETCH</p>
            </div>
        </div>

        <div class="logobg">
            <img id="jsImgLogo" class="logoimg <?= (!$displayCTextDisplayLogo) ? 'cTextHidden' : '' ?>" src="/intern/img/logo-normal.png">
            <p class="logotext <?= (!$displayCTextDisplayWKName) ? 'cTextHidden' : '' ?>"><?= $wkName ?></p>
            <p class="logoctext"></p>
        </div>

        <div class="pagediv">
            <div class="display-row row1">
            <p class="row1text"></p>
            </div>
            <div class="display-row row2">
            <div class="row2text">
                <p class="row2_1text sds"></p>
                <p class="row2_2text"></p>
            </div>
            <div class="start_div">
                <p class="start_text"></p>
            </div>
            </div>
            <div class="display-row row3">
            <p class="row3_1text"></p>
            <p class="row3_2text"></p>
            </div>
        </div>
    </div>
  

  <script>
    
    const userAccess = "<?php echo $lastSegment; ?>_display";
    const jsonUrl = "<?php echo $jsonUrl; ?>";
    const jsonUrlconfig = "<?php echo $jsonUrlconfig; ?>";

    // --- State Management ---
    let ws;
    let filedConectCount = 1;
    const fallbackConectCount = 5;
    const RETRY_DELAY = 3000;
    const FALLBACK_POLL_INTERVAL = 3000; // Fetch every 3 seconds if WS is dead
    const LONG_TERM_RETRY_DELAY_MULTIPLIER = 5;

    let morphOnConnect = false;
    let morphOk = true;

    const WSaccesstype = "<?= $WSaccesstype ?>";
    const WSaccess = "<?= $WSaccess ?>";
    const csrf_token = "<?= $csrf_token ?>";
    const WSaccessPermission = "<?= $WSaccessPermission ?>";
    let firstConnect = true;
    let longTermConnection = false;
    const urlAjaxNewWSToken = '/intern/scripts/ajax-create-ws-token.php';
    
    let fallbackTimer = null;
    
    // Hold our current data in memory
    let displayConfig = { type: 'logo', ctext: '' }; 
    let currentScore = {};
    let lastUniqueId = null;

    const logobg = document.querySelector('.logobg');
    const ctext = document.querySelector('.logoctext');

    // UI Elements
    let counterTriesEl = document.getElementById('counterTries');
    let counterMaxTriesEl = document.getElementById('counterMaxTries');
    
    if(counterTriesEl) counterTriesEl.innerHTML = filedConectCount;
    if(counterMaxTriesEl) counterMaxTriesEl.innerHTML = fallbackConectCount;

    // --- Fullscreen Listener ---
    const fs = document.documentElement;
    document.body.addEventListener('click', () => {
        if (fs.requestFullscreen) fs.requestFullscreen();
        else if (fs.webkitRequestFullscreen) fs.webkitRequestFullscreen();
        else if (fs.mozRequestFullScreen) fs.mozRequestFullScreen();
        else if (fs.msRequestFullscreen) fs.msRequestFullscreen();
    });

    async function fetchNewWSToken(accesstype, access, accessPermission) {
        try {
            const response = await fetch(urlAjaxNewWSToken, {
                method: "POST",
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                },
                body: new URLSearchParams({ 
                    csrf_token,
                    accesstype, 
                    access, 
                    accessPermission 
                })
            });

            if (!response.ok) return null;

            const data = await response.json();
            return data.success ? data.token : null;
        } catch (error) {
            console.error("Token fetch failed:", error);
            return null;
        }
    }

    // --- WebSocket Logic ---
    async function startWebSocket() {
        console.log("Attempting WebSocket connection...");

        let token;
        if (firstConnect) {
            token = '<?= generateWSReadToken($WSaccesstype, $WSaccess) ?>';
        } else {
            token = await fetchNewWSToken(WSaccesstype, WSaccess, WSaccessPermission);
        }

        if (!token) {
            console.error("No valid token available. Retrying...");
            scheduleRetry();
            return;
        }

        try {
            ws = new WebSocket(`wss://${window.location.hostname}/ws/?access=token&token=${token}`);
        } catch (err) {
            console.error("Malformed WebSocket URL", err);
            scheduleRetry();
            return;
        }

        ws.onopen = () => {
            console.log("WebSocket connected!");

            if (morphOnConnect) {
                morph();
            } else {
                document.querySelector('.errorws').style.display = 'none';
                document.querySelector('.noWsConnection').style.display = 'none';
                document.querySelector('.pagediv').classList.remove("manuel");
            }
            
            filedConectCount = 1;
            if(counterTriesEl) counterTriesEl.innerHTML = filedConectCount;

            firstConnect = false;
            longTermConnection = false;
            morphOnConnect = false;
            morphOk = true;

            // Stop fallback polling since WS is alive
            stopFallbackPolling();
            
            // Do ONE initial fetch to sync current state
            fetchFullState();
        };

        ws.onerror = () => {
            if (!longTermConnection) {
                document.querySelector('.errorws').style.display = 'flex';
                document.querySelector('.errorws').style.opacity = '1';
            }
            morphOk = false;
            restore();
        };

        ws.onclose = () => {
            if (!longTermConnection) {
                document.querySelector('.errorws').style.display = 'flex';
                document.querySelector('.errorws').style.opacity = '1';
            }
            morphOnConnect = true;
            morphOk = false;
            restore();
            scheduleRetry();
        };

        ws.addEventListener("message", msg => {
            let msgJSON;
            try {
                msgJSON = JSON.parse(msg.data);
            } catch (error) {
                return; // Ignore malformed messages
            }

            // Route the incoming push data
            switch (msgJSON.type) {
                case "EINSTELLUNGEN_DISPLAY_UPDATE":
                    updateSettings(msgJSON.payload.key, msgJSON.payload.value);
                    break;
                case "UPDATE_DISPLAYCONTROL":
                    // Expecting payload: { type: 'logo'|'ctext'|'scoring', ctext: '...' }
                    displayConfig = msgJSON.payload; 
                    renderDOM();
                    break;
                case "UPDATE_SCORE":
                    // Expecting payload: the exact same structure as your jsonUrl outputs
                    currentScore = msgJSON.payload;
                    renderDOM();
                    break;
            }
        });
    }

    function scheduleRetry() {

        if (longTermConnection) {
            setTimeout(startWebSocket, RETRY_DELAY * LONG_TERM_RETRY_DELAY_MULTIPLIER);
        } else {
            console.log(`Retrying in ${RETRY_DELAY}ms...`);
        
            if (filedConectCount >= fallbackConectCount) {
                // MAX RETRIES REACHED -> Enter Fallback Mode
                longTermConnection = true;
                document.querySelector('.errorws').style.display = 'none';
                document.querySelector('.noWsConnection').style.display = 'flex';
                document.querySelector('.pagediv').classList.add("manuel");
                startFallbackPolling();
            } else {

                if(counterTriesEl) counterTriesEl.innerHTML = filedConectCount;
                setTimeout(startWebSocket, RETRY_DELAY);
                filedConectCount++;
            }
        }
    }

    // --- Fallback Polling Logic ---
    function startFallbackPolling() {
        if (fallbackTimer === null) {
            console.warn("Starting JSON fallback polling...");
            fetchFullState(); // Fetch immediately once
            fallbackTimer = setInterval(fetchFullState, FALLBACK_POLL_INTERVAL);
        }
    }

    function stopFallbackPolling() {
        if (fallbackTimer !== null) {
            clearInterval(fallbackTimer);
            fallbackTimer = null;
            console.log("Stopped JSON fallback polling.");
        }
    }

    // --- Data Fetching (Initial Sync & Fallback) ---
    async function fetchFullState() {
        try {
            // Fetch both config and score simultaneously 
            const [resConfig, resScore] = await Promise.all([
                fetch(jsonUrlconfig + '?t=' + Date.now(), { cache: "no-store" }),
                fetch(jsonUrl + '?t=' + Date.now(), { cache: "no-store" })
            ]);

            if (resConfig.ok) displayConfig = await resConfig.json();
            if (resScore.ok) currentScore = await resScore.json();

            renderDOM();
        } catch (err) {
            console.error("Error fetching JSON:", err);
            const container = document.getElementById('score');
            if(container) {
                container.innerHTML = "";
                container.style.backgroundColor = "black";
            }
        }
    }

    // --- The Master Renderer ---
    function renderDOM() {

        if (displayConfig.type === 'logo' || displayConfig.type === 'ctext') {
            // LOGO OR CUSTOM TEXT MODE
            if (logobg) {
                logobg.style.opacity = "1";
                
                if (displayConfig.type === 'logo') logobg.classList.remove('ctext');
                if (displayConfig.type === 'ctext') logobg.classList.add('ctext');
            }
            if (ctext) {
                ctext.innerText = (displayConfig.type === 'ctext') ? (displayConfig.ctext || '') : '';
                resizeCText();
            }
            
        } else if (displayConfig.type === 'scoring') {
            // SCORING MODE
            if (logobg) logobg.style.opacity = "0";
            
            if (currentScore.uniqueid !== lastUniqueId) {
                lastUniqueId = currentScore.uniqueid;
                // Reset any animation/repeat logic here if needed
            }

            const safeText = (selector, text) => {
                const el = document.querySelector(selector);
                if (el) el.innerText = text !== undefined ? text : '';
            };

            safeText('.row1text', `${currentScore.vorname || ''} ${currentScore.name || ''}`);
            safeText('.row2_1text', currentScore.verein ? `${currentScore.verein}, ` : '');
            safeText('.row2_2text', currentScore.programm || '');

            const starttext = document.querySelector('.start_text');
            const row2El = document.querySelector('.row2');
            if (starttext && row2El) {
                const rootStyles = getComputedStyle(document.documentElement);
                const dangerColor = rootStyles.getPropertyValue('--display-danger').trim();
                const successColor = rootStyles.getPropertyValue('--display-success').trim();

                if (currentScore.start === true) {
                    row2El.style.setProperty('--display-colorStartDiv', successColor);
                    starttext.innerHTML = 'Start';
                } else {
                    row2El.style.setProperty('--display-colorStartDiv', dangerColor);
                    starttext.innerHTML = 'Stop';
                }
            }
            
            safeText('.row3_1text', currentScore.noteLinks);
            safeText('.row3_2text', currentScore.noteRechts);
        }

        fitTextAll();
    }

    // --- Settings & UI Handlers ---
    function updateSettings(type, value) {

        const sanitizeHex = (val) => val.replace(/[^0-9a-fA-F#]/g, '');

        switch (type) {
            case 'wkName':
                const logotext = document.querySelector('.logotext');
                if (logotext) logotext.innerHTML = value;
                break;
            case 'displayColourLogo':
                document.documentElement.style.setProperty('--display-panelBgLogo', sanitizeHex(value));
                break;
            case 'displayTextColourLogo':
                document.documentElement.style.setProperty('--display-text-color', sanitizeHex(value));
                break;
            case 'displayCTextLogo': 
                document.querySelector('.logoimg').classList.toggle('cTextHidden');
                break;
            case 'displayCTextWKName': 
                document.querySelector('.logotext').classList.toggle('cTextHidden');
                break;
            case 'displayColorScoringBg':
                document.documentElement.style.setProperty('--display-bg', sanitizeHex(value));
                break;
            case 'displayColorScoringBgSoft':
                document.documentElement.style.setProperty('--display-bg-soft', sanitizeHex(value));
                break;
            case 'displayColorScoringShadowColor':
                document.documentElement.style.setProperty('--display-shadow', sanitizeHex(value));
                break;
            case 'displayColorScoringBorderColor':
                document.documentElement.style.setProperty('--display-border', sanitizeHex(value));
                break;
            case 'displayColorScoringPanel':
                document.documentElement.style.setProperty('--display-panel', sanitizeHex(value));
                break;
            case 'displayColorScoringPanelSoft':
                document.documentElement.style.setProperty('--display-panel-soft', sanitizeHex(value));
                break;
            case 'displayColorScoringPanelText':
                document.documentElement.style.setProperty('--display-text-main', sanitizeHex(value));
                break;
            case 'displayColorScoringPanelTextSoft':
                document.documentElement.style.setProperty('--display-text-muted', sanitizeHex(value));
                break;
            case 'displayColorScoringPanelTextNoteL': // Matching your PHP typo
                document.documentElement.style.setProperty('--display-color-note-l', sanitizeHex(value));
                break;
            case 'displayColorScoringPanelTextNoteR': // Matching your PHP typo
                document.documentElement.style.setProperty('--display-color-note-r', sanitizeHex(value));
                break;
            case 'logo-normal':
                const jsImgLogo = document.getElementById('jsImgLogo');
                if(jsImgLogo) jsImgLogo.src = '/intern/img/logo-normal.png?' + Date.now();
                break;
        }
    }

    // --- Text Resizing Engine ---
    function isOverflown(parent, elem, heightscale, widthscale, paddingtext) {
        return (
            (elem.scrollWidth + paddingtext) > (parent.clientWidth / widthscale) ||
            (elem.scrollHeight + paddingtext) > (parent.clientHeight / heightscale)
        );
    }

    function fitTextElement(elem, { minSize = 10, maxSize = 1000, step = 1, unit = 'px' } = {}) {
        if (!elem) return;
        const parent = elem.parentElement;
        if (!parent) return;

        // FIXED: Declare variables properly so they don't leak into the global scope
        let heightscale = 1;
        let widthscale = 1;
        let paddingtext = 60;

        if (parent.classList.contains('row2text')) {
            heightscale = 2;
            paddingtext = 0;
        }
        if (parent.classList.contains('row3')) {
            widthscale = 2;
        }

        let size = minSize;
        elem.style.whiteSpace = 'nowrap';
        elem.style.fontSize = size + unit;

        while (size < maxSize && !isOverflown(parent, elem, heightscale, widthscale, paddingtext)) {
            size += step;
            elem.style.fontSize = size + unit;
        }

        elem.style.fontSize = (size - step) + unit;
    }

    function resizeCText() {
        ctext.style.fontSize = '';
        if (isOverflown(logobg, ctext, 1, 1, 0)) fitTextElement(ctext);
    }

    function fitTextAll() {
        const paragraphs = document.querySelectorAll('.pagediv p');
        paragraphs.forEach(p => fitTextElement(p));
        resizeCText();
    }

    window.addEventListener('resize', fitTextAll);

    // --- Initialize ---
    startWebSocket();

    async function morph() {
        gsap.to("#cross", {
            duration: 1,
            attr: { 
                d: "M-15,-60 L0,-30 M30,-90 L0,-30" 
            },
            stroke: "#0e0",
            ease: "power2.inOut"
        });

        gsap.to(".opacity0gsap", {
            duration: 1,
            opacity: "0",
            ease: "back.out(1.7)"
        });

        const pathStart = "M150 100 h200 m100 0 h200";

        const pathEnd   = "M150 100 h250 m0 0 h250"; 

        gsap.to("#errorLine", {
            duration: 1,
            attr: { d: pathEnd },
            ease: "power2.inOut"
        });

        gsap.to(".errortext", {
            duration: 1,
            textContent: "Verbindung Wiederhergestellt",
            ease: "back.out(1.7)"
        });

        gsap.to(".errortextSmall", {
            duration: 1,
            textContent: "Stelle auf Wettkampfbetrieb um...",
            ease: "back.out(1.7)"
        });

        setTimeout(() => {
            if (morphOk) {
                gsap.to('.errorws', {
                    opacity: 0,
                    duration: 0.5,
                    overwrite: "auto"
                });
            }
        }, 3000);

        setTimeout(() => {
            if (morphOk) {
                document.querySelector('.errorws').style.display = 'none';
            }
        }, 3800);
    }

    function restore() {
        // 1. Reset the Cross/Check path and color
        gsap.set("#cross", {
            attr: { d: "M-30,-30 L30,30 M30,-30 L-30,30" }, 
            stroke: "#f43f5e"
        });

        // 2. Reset opacity for elements with that class
        gsap.set(".opacity0gsap", {
            opacity: ".4"
        });

        // 3. Reset the Error Line path
        gsap.set("#errorLine", {
            attr: { d: "M150 100 h200 m100 0 h200" }
        });

        gsap.to(".errortext", {
            duration: 1,
            textContent: "Keine WebSocket Verbindung",
            ease: "back.out(1.7)"
        });

        document.querySelector(".errortextSmall").innerHTML = 'Versuche Verbindung... (Versuch <span id="counterTries"></span> / <span id="counterMaxTries"></span>)';

        counterTriesEl = document.getElementById('counterTries');
        counterMaxTriesEl = document.getElementById('counterMaxTries');

        if(counterTriesEl) counterTriesEl.innerHTML = filedConectCount;
        if(counterMaxTriesEl) counterMaxTriesEl.innerHTML = fallbackConectCount;
        
    }
 
  </script>
</body>
</html>