<!DOCTYPE html>
<html lang="en">
<head>
    <title>Multi-Console ({{ hostnames | length }} hosts)</title>
    <link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='images/dracs.svg') }}">
    <meta name="theme-color" content="#313131">
    <style>
        * { box-sizing: border-box; margin: 0; padding: 0; }

        html, body {
            height: 100%;
            background: #1a1a1a;
            color: #ccc;
            font-family: monospace;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        #mc-toolbar {
            background: #2d2d2d;
            border-bottom: 1px solid #444;
            padding: 5px 8px;
            display: flex;
            align-items: center;
            gap: 5px;
            flex-wrap: wrap;
            flex-shrink: 0;
            font-size: 12px;
        }

        .mc-label { color: #888; white-space: nowrap; }
        .mc-sep { color: #555; padding: 0 3px; }

        .mc-show-btn {
            padding: 2px 8px;
            background: #2d6a2d;
            color: #ccc;
            border: 1px solid #4a8a4a;
            cursor: pointer;
            font-family: monospace;
            font-size: 11px;
            white-space: nowrap;
        }
        .mc-show-btn:hover { background: #3a7a3a; }

        #mc-host-modal {
            position: fixed;
            inset: 0;
            z-index: 10000;
            background: rgba(0,0,0,0.6);
            display: none;
            align-items: center;
            justify-content: center;
        }
        #mc-host-panel {
            background: #2d2d2d;
            border: 1px solid #555;
            padding: 14px 16px;
            min-width: 280px;
            max-width: 520px;
            width: 90%;
            display: flex;
            flex-direction: column;
            gap: 10px;
        }
        .mc-modal-title {
            color: #bbb;
            font-size: 12px;
        }
        #mc-host-list {
            overflow-y: auto;
            max-height: 320px;
            border: 1px solid #444;
            background: #1a1a1a;
        }
        .mc-modal-item {
            padding: 5px 10px;
            color: #aaa;
            cursor: pointer;
            font-size: 12px;
            user-select: none;
            -webkit-user-select: none;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .mc-modal-item:hover { background: #252525; }
        .mc-modal-item.mc-modal-sel { background: #2d6a2d; color: #ddd; }
        .mc-modal-item.mc-modal-sel:hover { background: #3a7a3a; }
        .mc-modal-hint {
            color: #555;
            font-size: 10px;
        }
        .mc-modal-btns {
            display: flex;
            gap: 8px;
            justify-content: flex-end;
        }
        .mc-modal-btn {
            padding: 4px 14px;
            border: 1px solid #555;
            background: #333;
            color: #aaa;
            cursor: pointer;
            font-family: monospace;
            font-size: 12px;
        }
        .mc-modal-btn:hover { background: #444; color: #fff; }
        .mc-modal-btn.mc-modal-apply {
            background: #2d6a2d;
            border-color: #4a8a4a;
            color: #ddd;
        }
        .mc-modal-btn.mc-modal-apply:hover { background: #3a7a3a; }
        .mc-modal-btn.mc-modal-apply:disabled {
            background: #333;
            border-color: #444;
            color: #555;
            cursor: default;
        }

        .mc-col-btn {
            padding: 2px 8px;
            background: #333;
            color: #aaa;
            border: 1px solid #555;
            cursor: pointer;
            font-family: monospace;
            font-size: 12px;
        }
        .mc-col-btn:hover { background: #444; color: #fff; }
        .mc-col-btn.active { background: #555; color: #fff; border-color: #777; }

        #mc-grid-wrap {
            flex: 1;
            overflow: auto;
            padding: 4px;
            min-height: 0;
        }

        #mc-grid {
            display: grid;
            gap: 4px;
        }

        .mc-cell {
            display: flex;
            flex-direction: column;
            background: #111;
            border: 1px solid #444;
            overflow: hidden;
            resize: both;
            min-width: 220px;
            min-height: 180px;
        }

        .mc-cell-hdr {
            background: #313131;
            border-bottom: 1px solid #444;
            padding: 2px 6px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            flex-shrink: 0;
            font-size: 11px;
            color: #ccc;
            gap: 4px;
        }

        .mc-cell-hdr-title {
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            flex: 1;
        }

        .mc-max-btn, .mc-popout-btn, .mc-close-btn {
            flex-shrink: 0;
            background: none;
            border: 1px solid #555;
            cursor: pointer;
            padding: 1px 4px;
            display: flex;
            align-items: center;
        }
        .mc-max-btn img, .mc-popout-btn img, .mc-close-btn img { display: block; }
        .mc-max-btn:hover { background: #444; }
        .mc-popout-btn:hover:not(:disabled) { background: #444; }
        .mc-popout-btn:disabled { opacity: 0.3; cursor: default; }
        .mc-close-btn:hover { background: #5a2020; }

        .mc-cell-body {
            flex: 1;
            position: relative;
            min-height: 0;
        }

        .mc-cell-body iframe {
            position: absolute;
            inset: 0;
            width: 100%;
            height: 100%;
            border: none;
        }

        .mc-cell-msg {
            position: absolute;
            inset: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: 8px;
            font-size: 12px;
            color: #888;
            padding: 8px;
            text-align: center;
        }

        .mc-spinner {
            width: 24px;
            height: 24px;
            border: 2px solid #444;
            border-top-color: #aaa;
            border-radius: 50%;
            animation: spin 0.8s linear infinite;
            flex-shrink: 0;
        }

        .mc-error { color: #f66; }

        @keyframes spin { to { transform: rotate(360deg); } }
    </style>
</head>
<body>

<div id="mc-toolbar">
    <span class="mc-label">Showing</span>
    <button class="mc-show-btn" id="show-btn" onclick="openHostModal()">[ {{ hostnames|length }} / {{ hostnames|length }} ]</button>
    <span class="mc-label">hosts</span>

    <span class="mc-sep">|</span>
    <span class="mc-label">Cols:</span>
    <button class="mc-col-btn{% if hostnames|length == 1 %} active{% endif %}" data-cols="1" onclick="setCols(1)">1</button>
    <button class="mc-col-btn{% if hostnames|length == 2 %} active{% endif %}" data-cols="2" onclick="setCols(2)">2</button>
    <button class="mc-col-btn{% if hostnames|length in [3,4] %} active{% endif %}" data-cols="3" onclick="setCols(3)">3</button>
    <button class="mc-col-btn{% if hostnames|length in [5,6,7,8,9,10,11,12] %} active{% endif %}" data-cols="4" onclick="setCols(4)">4</button>
    <button class="mc-col-btn{% if hostnames|length > 12 %} active{% endif %}" data-cols="5" onclick="setCols(5)">5</button>
</div>

<div id="mc-grid-wrap">
    <div id="mc-grid">
        {% for hostname in hostnames %}
        <div class="mc-cell" id="cell-{{ loop.index0 }}" data-idx="{{ loop.index0 }}">
            <div class="mc-cell-hdr">
                <button class="mc-max-btn"
                        id="maxbtn-{{ loop.index0 }}"
                        onclick="toggleMaximize({{ loop.index0 }})"
                        title="Maximize">
                    <img src="{{ url_for('static', filename='images/expand.png') }}" alt="Expand" height="14">
                </button>
                <span class="mc-cell-hdr-title" title="{{ hostname }}">{{ hostname }}</span>
                <button class="mc-popout-btn"
                        id="popout-{{ loop.index0 }}"
                        onclick="popoutConsole({{ loop.index0 }})"
                        disabled
                        title="Open in separate popup window">
                    <img src="{{ url_for('static', filename='images/popout.png') }}" alt="Popout" height="14">
                </button>
                <button class="mc-close-btn"
                        onclick="closeConsole({{ loop.index0 }})"
                        title="Hide this console">
                    <img src="{{ url_for('static', filename='images/close.png') }}" alt="Close" height="14">
                </button>
            </div>
            <div class="mc-cell-body" id="body-{{ loop.index0 }}">
                <div class="mc-cell-msg" id="msg-{{ loop.index0 }}">
                    <div class="mc-spinner"></div>
                    <div>Connecting...</div>
                </div>
            </div>
        </div>
        {% endfor %}
    </div>
</div>

<script>
    var mcHosts = {{ hostnames | tojson }};
    var mcTokens = new Array(mcHosts.length).fill(null);
    var mcVisible = new Set(mcHosts.map(function(_, i) { return i; }));
    var mcCols = 2;
    var mcExpandSrc = "{{ url_for('static', filename='images/expand.png') }}";
    var mcUnexpandSrc = "{{ url_for('static', filename='images/unexpand.png') }}";

    (function initCols() {
        var n = mcHosts.length;
        if (n === 1) mcCols = 1;
        else if (n === 2) mcCols = 2;
        else if (n <= 4) mcCols = 3;
        else if (n <= 12) mcCols = 4;
        else mcCols = 5;
    })();

    function escapeHtml(str) {
        var d = document.createElement('div');
        d.appendChild(document.createTextNode(str));
        return d.innerHTML;
    }

    function shortHost(fqdn) {
        return fqdn.split('.')[0];
    }

    function computeCellHeight() {
        var toolbar = document.getElementById('mc-toolbar');
        var wrap = document.getElementById('mc-grid-wrap');
        var wrapH = wrap.clientHeight;
        var rows = Math.ceil(mcVisible.size / mcCols);
        var gapTotal = (rows - 1) * 4 + 8;
        return Math.max(180, Math.floor((wrapH - gapTotal) / rows));
    }

    function updateGrid() {
        var grid = document.getElementById('mc-grid');
        grid.style.gridTemplateColumns = 'repeat(' + mcCols + ', 1fr)';

        mcHosts.forEach(function(_, idx) {
            var cell = document.getElementById('cell-' + idx);
            var hidden = !mcVisible.has(idx);
            cell.style.display = hidden ? 'none' : 'flex';
        });

        var h = computeCellHeight();
        document.querySelectorAll('.mc-cell').forEach(function(cell) {
            if (cell.style.display !== 'none' && !cell.dataset.userResized) {
                cell.style.height = h + 'px';
            }
        });
    }

    function setCols(n) {
        mcCols = n;
        document.querySelectorAll('.mc-col-btn').forEach(function(btn) {
            btn.classList.toggle('active', parseInt(btn.dataset.cols) === n);
        });
        document.querySelectorAll('.mc-cell').forEach(function(cell) {
            delete cell.dataset.userResized;
            cell.style.width = '';
            cell.style.height = '';
        });
        updateGrid();
    }

    var mcModalSelected = new Set();
    var mcModalAnchor = -1;

    function updateShowBtn() {
        var btn = document.getElementById('show-btn');
        btn.textContent = '[ ' + mcVisible.size + ' / ' + mcHosts.length + ' ]';
    }

    function openHostModal() {
        mcModalSelected = new Set(mcVisible);
        mcModalAnchor = -1;
        var list = document.getElementById('mc-host-list');
        list.innerHTML = '';
        mcHosts.forEach(function(hostname, idx) {
            var item = document.createElement('div');
            item.className = 'mc-modal-item' + (mcModalSelected.has(idx) ? ' mc-modal-sel' : '');
            item.textContent = hostname;
            item.dataset.idx = String(idx);
            item.addEventListener('click', function(e) { modalItemClick(idx, e); });
            list.appendChild(item);
        });
        refreshModalApply();
        document.getElementById('mc-host-modal').style.display = 'flex';
    }

    function modalItemClick(idx, e) {
        if (e.shiftKey && mcModalAnchor !== -1) {
            var lo = Math.min(mcModalAnchor, idx);
            var hi = Math.max(mcModalAnchor, idx);
            for (var i = lo; i <= hi; i++) mcModalSelected.add(i);
        } else if (e.ctrlKey || e.metaKey) {
            if (mcModalSelected.has(idx)) mcModalSelected.delete(idx);
            else mcModalSelected.add(idx);
            mcModalAnchor = idx;
        } else {
            mcModalSelected = new Set([idx]);
            mcModalAnchor = idx;
        }
        document.querySelectorAll('.mc-modal-item').forEach(function(el) {
            el.classList.toggle('mc-modal-sel', mcModalSelected.has(parseInt(el.dataset.idx)));
        });
        refreshModalApply();
    }

    function refreshModalApply() {
        document.getElementById('mc-modal-apply-btn').disabled = mcModalSelected.size === 0;
    }

    function applyHostModal() {
        if (mcModalSelected.size === 0) return;
        mcVisible = new Set(mcModalSelected);
        updateShowBtn();
        document.getElementById('mc-host-modal').style.display = 'none';
        updateGrid();
    }

    function cancelHostModal() {
        document.getElementById('mc-host-modal').style.display = 'none';
    }

    function closeConsole(idx) {
        mcVisible.delete(idx);
        updateShowBtn();
        updateGrid();
    }

    var maximizedIdx = -1;

    function toggleMaximize(idx) {
        if (maximizedIdx === idx) {
            restoreCell(idx);
        } else {
            if (maximizedIdx !== -1) {
                restoreCell(maximizedIdx);
            }
            maximizeCell(idx);
        }
    }

    function maximizeCell(idx) {
        var toolbarH = document.getElementById('mc-toolbar').offsetHeight;
        var cell = document.getElementById('cell-' + idx);
        cell.style.position = 'fixed';
        cell.style.top = toolbarH + 'px';
        cell.style.left = '0';
        cell.style.right = '0';
        cell.style.bottom = '0';
        cell.style.width = 'auto';
        cell.style.height = 'auto';
        cell.style.zIndex = '9999';
        cell.style.resize = 'none';
        document.getElementById('maxbtn-' + idx).querySelector('img').src = mcUnexpandSrc;
        document.getElementById('maxbtn-' + idx).title = 'Restore';
        maximizedIdx = idx;
    }

    function restoreCell(idx) {
        var cell = document.getElementById('cell-' + idx);
        cell.style.position = '';
        cell.style.top = '';
        cell.style.left = '';
        cell.style.right = '';
        cell.style.bottom = '';
        cell.style.width = '';
        cell.style.height = '';
        cell.style.zIndex = '';
        cell.style.resize = '';
        delete cell.dataset.userResized;
        document.getElementById('maxbtn-' + idx).querySelector('img').src = mcExpandSrc;
        document.getElementById('maxbtn-' + idx).title = 'Maximize';
        maximizedIdx = -1;
        updateGrid();
    }

    function popoutConsole(idx) {
        var token = mcTokens[idx];
        if (!token) return;
        // Share the same token so both windows count as one iDRAC VNC session.
        // The ?shared=1 flag tells console.html to register an addref on load
        // and release it (decrement) on close, leaving this window's reference intact.
        window.open(
            '/console/' + encodeURIComponent(token) + '?shared=1',
            '_blank',
            'width=1024,height=768,menubar=no,toolbar=no,location=no,status=no,scrollbars=no'
        );
    }

    function showConsole(idx, token) {
        var body = document.getElementById('body-' + idx);
        var msg = document.getElementById('msg-' + idx);
        msg.style.display = 'none';
        var iframe = document.createElement('iframe');
        iframe.src = '/console/' + encodeURIComponent(token);
        body.appendChild(iframe);
        document.getElementById('popout-' + idx).disabled = false;
    }

    function showError(idx, errMsg) {
        var msg = document.getElementById('msg-' + idx);
        msg.classList.add('mc-error');
        msg.innerHTML = '<div>' + escapeHtml(errMsg) + '</div>';
    }

    function createSession(hostname, idx) {
        return fetch('/api/vnc-session', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({hostname: hostname})
        })
        .then(function(resp) { return resp.json(); })
        .then(function(data) {
            if (data.success) {
                mcTokens[idx] = data.token;
                showConsole(idx, data.token);
            } else {
                showError(idx, data.message || 'Session creation failed');
            }
        })
        .catch(function(e) {
            showError(idx, e.message || 'Network error');
        });
    }

    function createAllSessions() {
        return Promise.all(mcHosts.map(function(hostname, idx) {
            return createSession(hostname, idx);
        }));
    }

    // Install ResizeObserver after the initial layout settles so that only
    // user-initiated drags set the userResized flag, not programmatic sizing.
    setTimeout(function() {
        if (!window.ResizeObserver) return;
        var obs = new ResizeObserver(function(entries) {
            entries.forEach(function(entry) {
                entry.target.dataset.userResized = '1';
            });
        });
        document.querySelectorAll('.mc-cell').forEach(function(cell) {
            obs.observe(cell);
        });
    }, 200);

    setInterval(function() {
        mcTokens.forEach(function(token) {
            if (token) {
                fetch('/api/vnc-session/' + encodeURIComponent(token), {method: 'PATCH'});
            }
        });
    }, 5 * 60 * 1000);

    window.addEventListener('beforeunload', function() {
        mcTokens.forEach(function(token) {
            if (token) {
                fetch('/api/vnc-session/' + encodeURIComponent(token), {
                    method: 'DELETE',
                    keepalive: true
                });
            }
        });
    });

    updateGrid();
    updateShowBtn();
    createAllSessions();
    document.getElementById('mc-host-modal').addEventListener('click', function(e) {
        if (e.target === this) cancelHostModal();
    });
    document.addEventListener('keydown', function(e) {
        if (e.key === 'Escape') cancelHostModal();
    });
</script>

<div id="mc-host-modal">
    <div id="mc-host-panel">
        <div class="mc-modal-title">Select hosts to display:</div>
        <div id="mc-host-list"></div>
        <div class="mc-modal-hint">Click to select &nbsp; Ctrl+click to toggle &nbsp; Shift+click to extend</div>
        <div class="mc-modal-btns">
            <button class="mc-modal-btn" onclick="cancelHostModal()">Cancel</button>
            <button class="mc-modal-btn mc-modal-apply" id="mc-modal-apply-btn" onclick="applyHostModal()">Apply</button>
        </div>
    </div>
</div>
</body>
</html>
