<?php

declare(strict_types=1);

/**
 * The web ANSI editor (SPEC §12): TheDraw's spirit in the console. The
 * document model, tools, and .ans encoder live in assets/editor.js (the
 * same renderer core the caller terminal uses); this page provides the
 * chrome and four JSON endpoints against the art gallery (1.1.0) --
 * one board-wide shelf of pieces under data/art/, no theme coupling:
 *
 *   load:   return a gallery piece as board-decoded text + SAUCE meta;
 *   save:   accept encoder bytes (base64), sanity-check the SAUCE, and
 *            write data/art/<name>.ans;
 *   import: parse an uploaded .ans and hand it back, unsaved;
 *   delete: take a piece off the shelf.
 *
 * Export never touches the server: the client builds the exact bytes and
 * downloads them. Everything drawn here renders on the board through
 * Cp437::loadAns, so what you see is what callers get. Themes bind their
 * built-in screens to pieces on the Themes page; menus and bulletins
 * pick pieces on theirs.
 */

require __DIR__ . '/_lib.php';
admin_require();
$db = atdt_db();
$settings = atdt_settings();

function ed_json(array $payload): never
{
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode($payload);
    exit;
}

/** @return array{text: string, ice: bool, rows: int, sauce: array<string, string>} */
function ed_parse(string $bytes): array
{
    [, $sauce] = \Atdt\Cp437::splitSauce($bytes);
    return [
        'text' => \Atdt\Cp437::loadAns($bytes),
        'ice' => (bool)(((int)($sauce['flags'] ?? 0)) & 1),
        'rows' => (int)($sauce['tinfo2'] ?? 0),
        'sauce' => [
            'title' => (string)($sauce['title'] ?? ''),
            'author' => (string)($sauce['author'] ?? ''),
            'group' => (string)($sauce['group'] ?? ''),
        ],
    ];
}

if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
    $name = strtolower(trim((string)($_POST['slot'] ?? '')));
    switch ((string)($_POST['action'] ?? '')) {
        case 'load':
            $bytes = \Atdt\Art::bytes($name);
            if ($bytes === null) {
                ed_json(['ok' => false, 'err' => 'No such piece in the gallery.']);
            }
            ed_json(['ok' => true] + ed_parse($bytes));

        case 'save':
            if (preg_match(\Atdt\Art::NAME_RE, $name) !== 1) {
                ed_json(['ok' => false, 'err' => 'Piece names: 1-32 chars, a-z 0-9 _ - only.']);
            }
            $bytes = base64_decode((string)($_POST['bytes'] ?? ''), true);
            if ($bytes === false || $bytes === '' || strlen($bytes) > \Atdt\Art::MAX_BYTES) {
                ed_json(['ok' => false, 'err' => 'Bad or oversized payload.']);
            }
            [, $sauce] = \Atdt\Cp437::splitSauce($bytes);
            if ($sauce === null || (int)$sauce['datatype'] !== 1) {
                ed_json(['ok' => false, 'err' => 'That is not an editor-built .ans (no SAUCE).']);
            }
            if (!\Atdt\Art::save($name, $bytes)) {
                ed_json(['ok' => false, 'err' => 'Could not write to data/art/ - check permissions.']);
            }
            admin_log("Saved gallery piece '$name' (" . strlen($bytes) . ' bytes) via the editor');
            ed_json(['ok' => true, 'slots' => \Atdt\Art::pieces()]);

        case 'delete':
            if (!\Atdt\Art::delete($name)) {
                ed_json(['ok' => false, 'err' => 'No such piece in the gallery.']);
            }
            admin_log("Deleted gallery piece '$name' via the editor");
            ed_json(['ok' => true, 'slots' => \Atdt\Art::pieces()]);

        case 'import':
            $file = $_FILES['file'] ?? null;
            if (!is_array($file) || (int)($file['error'] ?? 1) !== UPLOAD_ERR_OK) {
                ed_json(['ok' => false, 'err' => 'Upload failed.']);
            }
            if ((int)$file['size'] > \Atdt\Art::MAX_BYTES) {
                ed_json(['ok' => false, 'err' => 'File too large (256 KB cap).']);
            }
            $bytes = (string)file_get_contents((string)$file['tmp_name']);
            ed_json(['ok' => true] + ed_parse($bytes));

        // ---- ANSI letter fonts (1.5.5) ----------------------------------

        case 'font_load':
            $font = \Atdt\AnsiFonts::load($name);
            if ($font === null) {
                ed_json(['ok' => false, 'err' => 'No such font.']);
            }
            ed_json(['ok' => true, 'font' => $font]);

        case 'font_save':
            if (preg_match(\Atdt\AnsiFonts::NAME_RE, $name) !== 1) {
                ed_json(['ok' => false, 'err' => 'Font names: 1-24 chars, a-z 0-9 _ - only.']);
            }
            $raw = (string)($_POST['font'] ?? '');
            if (strlen($raw) > \Atdt\AnsiFonts::MAX_BYTES) {
                ed_json(['ok' => false, 'err' => 'Font too large.']);
            }
            if (!\Atdt\AnsiFonts::save($name, json_decode($raw, true))) {
                ed_json(['ok' => false, 'err' => 'That font did not validate (glyphs are A-Z 0-9, up to 16x8 cells).']);
            }
            admin_log("Saved ANSI font '$name' via the editor");
            ed_json(['ok' => true, 'fonts' => \Atdt\AnsiFonts::pieces()]);

        case 'font_delete':
            if (!\Atdt\AnsiFonts::delete($name)) {
                ed_json(['ok' => false, 'err' => 'No such font.']);
            }
            admin_log("Deleted ANSI font '$name' via the editor");
            ed_json(['ok' => true, 'fonts' => \Atdt\AnsiFonts::pieces()]);
    }
    ed_json(['ok' => false, 'err' => 'Unknown action.']);
}

