{% extends "base.html" %}

{% block title %}Edit: {{ art.name }}{% endblock %}

{% block extra_css %}
<style>
.ansi-editor { display:grid; grid-template-columns:1fr 280px; gap:8px; }
@media (max-width:1100px) { .ansi-editor { grid-template-columns:1fr; } }

.ansi-grid-wrap { background:#000; padding:6px; overflow:auto; border:1px solid #333; }
.ansi-grid {
    display: grid;
    gap: 0;
    width: max-content;        /* shrink-wrap to columns × cell width */
    user-select: none;
    cursor: crosshair;
    font-family: 'Courier New', 'DejaVu Sans Mono', monospace;
}
.ansi-grid .cell {
    width: var(--cell-w, 10px);
    height: var(--cell-h, 18px);
    box-sizing: border-box;
    text-align: center;
    line-height: var(--cell-h, 18px);
    font-size: var(--cell-font, 14px);
    overflow: hidden;
    padding: 0;
    margin: 0;
}
.ansi-grid .cell.cur {
    outline: 1px solid #ff0;
    outline-offset: -1px;
    z-index: 1;
}
.ansi-grid .cell.sel {
    box-shadow: inset 0 0 0 1px #0ff;
}

.ansi-tools { background:var(--theme-bg-dark, #111); padding:8px; border:1px solid #333; }
.ansi-tools h6 { color:var(--theme-primary,#0f0); font-size:0.85rem; margin:6px 0 4px; }

.swatches { display:grid; grid-template-columns:repeat(8, 1fr); gap:2px; }
.swatch { width:100%; height:22px; cursor:pointer; border:1px solid #333; box-sizing:border-box; }
.swatch.active { outline:2px solid #ff0; outline-offset:-1px; }

.charset { display:grid; grid-template-columns:repeat(16, 1fr); gap:1px; max-height:200px; overflow-y:auto;
           background:#000; padding:2px; }
.charset .ch {
    text-align: center; cursor: pointer;
    color:#ccc; background:#000;
    font-family: 'Courier New', monospace;
    font-size: 14px; line-height: 18px;
    width: 18px; height: 18px;
}
.charset .ch:hover, .charset .ch.active { background:#333; color:#fff; }

.tool-btn { width:36px; height:36px; margin:2px; }
.tool-btn.active { background:#ff0; color:#000; }
</style>
{% endblock %}

{% block content %}
<div class="bbs-header">
    <h1><i class="bi bi-easel-fill"></i> {{ art.name }}</h1>
    <p>
        <code>{{ art.slug }}</code> — {{ art.width }}×{{ art.height }} —
        <span id="saveStatus" class="text-muted small">unsaved</span>
    </p>
</div>

<div class="mb-2 d-flex flex-wrap gap-1 align-items-center">
    <button id="btnSave" class="btn btn-primary btn-sm"><i class="bi bi-save"></i> Save</button>
    <button id="btnUndo" class="btn btn-outline-light btn-sm" title="Undo (Ctrl+Z)"><i class="bi bi-arrow-counterclockwise"></i></button>
    <button id="btnRedo" class="btn btn-outline-light btn-sm" title="Redo (Ctrl+Shift+Z)"><i class="bi bi-arrow-clockwise"></i></button>
    <button id="btnClear" class="btn btn-outline-warning btn-sm" title="Clear canvas"><i class="bi bi-trash"></i> Clear</button>
    <button id="btnPreview" class="btn btn-outline-info btn-sm" title="Preview as terminal would render"><i class="bi bi-eye"></i> Preview</button>
    <a href="{{ url_for('ansi_editor.raw_ansi', art_id=art.id) }}" class="btn btn-outline-secondary btn-sm">
        <i class="bi bi-download"></i> .ans
    </a>
    <form method="post" action="{{ url_for('ansi_editor.duplicate', art_id=art.id) }}" style="display:inline;">
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
        <button class="btn btn-outline-secondary btn-sm"><i class="bi bi-files"></i> Duplicate</button>
    </form>
    <a href="{{ url_for('ansi_editor.index') }}" class="btn btn-link btn-sm">← Library</a>
    <span class="ms-3 text-muted small" id="cursorPos">row 0 col 0</span>
</div>

<div class="ansi-editor">
    <div class="ansi-grid-wrap">
        <div id="grid" class="ansi-grid"></div>
    </div>
    <div class="ansi-tools">
        <h6>TOOLS</h6>
        <button class="btn btn-sm btn-outline-light tool-btn active" data-tool="pencil" title="Pencil"><i class="bi bi-pencil"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" data-tool="eraser" title="Eraser"><i class="bi bi-eraser"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" data-tool="line" title="Line"><i class="bi bi-slash-lg"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" data-tool="rect" title="Rectangle"><i class="bi bi-square"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" data-tool="fill" title="Fill"><i class="bi bi-paint-bucket"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" data-tool="text" title="Text typing"><i class="bi bi-fonts"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" data-tool="select" title="Select rectangle"><i class="bi bi-bounding-box"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" id="btnCopy" title="Copy selection (Ctrl+C)"><i class="bi bi-clipboard"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" id="btnPaste" title="Paste (Ctrl+V)"><i class="bi bi-clipboard-check"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" id="btnFlipH" title="Flip selection horizontally"><i class="bi bi-symmetry-vertical"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" id="btnFlipV" title="Flip selection vertically"><i class="bi bi-symmetry-horizontal"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" data-tool="eyedrop" title="Eyedropper (click to grab a cell's char + colors)"><i class="bi bi-droplet"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" id="btnZoomIn" title="Zoom in"><i class="bi bi-zoom-in"></i></button>
        <button class="btn btn-sm btn-outline-light tool-btn" id="btnZoomOut" title="Zoom out"><i class="bi bi-zoom-out"></i></button>

        <h6 class="mt-3">BRUSH SIZE</h6>
        <div class="d-flex gap-1">
            <button class="btn btn-sm btn-outline-light tool-btn brush-btn active" data-brush="1">1×1</button>
            <button class="btn btn-sm btn-outline-light tool-btn brush-btn" data-brush="2">2×2</button>
            <button class="btn btn-sm btn-outline-light tool-btn brush-btn" data-brush="3">3×3</button>
        </div>

        <h6 class="mt-3">FOREGROUND</h6>
        <div class="swatches" id="fgSwatches"></div>
        <h6 class="mt-2">BACKGROUND</h6>
        <div class="swatches" id="bgSwatches" style="grid-template-columns:repeat(8,1fr);"></div>

        <h6 class="mt-3">SELECTED CHAR</h6>
        <div class="d-flex align-items-center gap-2">
            <div id="curCharPreview" style="font-family:monospace;font-size:24px;width:32px;height:32px;background:#000;color:#fff;text-align:center;line-height:32px;border:1px solid #333;">█</div>
            <small class="text-muted">code <span id="curCharCode">219</span></small>
        </div>

        <h6 class="mt-3">CHARACTER PALETTE</h6>
        <div class="charset" id="charset"></div>

        <h6 class="mt-3">QUICK GLYPHS</h6>
        <div>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="█">█</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="▓">▓</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="▒">▒</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="░">░</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="▀">▀</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="▄">▄</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="▌">▌</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="▐">▐</button>
        </div>
        <div class="mt-1">
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="┌">┌</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="┐">┐</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="└">└</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="┘">┘</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="─">─</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="│">│</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="┼">┼</button>
        </div>
        <div class="mt-1">
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="╔">╔</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="╗">╗</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="╚">╚</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="╝">╝</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="═">═</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="║">║</button>
            <button class="btn btn-sm btn-outline-light tool-btn" data-quickch="╬">╬</button>
        </div>
    </div>
</div>

<div class="card mt-3">
    <div class="card-header"><i class="bi bi-arrow-right-square"></i> Apply to a BBS Menu</div>
    <div class="card-body">
        <p class="small text-muted mb-2">Pick a menu and click Apply — this art
            replaces the auto-rendered screen for that menu. Save your work
            first; the rendered ANSI from the last save is what gets applied.</p>
        <form method="post" action="{{ url_for('ansi_editor.apply_to_menu') }}"
              class="row g-2 align-items-end">
            <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
            <input type="hidden" name="art_id" value="{{ art.id }}">
            <div class="col-md-8">
                <label class="form-label">Target Menu</label>
                <select name="menu_id" class="form-select" required>
                    {% for m in menus %}
                        <option value="{{ m.id }}">
                            {{ m.name }} — {{ m.title }}
                            {% if m.is_default %}(default){% endif %}
                        </option>
                    {% endfor %}
                </select>
            </div>
            <div class="col-md-4">
                <button class="btn btn-success w-100"
                        onclick="return confirm('Apply this art as the ANSI screen for the selected menu?')">
                    <i class="bi bi-check2-circle"></i> Apply
                </button>
            </div>
        </form>
    </div>
</div>

<script>
(function() {
    const initial = {{ grid_json | safe }};
    const W = initial.width;
    const H = initial.height;
    let cells = initial.cells.map(c => ({c: c.c || ' ', fg: c.fg|0, bg: c.bg|0}));

    // 16-color CSS palette (mIRC-like)
    const palette = [
        '#ffffff','#000000','#00007f','#009300',
        '#ff0000','#7f0000','#9c009c','#fc7f00',
        '#ffff00','#00fc00','#009393','#00ffff',
        '#0000fc','#ff00ff','#7f7f7f','#d2d2d2'
    ];

    // Editor state
    let curFg = 7;          // light grey
    let curBg = 0;          // black
    let curChar = '█';
    let tool = 'pencil';
    let cursorR = 0, cursorC = 0;
    let dirty = false;
    let mouseDown = false;
    let lineStart = null;     // for line/rect tools
    let selStart = null;      // [r,c] when selection drag begins
    let selRect  = null;      // {r0,c0,r1,c1} after drag completes
    let clipboard = null;     // 2D array of cells
    let brushSize = 1;        // 1×1, 2×2, 3×3

    const grid = document.getElementById('grid');
    const cursorPos = document.getElementById('cursorPos');

    // Flat grid: cells go directly into #grid via CSS Grid.
    // Track them in a flat array for O(1) lookup by index.
    let cellNodes = [];

    function buildGrid() {
        grid.innerHTML = '';
        grid.style.gridTemplateColumns = `repeat(${W}, 10px)`;
        grid.style.gridTemplateRows    = `repeat(${H}, 18px)`;
        cellNodes = new Array(W * H);
        for (let i = 0; i < W * H; i++) {
            const span = document.createElement('span');
            span.className = 'cell';
            const r = Math.floor(i / W);
            const c = i % W;
            span.dataset.r = r;
            span.dataset.c = c;
            grid.appendChild(span);
            cellNodes[i] = span;
        }
        for (let i = 0; i < cells.length; i++) drawCell(i);
        updateCursorMark();
    }

    function drawCell(i) {
        const node = cellNodes[i];
        if (!node) return;
        const c = cells[i];
        node.textContent = c.c;
        node.style.color = palette[c.fg & 0x0F];
        node.style.background = palette[c.bg & 0x07];
    }

    function updateCursorMark() {
        document.querySelectorAll('.cell.cur').forEach(el => el.classList.remove('cur'));
        const idx = cursorR * W + cursorC;
        const node = cellNodes[idx];
        if (node) node.classList.add('cur');
        cursorPos.textContent = `row ${cursorR} col ${cursorC}`;
    }

    function paint(r, c, ch) {
        // Apply across the brush footprint
        const off = Math.floor(brushSize / 2);
        for (let dr = 0; dr < brushSize; dr++) {
            for (let dc = 0; dc < brushSize; dc++) {
                const tr = r + dr - off;
                const tc = c + dc - off;
                if (tr < 0 || tr >= H || tc < 0 || tc >= W) continue;
                const i = tr * W + tc;
                const cell = cells[i];
                const newCh = ch === undefined ? curChar : ch;
                if (cell.c === newCh && cell.fg === curFg && cell.bg === curBg) continue;
                cell.c = newCh; cell.fg = curFg; cell.bg = curBg;
                drawCell(i);
            }
        }
        dirty = true;
        document.getElementById('saveStatus').textContent = 'unsaved';
    }

    function erase(r, c) {
        if (r < 0 || r >= H || c < 0 || c >= W) return;
        const i = r * W + c;
        cells[i] = {c: ' ', fg: 7, bg: 0};
        drawCell(i);
        dirty = true;
    }

    function drawLine(r0, c0, r1, c1, ch) {
        // Bresenham
        const dx = Math.abs(c1 - c0);
        const dy = Math.abs(r1 - r0);
        const sx = c0 < c1 ? 1 : -1;
        const sy = r0 < r1 ? 1 : -1;
        let err = dx - dy;
        let r = r0, c = c0;
        while (true) {
            paint(r, c, ch);
            if (r === r1 && c === c1) break;
            const e2 = 2 * err;
            if (e2 > -dy) { err -= dy; c += sx; }
            if (e2 < dx)  { err += dx; r += sy; }
        }
    }

    function drawRect(r0, c0, r1, c1) {
        const top = Math.min(r0, r1), bot = Math.max(r0, r1);
        const lft = Math.min(c0, c1), rgt = Math.max(c0, c1);
        for (let c = lft; c <= rgt; c++) {
            paint(top, c); paint(bot, c);
        }
        for (let r = top; r <= bot; r++) {
            paint(r, lft); paint(r, rgt);
        }
    }

    function clearSelection() {
        selRect = null;
        document.querySelectorAll('.cell.sel').forEach(el => el.classList.remove('sel'));
    }

    function highlightSelection() {
        document.querySelectorAll('.cell.sel').forEach(el => el.classList.remove('sel'));
        if (!selRect) return;
        for (let r = selRect.r0; r <= selRect.r1; r++) {
            for (let c = selRect.c0; c <= selRect.c1; c++) {
                const node = cellNodes[r * W + c];
                if (node) node.classList.add('sel');
            }
        }
    }

    function copySelection() {
        if (!selRect) return;
        const w = selRect.c1 - selRect.c0 + 1;
        const h = selRect.r1 - selRect.r0 + 1;
        const data = [];
        for (let r = 0; r < h; r++) {
            for (let c = 0; c < w; c++) {
                const cell = cells[(selRect.r0 + r) * W + (selRect.c0 + c)];
                data.push({ c: cell.c, fg: cell.fg, bg: cell.bg });
            }
        }
        clipboard = { width: w, height: h, cells: data };
    }

    function pasteAt(rTop, cLeft) {
        if (!clipboard) return;
        pushHistory();
        for (let r = 0; r < clipboard.height; r++) {
            for (let c = 0; c < clipboard.width; c++) {
                const tr = rTop + r, tc = cLeft + c;
                if (tr < 0 || tr >= H || tc < 0 || tc >= W) continue;
                const src = clipboard.cells[r * clipboard.width + c];
                const i = tr * W + tc;
                cells[i] = { c: src.c, fg: src.fg, bg: src.bg };
                drawCell(i);
            }
        }
        dirty = true;
        document.getElementById('saveStatus').textContent = 'unsaved';
    }

    function flipSelectionHorizontal() {
        if (!selRect) return;
        pushHistory();
        const w = selRect.c1 - selRect.c0 + 1;
        const h = selRect.r1 - selRect.r0 + 1;
        for (let r = 0; r < h; r++) {
            for (let c = 0; c < Math.floor(w / 2); c++) {
                const a = (selRect.r0 + r) * W + (selRect.c0 + c);
                const b = (selRect.r0 + r) * W + (selRect.c0 + (w - 1 - c));
                const tmp = cells[a]; cells[a] = cells[b]; cells[b] = tmp;
                drawCell(a); drawCell(b);
            }
        }
        dirty = true;
        document.getElementById('saveStatus').textContent = 'unsaved';
    }

    function flipSelectionVertical() {
        if (!selRect) return;
        pushHistory();
        const w = selRect.c1 - selRect.c0 + 1;
        const h = selRect.r1 - selRect.r0 + 1;
        for (let c = 0; c < w; c++) {
            for (let r = 0; r < Math.floor(h / 2); r++) {
                const a = (selRect.r0 + r) * W + (selRect.c0 + c);
                const b = (selRect.r0 + (h - 1 - r)) * W + (selRect.c0 + c);
                const tmp = cells[a]; cells[a] = cells[b]; cells[b] = tmp;
                drawCell(a); drawCell(b);
            }
        }
        dirty = true;
        document.getElementById('saveStatus').textContent = 'unsaved';
    }

    function fillFlood(r, c) {
        if (r < 0 || r >= H || c < 0 || c >= W) return;
        const i = r * W + c;
        const target = {c: cells[i].c, fg: cells[i].fg, bg: cells[i].bg};
        if (target.c === curChar && target.fg === curFg && target.bg === curBg) return;
        const stack = [[r, c]];
        while (stack.length) {
            const [rr, cc] = stack.pop();
            if (rr < 0 || rr >= H || cc < 0 || cc >= W) continue;
            const ii = rr * W + cc;
            const cell = cells[ii];
            if (cell.c !== target.c || cell.fg !== target.fg || cell.bg !== target.bg) continue;
            paint(rr, cc);
            stack.push([rr+1, cc], [rr-1, cc], [rr, cc+1], [rr, cc-1]);
        }
    }

    // ---- Mouse interaction ----
    grid.addEventListener('mousedown', e => {
        const cell = e.target.closest('.cell');
        if (!cell) return;
        const r = parseInt(cell.dataset.r, 10);
        const c = parseInt(cell.dataset.c, 10);
        cursorR = r; cursorC = c; updateCursorMark();
        if (tool === 'pencil') { paint(r, c); mouseDown = true; }
        else if (tool === 'eraser') { erase(r, c); mouseDown = true; }
        else if (tool === 'fill') { fillFlood(r, c); }
        else if (tool === 'line') { lineStart = [r, c]; }
        else if (tool === 'rect') { lineStart = [r, c]; }
        else if (tool === 'select') { selStart = [r, c]; clearSelection(); }
        else if (tool === 'eyedrop') {
            const cell = cells[r * W + c];
            curChar = cell.c || ' ';
            curFg = cell.fg & 0x0F;
            curBg = cell.bg & 0x07;
            buildSwatches();
            updateCharPreview();
        }
        else if (tool === 'text') { /* cursor placement only */ }
    });
    grid.addEventListener('mouseup', e => {
        const cell = e.target.closest('.cell');
        if (cell && (tool === 'line' || tool === 'rect') && lineStart) {
            const r = parseInt(cell.dataset.r, 10);
            const c = parseInt(cell.dataset.c, 10);
            if (tool === 'line') drawLine(lineStart[0], lineStart[1], r, c);
            else if (tool === 'rect') drawRect(lineStart[0], lineStart[1], r, c);
            lineStart = null;
        }
        if (cell && tool === 'select' && selStart) {
            const r = parseInt(cell.dataset.r, 10);
            const c = parseInt(cell.dataset.c, 10);
            selRect = {
                r0: Math.min(selStart[0], r), c0: Math.min(selStart[1], c),
                r1: Math.max(selStart[0], r), c1: Math.max(selStart[1], c),
            };
            highlightSelection();
            selStart = null;
        }
        mouseDown = false;
    });
    grid.addEventListener('mouseover', e => {
        const cell = e.target.closest('.cell');
        if (!cell) return;
        if (mouseDown) {
            const r = parseInt(cell.dataset.r, 10);
            const c = parseInt(cell.dataset.c, 10);
            if (tool === 'pencil') paint(r, c);
            else if (tool === 'eraser') erase(r, c);
        }
    });
    document.addEventListener('mouseup', () => { mouseDown = false; });

    // ---- Keyboard for text mode ----
    document.addEventListener('keydown', e => {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
        if (tool === 'text') {
            if (e.key === 'ArrowUp')   { cursorR = Math.max(0, cursorR-1); updateCursorMark(); e.preventDefault(); }
            else if (e.key === 'ArrowDown') { cursorR = Math.min(H-1, cursorR+1); updateCursorMark(); e.preventDefault(); }
            else if (e.key === 'ArrowLeft') { cursorC = Math.max(0, cursorC-1); updateCursorMark(); e.preventDefault(); }
            else if (e.key === 'ArrowRight'){ cursorC = Math.min(W-1, cursorC+1); updateCursorMark(); e.preventDefault(); }
            else if (e.key === 'Backspace') {
                cursorC = Math.max(0, cursorC-1);
                erase(cursorR, cursorC);
                updateCursorMark(); e.preventDefault();
            } else if (e.key === 'Enter') {
                cursorR = Math.min(H-1, cursorR+1); cursorC = 0;
                updateCursorMark(); e.preventDefault();
            } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
                paint(cursorR, cursorC, e.key);
                if (cursorC < W-1) cursorC++;
                else if (cursorR < H-1) { cursorC = 0; cursorR++; }
                updateCursorMark();
                e.preventDefault();
            }
        }
    });

    // ---- UI: tool buttons ----
    document.querySelectorAll('[data-tool]').forEach(btn => {
        btn.addEventListener('click', () => {
            tool = btn.dataset.tool;
            document.querySelectorAll('[data-tool]').forEach(b => b.classList.remove('active'));
            btn.classList.add('active');
        });
    });
    document.querySelectorAll('[data-quickch]').forEach(btn => {
        btn.addEventListener('click', () => {
            curChar = btn.dataset.quickch;
            updateCharPreview();
        });
    });

    // ---- Color swatches ----
    function buildSwatches() {
        const fg = document.getElementById('fgSwatches');
        const bg = document.getElementById('bgSwatches');
        fg.innerHTML = ''; bg.innerHTML = '';
        for (let i = 0; i < 16; i++) {
            const s = document.createElement('div');
            s.className = 'swatch' + (i === curFg ? ' active' : '');
            s.style.background = palette[i];
            s.dataset.fg = i;
            s.addEventListener('click', () => {
                curFg = i;
                document.querySelectorAll('#fgSwatches .swatch').forEach(x => x.classList.remove('active'));
                s.classList.add('active');
                updateCharPreview();
            });
            fg.appendChild(s);
        }
        for (let i = 0; i < 8; i++) {
            const s = document.createElement('div');
            s.className = 'swatch' + (i === curBg ? ' active' : '');
            s.style.background = palette[i];
            s.dataset.bg = i;
            s.addEventListener('click', () => {
                curBg = i;
                document.querySelectorAll('#bgSwatches .swatch').forEach(x => x.classList.remove('active'));
                s.classList.add('active');
                updateCharPreview();
            });
            bg.appendChild(s);
        }
    }

    function updateCharPreview() {
        const p = document.getElementById('curCharPreview');
        p.textContent = curChar;
        p.style.color = palette[curFg];
        p.style.background = palette[curBg];
        document.getElementById('curCharCode').textContent = curChar.charCodeAt(0);
    }

    // ---- Character palette (printable ASCII + common CP437 box chars) ----
    function buildCharset() {
        const cs = document.getElementById('charset');
        const chars = [];
        // Printable ASCII 32-126
        for (let i = 32; i < 127; i++) chars.push(String.fromCharCode(i));
        // CP437 high glyphs (use Unicode equivalents)
        const high = '░▒▓█▀▄▌▐♠♣♥♦☺☻☼♀♂♪♫¤§∙√∞×ƒ' +
                     '┌┐└┘─│┬┴├┤┼' +
                     '╔╗╚╝═║╦╩╠╣╬' +
                     '╓╖╙╜╤╧╟╢╫╪╕╛╞╡' +
                     '←→↑↓↔↕▲▼◄►■□●○◆';
        for (const c of high) chars.push(c);
        cs.innerHTML = '';
        for (const ch of chars) {
            const el = document.createElement('div');
            el.className = 'ch';
            el.textContent = ch;
            el.title = `code ${ch.charCodeAt(0)}`;
            el.addEventListener('click', () => {
                curChar = ch;
                document.querySelectorAll('#charset .ch').forEach(x => x.classList.remove('active'));
                el.classList.add('active');
                updateCharPreview();
            });
            cs.appendChild(el);
        }
    }

    // ---- Undo/Redo stack ----
    // Snapshot whole `cells` array on every "stroke boundary" (mousedown,
    // text-mode keypress) — chunky but simple. Cap at 50 history entries.
    const undoStack = [];
    const redoStack = [];
    const HISTORY_MAX = 50;

    function snapshot() {
        // Deep-clone cells so future mutations don't mutate this snapshot.
        return cells.map(c => ({ c: c.c, fg: c.fg, bg: c.bg }));
    }

    function pushHistory() {
        undoStack.push(snapshot());
        if (undoStack.length > HISTORY_MAX) undoStack.shift();
        redoStack.length = 0;        // any new edit invalidates redo
    }

    function applySnapshot(snap) {
        cells = snap.map(c => ({ c: c.c, fg: c.fg, bg: c.bg }));
        for (let i = 0; i < cells.length; i++) drawCell(i);
        dirty = true;
        document.getElementById('saveStatus').textContent = 'unsaved';
    }

    function doUndo() {
        if (!undoStack.length) return;
        redoStack.push(snapshot());
        applySnapshot(undoStack.pop());
    }

    function doRedo() {
        if (!redoStack.length) return;
        undoStack.push(snapshot());
        applySnapshot(redoStack.pop());
    }

    document.getElementById('btnUndo').addEventListener('click', doUndo);
    document.getElementById('btnRedo').addEventListener('click', doRedo);
    document.getElementById('btnCopy').addEventListener('click', copySelection);
    document.getElementById('btnPaste').addEventListener('click', () => {
        if (!clipboard) return;
        // Paste at the cursor or top-left of selection.
        const rTop = selRect ? selRect.r0 : cursorR;
        const cLeft = selRect ? selRect.c0 : cursorC;
        pasteAt(rTop, cLeft);
    });
    document.getElementById('btnFlipH').addEventListener('click', flipSelectionHorizontal);
    document.getElementById('btnFlipV').addEventListener('click', flipSelectionVertical);

    // Zoom — scales cell width/height/font via CSS variables.
    let zoom = 1;
    function applyZoom() {
        const w = Math.round(10 * zoom);
        const h = Math.round(18 * zoom);
        const f = Math.round(14 * zoom);
        const wrap = document.querySelector('.ansi-grid-wrap');
        wrap.style.setProperty('--cell-w', w + 'px');
        wrap.style.setProperty('--cell-h', h + 'px');
        wrap.style.setProperty('--cell-font', f + 'px');
        // Refresh grid template
        grid.style.gridTemplateColumns = `repeat(${W}, ${w}px)`;
        grid.style.gridTemplateRows    = `repeat(${H}, ${h}px)`;
    }
    document.getElementById('btnZoomIn').addEventListener('click', () => {
        zoom = Math.min(3, zoom + 0.25); applyZoom();
    });
    document.getElementById('btnZoomOut').addEventListener('click', () => {
        zoom = Math.max(0.5, zoom - 0.25); applyZoom();
    });

    document.querySelectorAll('.brush-btn').forEach(btn => {
        btn.addEventListener('click', () => {
            brushSize = parseInt(btn.dataset.brush, 10) || 1;
            document.querySelectorAll('.brush-btn').forEach(b => b.classList.remove('active'));
            btn.classList.add('active');
        });
    });

    // Hijack mousedown to snapshot before any draw happens.
    grid.addEventListener('mousedown', () => pushHistory(), true);

    // Clear canvas
    document.getElementById('btnClear').addEventListener('click', () => {
        if (!confirm('Clear the entire canvas? (Undo will restore it.)')) return;
        pushHistory();
        for (let i = 0; i < cells.length; i++) {
            cells[i] = { c: ' ', fg: 7, bg: 0 };
            drawCell(i);
        }
        dirty = true;
        document.getElementById('saveStatus').textContent = 'unsaved';
    });

    // Preview window — opens the rendered .ans in a new tab.
    document.getElementById('btnPreview').addEventListener('click', () => {
        // Save first so the preview reflects the latest edits.
        document.getElementById('btnSave').click();
        setTimeout(() => {
            window.open('{{ url_for("ansi_editor.preview", art_id=art.id) }}',
                        '_blank');
        }, 400);
    });

    // Keyboard shortcuts (when not in text-typing mode)
    document.addEventListener('keydown', e => {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
        if (tool === 'text') return;
        if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'z') {
            e.preventDefault();
            if (e.shiftKey) doRedo(); else doUndo();
        } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'y') {
            e.preventDefault();
            doRedo();
        } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 's') {
            e.preventDefault();
            document.getElementById('btnSave').click();
        } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'c') {
            e.preventDefault();
            copySelection();
        } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'v') {
            e.preventDefault();
            const rTop = selRect ? selRect.r0 : cursorR;
            const cLeft = selRect ? selRect.c0 : cursorC;
            pasteAt(rTop, cLeft);
        } else if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'a') {
            e.preventDefault();
            selRect = { r0: 0, c0: 0, r1: H - 1, c1: W - 1 };
            highlightSelection();
        } else if (e.key === 'Escape') {
            clearSelection();
        }
    });

    // ---- Save ----
    document.getElementById('btnSave').addEventListener('click', async () => {
        const status = document.getElementById('saveStatus');
        status.textContent = 'saving…';
        try {
            const r = await fetch('{{ url_for("ansi_editor.save", art_id=art.id) }}', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                credentials: 'same-origin',
                body: JSON.stringify({
                    grid: { width: W, height: H, cells: cells },
                }),
            });
            if (!r.ok) throw new Error('HTTP ' + r.status);
            const data = await r.json();
            if (!data.ok) throw new Error(data.error || 'save failed');
            status.textContent = 'saved ' + new Date().toLocaleTimeString();
            dirty = false;
        } catch (e) {
            status.textContent = 'save failed: ' + e.message;
        }
    });

    // Warn if leaving with unsaved changes
    window.addEventListener('beforeunload', e => {
        if (dirty) { e.preventDefault(); return ''; }
    });

    // ---- Initialize ----
    buildGrid();
    buildSwatches();
    buildCharset();
    updateCharPreview();
})();
</script>
{% endblock %}
