/**
 * Main App
 * Orchestrates WebSocket, Tiles, Controls
 */
(function () {

    /* Icon paths for img src swaps */
    var ICONS = {
        enable: 'assets/icons/Enable_32_grey.svg',
        reset: 'assets/icons/Zuruecksetzen_32_grey.svg',
        start: 'assets/icons/Play_32_grey.svg',
        pause: 'assets/icons/Pause_32_grey.svg'
    };

    function init() {
        console.log('[App] Initializing - MOCK_MODE:', Config.MOCK_MODE);

        Router.init();

        Tiles.load(function () {
            Controls.init();
            Jogging.init();
            Controls.loadProgramList();

            if (!Config.MOCK_MODE) {
                setInterval(Controls.loadProgramList, 30000);
            }

            RobotWebSocket.onMessage(onStateUpdate);
            RobotWebSocket.onStatusChange(onConnectionChange);
            RobotWebSocket.connect();
        });

        var demoToggle = document.getElementById('demo-toggle');
        if (demoToggle) {
            demoToggle.style.display = Config.MOCK_MODE ? 'flex' : 'none';
            if (Config.MOCK_MODE) bindDemoToggle();
        }

        updateClock();
        setInterval(updateClock, 1000);

        // Safety reveal: if no state arrives, still show button content
        // at the HTML defaults rather than leaving them invisible forever.
        setTimeout(function () {
            var btns = document.querySelectorAll('.ctrl-btn');
            for (var i = 0; i < btns.length; i++) btns[i].classList.add('initialized');
        }, 500);

        // Bottom sheet toggle + drag (mobile)
        BottomSheet.init();

        // Error banner click → detail dialog
        bindErrorDialog();

        // 5 rapid taps on logo → reload page
        var logoTaps = 0;
        var logoTimer = null;
        var logo = document.querySelector('.igus-logo');
        if (logo) {
            logo.addEventListener('click', function () {
                logoTaps++;
                clearTimeout(logoTimer);
                if (logoTaps >= 5) {
                    window.location.reload();
                }
                logoTimer = setTimeout(function () { logoTaps = 0; }, 1000);
            });
        }
    }

    /**
     * Derives boolean flags from raw state once per tick.
     * Avoids recomputing the same checks in multiple functions.
     */
    function deriveState(state) {
        var robot = state.robot || {};
        var mp = (state.motion && state.motion.motionProgram) || {};

        return {
            isEnabled: !!robot.isEnabled,
            isReferenced: robot.referencingState === 'referenced',
            isReferencing: robot.referencingState === 'referencing...',
            hasError: !!robot.hardwareState && robot.hardwareState !== 'NoError',
            hasProgram: !!mp.currentProgram,
            isRunning: mp.runState === 'running',
            isPaused: mp.runState === 'paused'
        };
    }

    function onStateUpdate(state) {
        updateErrorBanner(state);
        updateLicenseOverlay(state);

        if (!state.robot) return;

        var ds = deriveState(state);

        Controls.updateFromState(state);
        Jogging.updateFromState(state);
        Info.update(state);
        Controls.updateVelocityFromState(state.robot.velocityOverride);
        updateProgramStatus(state);
        Tiles.update(state);
        updateControlCards(state, ds);
        updateCardStates(ds);
        updateHighlight(ds);
        updateProgramSelectDisabled(ds);
    }

    var _currentError = null;

    function updateErrorBanner(state) {
        var banner = document.getElementById("error-banner");
        if (!banner || connectionLost) return;

        var robot = state.robot || {};
        var detail = null;

        if (state.grpcConnected === false) {
            detail = { code: "", title: "Robot not connected", description: "" };
        } else if (robot.hardwareState && robot.hardwareState !== "NoError") {
            detail = {
                code: robot.hardwareState,
                title: robot.errorTitle || robot.hardwareState,
                description: robot.errorDescription || ""
            };
        }

        if (detail) {
            banner.innerHTML = '';
            var icon = document.createElement("span");
            icon.className = "error-icon";
            icon.innerHTML = '<img src="assets/icons/alert-warning.svg" alt="">';
            var title = document.createElement("span");
            title.className = "error-title";
            title.textContent = detail.title;
            banner.appendChild(icon);
            banner.appendChild(title);
            banner.classList.remove('hidden');
            _currentError = detail;
            if (ErrorDialog.isOpen()) ErrorDialog.refresh(detail);
        } else {
            banner.innerHTML = '';
            banner.classList.add('hidden');
            _currentError = null;
            ErrorDialog.close();
        }
    }

    /**
     * Full-screen lock while the app has neither a license nor trial time.
     * Absent state.license (before the backend's first successful license
     * check) means unlocked; the overlay clears itself as soon as a valid
     * license is reported again.
     */
    function updateLicenseOverlay(state) {
        var overlay = document.getElementById('license-overlay');
        if (!overlay) return;
        var locked = !!(state.license && state.license.valid === false);
        overlay.classList.toggle('hidden', !locked);
    }

    var ErrorDialog = (function () {
        var DESC_FALLBACK = "No additional information is available for this error code.";
        var open = false;

        function set(detail) {
            var codeEl = document.getElementById("error-dialog-code");
            var titleEl = document.getElementById("error-dialog-title");
            var descEl = document.getElementById("error-dialog-description");
            if (!codeEl || !titleEl || !descEl) return;
            codeEl.textContent = detail.code || "Error";
            codeEl.style.display = detail.code ? "" : "none";
            titleEl.textContent = detail.title || "";
            descEl.textContent = detail.description || DESC_FALLBACK;
        }

        function show(detail) {
            var backdrop = document.getElementById("error-dialog-backdrop");
            var dialog = document.getElementById("error-dialog");
            if (!backdrop || !dialog) return;
            set(detail);
            backdrop.classList.remove("hidden");
            dialog.classList.remove("hidden");
            open = true;
        }

        function hide() {
            var backdrop = document.getElementById("error-dialog-backdrop");
            var dialog = document.getElementById("error-dialog");
            if (!backdrop || !dialog) return;
            backdrop.classList.add("hidden");
            dialog.classList.add("hidden");
            open = false;
        }

        return {
            open: show,
            close: hide,
            refresh: function (detail) { if (open) set(detail); },
            isOpen: function () { return open; }
        };
    })();

    function bindErrorDialog() {
        var banner = document.getElementById("error-banner");
        if (banner) {
            banner.addEventListener("click", function () {
                if (banner.classList.contains("hidden")) return;
                if (_currentError) ErrorDialog.open(_currentError);
            });
        }
        var backdrop = document.getElementById("error-dialog-backdrop");
        if (backdrop) backdrop.addEventListener("click", ErrorDialog.close);
        var closeBtn = document.getElementById("error-dialog-close");
        if (closeBtn) closeBtn.addEventListener("click", ErrorDialog.close);
        document.addEventListener("keydown", function (e) {
            if (e.key === "Escape" && ErrorDialog.isOpen()) ErrorDialog.close();
        });
    }

    function updateProgramStatus(state) {
        if (!state.motion) return;
        var mp = state.motion.motionProgram;
        var el = document.getElementById("program-status");
        if (!el) return;

        if (!mp.currentProgram) {
            el.textContent = "Stopped";
            el.className = "program-status stopped";
        } else if (mp.runState === "running") {
            el.textContent = "Running (" + (mp.currentCommandIndex + 1) + "/" + mp.commandCount + ")";
            el.className = "program-status running";
        } else if (mp.runState === "paused") {
            el.textContent = "Paused";
            el.className = "program-status paused";
        } else {
            el.textContent = "Stopped";
            el.className = "program-status stopped";
        }

        Controls.syncProgramSelect();
    }

    function updateControlCards(state, ds) {
        // Enable/Reset Toggle
        var enableIcon = document.getElementById('icon-enable');
        var enableLabel = document.getElementById('label-enable');
        if (enableLabel) {
            enableLabel.textContent = ds.isEnabled ? 'Reset' : 'Enable';
        }
        if (enableIcon) {
            enableIcon.src = ds.isEnabled ? ICONS.reset : ICONS.enable;
        }

        // Start/Pause Toggle
        var startPauseIcon = document.getElementById('icon-start-pause');
        var startPauseLabel = document.getElementById('label-start-pause');
        if (startPauseLabel) {
            if (ds.isRunning) {
                startPauseLabel.textContent = 'Pause';
            } else if (ds.isPaused) {
                startPauseLabel.textContent = 'Resume';
            } else {
                startPauseLabel.textContent = 'Start';
            }
        }
        if (startPauseIcon) {
            startPauseIcon.src = ds.isRunning ? ICONS.pause : ICONS.start;
        }

        // Reveal button content — first real state has landed, no more flicker.
        var btns = document.querySelectorAll('.ctrl-btn');
        for (var i = 0; i < btns.length; i++) btns[i].classList.add('initialized');
    }

    function updateCardStates(ds) {
        setDisabled('btn-enable', false);
        setDisabled('panel-reference', !ds.isEnabled || ds.isReferenced || ds.isReferencing || ds.hasError);

        if (ds.hasError) {
            // An error (e.g. MNE after pressing Reset mid-run) must always
            // disable Start/Pause, even if the program still reports
            // running/paused for a moment.
            setDisabled('btn-start-pause', true);
        } else if (ds.isRunning || ds.isPaused) {
            setDisabled('btn-start-pause', false);
        } else {
            setDisabled('btn-start-pause', !ds.isEnabled || !ds.isReferenced || !ds.hasProgram);
        }

        setDisabled('btn-stop', !ds.isRunning && !ds.isPaused);
    }

    function updateHighlight(ds) {
        var allBtns = document.querySelectorAll('.ctrl-btn, .panel-card.ref-panel');
        for (var i = 0; i < allBtns.length; i++) {
            allBtns[i].classList.remove('highlight', 'active');
        }

        if (ds.hasError) {
            highlight('btn-enable');
        } else if (!ds.isEnabled) {
            highlight('btn-enable');
        } else if (!ds.isReferenced) {
            highlight('panel-reference');
        } else if (ds.isRunning) {
            highlight('btn-start-pause', true);
        } else if (ds.isPaused) {
            highlight('btn-start-pause');
        } else {
            highlight('btn-start-pause');
        }
    }

    function highlight(id, active) {
        var el = document.getElementById(id);
        if (!el) return;
        el.classList.add('highlight');
        if (active) el.classList.add('active');
    }

    function updateProgramSelectDisabled(ds) {
        ProgramDropdown.setDisabled(ds.isRunning);
    }

    function setDisabled(id, disabled) {
        var el = document.getElementById(id);
        if (!el) return;
        if (disabled) {
            el.classList.add('disabled');
        } else {
            el.classList.remove('disabled');
        }
    }

    function bindDemoToggle() {
        var toggle = document.getElementById('demo-toggle');
        var demoOk = true;

        if (toggle) {
            toggle.addEventListener('click', function () {
                demoOk = !demoOk;
                document.getElementById('toggle-track').className = 'toggle-track' + (demoOk ? ' ok' : '');
                document.getElementById('toggle-thumb').className = 'toggle-thumb' + (demoOk ? ' ok' : '');
                document.getElementById('demo-label-err').className = 'demo-label' + (demoOk ? '' : ' active-label');
                document.getElementById('demo-label-ok').className = 'demo-label' + (demoOk ? ' active-label' : '');
                MockWebSocket.setScenario(demoOk ? 'normal' : 'error');
            });
        }
    }

    var connectionLost = false;

    function onConnectionChange(connected) {
        connectionLost = !connected;
        var banner = document.getElementById("error-banner");
        if (!banner) return;

        if (!connected) {
            banner.innerHTML = '';
            var icon = document.createElement("span");
            icon.className = "error-icon";
            icon.innerHTML = '<img src="assets/icons/alert-warning.svg" alt="">';
            var title = document.createElement("span");
            title.className = "error-title";
            title.textContent = "Connection lost";
            banner.appendChild(icon);
            banner.appendChild(title);
            banner.classList.remove('hidden');
        } else {
            banner.innerHTML = '';
            banner.classList.add('hidden');
        }
    }

    function updateClock() {
        var now = new Date();
        var day = String(now.getDate()).padStart(2, '0');
        var month = String(now.getMonth() + 1).padStart(2, '0');
        var year = now.getFullYear();
        var hours = String(now.getHours()).padStart(2, '0');
        var minutes = String(now.getMinutes()).padStart(2, '0');

        var clockEl = document.getElementById('clock');
        if (clockEl) {
            clockEl.innerHTML = day + '.' + month + '.' + year + '&nbsp;&nbsp;' + hours + ':' + minutes;
        }
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