$glyphs = [];
for ($b = 0; $b < 256; $b++) {
    $glyphs[] = mb_ord(\Atdt\Cp437::toGlyph($b), 'UTF-8');
}

admin_page_start('ANSI Editor', 'editor');
?>

<div class="edwrap">
  <div class="edtop card">
    <label>Gallery
      <select id="edslots"></select></label>
    <button class="btn small" id="edloadbtn">Open</button>
    <button class="btn small secondary" id="ednew">New</button>
    <button class="btn small danger" id="eddelete" title="Delete the selected piece from the gallery">Delete</button>
    <span class="spacer"></span>
    <input type="text" id="edslotname" placeholder="piece name (e.g. main_menu)" maxlength="32" size="22">
    <button class="btn small" id="edsave">Save to gallery</button>
    <button class="btn small secondary" id="edexport">Export .ans</button>
    <label class="btn small secondary filelbl">Import… <input type="file" id="edimport" accept=".ans" hidden></label>
  </div>

  <div id="edrecover" class="flash ok" style="display:none">
    An unsaved draft from your last visit was recovered.
    <button class="btn small secondary" id="eddiscard">Discard it</button>
  </div>

  <div class="edmain">
    <div class="edrail card">
      <strong>Tools</strong>
      <div class="edtools" id="edtools">
        <button data-tool="pencil" class="active" title="Pencil (P): stamp the current glyph">Pencil</button>
        <button data-tool="half" title="Half-block brush (B): paint 80×2N pixels; right-drag erases">Half-block</button>
        <button data-tool="text" title="Text (T): click and type">Text</button>
        <button data-tool="font" title="Font (O): click, pick a font below, type big letters">Font</button>
        <button data-tool="line" title="Line (L): drag">Line</button>
        <button data-tool="rect" title="Rectangle (R): drag; draws the border">Rect</button>
        <button data-tool="fill" title="Flood fill (F): click a region">Fill</button>
        <button data-tool="color" title="Recolor (C): drag to repaint what is already there with the current ink -- nothing gets retyped">Recolor</button>
        <button data-tool="select" title="Select (S): drag, then Copy / Move / Tint / Del">Select</button>
      </div>
      <div class="edselops" id="edselops" style="display:none">
        <button id="edcopy" title="Copy: lift a copy of the selection; steer with mouse or arrows, Enter or click places, Esc cancels">Copy</button>
        <button id="edmove" title="Move: lift the selection itself and watch it ride; steer with mouse or arrows (Shift=5), Enter or click places, Esc puts it back">Move</button>
        <button id="edtint" title="Recolor the selection with the current ink; glyphs stay put">Tint</button>
        <button id="eddel" title="Blank the selection">Del</button>
      </div>
      <div class="edselops" id="edfontrow" style="display:none">
        <label>Font <select id="edfontsel"></select></label>
        <button id="edflopen" title="The font library: draw your own letter set">Fonts…</button>
      </div>
      <strong>Colors</strong>
      <div class="edpal" id="edpal-fg"></div>
      <div class="edpal" id="edpal-bg"></div>
      <label class="radio"><input type="checkbox" id="edice"> iCE colors <span class="hint">(bright backgrounds instead of blink)</span></label>
      <p class="hint">x swaps ink/paper.</p>
      <strong>Glyphs <span class="hint" id="edsetname">set F1</span></strong>
      <div class="edglyphrow" id="edglyphs"></div>
      <p class="hint">F1-F10 pick a classic set, 1-0 pick from it, Tab opens all 240.</p>
      <strong>Canvas</strong>
      <div class="row">
        <label>Rows <input type="number" id="edrows" min="1" max="100" value="25" style="width:70px"></label>
        <button class="btn small secondary" id="edzoom" title="Zoom">1×</button>
        <button class="btn small secondary" id="edgridbtn" title="Grid overlay">Grid</button>
      </div>
      <div class="row">
        <button class="btn small secondary" id="edundo" title="Undo (Ctrl+Z)">Undo</button>
        <button class="btn small secondary" id="edredo" title="Redo (Ctrl+Y)">Redo</button>
        <button class="btn small danger" id="edclear" title="Blank the whole canvas (asks first; Undo can bring it back)">Clear</button>
      </div>
      <strong>SAUCE</strong>
      <label>Title <input type="text" id="edtitle" maxlength="35"></label>
      <label>Author <input type="text" id="edauthor" maxlength="20"></label>
      <label>Group <input type="text" id="edgroup" maxlength="20"></label>
    </div>

    <div class="edcanvascol">
      <div class="edcanvasbox card"><canvas id="edcanvas" width="640" height="400"></canvas></div>
      <div class="edstatus card" id="edstatus">1,1 · pencil</div>
    </div>
  </div>

  <div id="edpicker" class="card" style="display:none"></div>

  <div id="edfontlib" class="card" style="display:none">
    <h2>Font library</h2>
    <p class="hint">TheDraw-style letter sets for the Font tool. The four shipped fonts take the
    editor's current ink when stamped; fonts you draw here stamp exactly as drawn, colors and
    all. Glyphs are A-Z and 0-9, up to 16 wide by 8 tall.</p>
    <div class="row">
      <label>Your fonts <select id="edflsel"></select></label>
      <button class="btn small" id="edflload">Edit</button>
      <button class="btn small danger" id="edfldelete">Delete</button>
      <span class="spacer"></span>
      <input type="text" id="edflname" placeholder="new font name" maxlength="24" size="16">
      <label>Rows tall <input type="number" id="edflh" min="1" max="8" value="5" style="width:60px"></label>
      <button class="btn small" id="edflnew">Start drawing</button>
      <button class="btn small secondary" id="edflclose">Close</button>
    </div>
    <div id="edflwork" style="display:none">
      <div class="edglyphrow" id="edflglyphs"></div>
      <div class="row">
        <label>This glyph's width <input type="number" id="edflw" min="1" max="16" value="5" style="width:60px"></label>
        <button class="btn small secondary" id="edflclearg">Clear glyph</button>
        <span class="hint">Left-drag paints with the editor's current glyph and ink; right-drag
        makes cells transparent (stamping leaves them untouched).</span>
      </div>
      <canvas id="edflcanvas" width="128" height="80" style="image-rendering:pixelated"></canvas>
      <div class="row">
        <button class="btn small" id="edflsave">Save font</button>
        <span class="hint" id="edflstatus"></span>
      </div>
    </div>
  </div>

  <div class="card">
    <h2>How this fits the board</h2>
    <p>Everything saved here lands in the <strong>art gallery</strong> - one board-wide shelf
    (<code>data/art/</code>). Anywhere the board can wear art, you pick a piece from this
    gallery by name: a theme's connect / logon / goodbye screens (Themes page), a menu's
    backdrop and its <strong>per-level variants</strong> (Menus page), a bulletin's full-screen
    art (Community page), and the <code>ART</code> menu action. The board shows rows 1-25;
    taller documents are for export. The <a href="gallery.php">Gallery page</a> shows every
    piece with a preview when the dropdown gets crowded.</p>
    <p>The <strong>Font tool</strong> (1.5.5) types big letters the way TheDraw's .TDF fonts
    did: pick a font, click the canvas, and type A-Z or 0-9. Four fonts ship with the board
    and stamp in your current ink; the <strong>font library</strong> lets you draw your own
    sets, colors and all.</p>
    <p class="hint">Hidden menu commands: on the Menus page, mark an item "hidden" and leave it out
    of your drawing; the hotkey still works for callers in the know. That plus a per-level backdrop
    is the classic easter-egg setup. Files are real .ans with SAUCE; PabloDraw and Moebius open
    them fine.</p>
  </div>
</div>

<script id="ed-config" type="application/json"><?= json_encode([
    'csrf' => admin_csrf_token(),
    'slots' => \Atdt\Art::pieces(),
    'fonts' => \Atdt\AnsiFonts::pieces(),
    'glyphs' => $glyphs,
    'font' => '../' . atdt_asset('assets/fonts/Web437_IBM_VGA_8x16.woff'),
    'open' => preg_match(\Atdt\Art::NAME_RE, (string)($_GET['open'] ?? '')) === 1 ? (string)$_GET['open'] : '',
], JSON_HEX_TAG) ?></script>
<script src="../<?= h(atdt_asset('assets/cp437render.js')) ?>"></script>
<script src="../<?= h(atdt_asset('assets/term.js')) ?>"></script>
<script src="../<?= h(atdt_asset('assets/editor.js')) ?>"></script>
<script>window.atdtEd = AtdtEd.boot(document);</script>

<?php admin_page_end(); ?>
