/**
 * SPCAMO_Import_Entrench.js
 * ─────────────────────────────────────────────────────────────────────────────
 * Imports obstacles (mines, dragon's teeth, barbed wire) from SP1 JSON into
 * the SPCAMO scenario format.
 *
 * SPCAMO obstacle storage:
 *   S17 (19-byte records, blocks of 1000):
 *     Block 2 (recs 2000-2999): Dragon's Teeth  (typ=0, strength=count×10)
 *     Block 6 (recs 6000-6999): Mines (typ=1) + Barbed Wire (tile=1491, str=1004)
 *   Per-hex flag sections (1 byte/hex, column-major, stride=MAPSIZE=200):
 *     S9:  Mine presence flag
 *     S52: Dragon's Teeth presence flag
 *     S56: Barbed Wire presence flag
 *
 * Dependencies (loaded before this file):
 *   SPCAMO_ScenData.js  — parseSections, getSectionData, _rebuildFile,
 *                         SPCAMO_MAPSIZE, S17_OBS_* constants
 *
 * Editor globals used:
 *   _ab, _obstacles, _markDirty(), renderEntrenchments()
 */

'use strict';

// ─────────────────────────────────────────────────────────────────────────────
// Public entry point — called by the ENTRENCHMENT JSON button
// ─────────────────────────────────────────────────────────────────────────────

function importEntrenchJSON() {
  if (!_ab) { alert('Load a SPCAMO DAT file first.'); return; }

  const fi = document.createElement('input');
  fi.type = 'file';
  fi.accept = '.json,.JSON';
  fi.style.display = 'none';
  document.body.appendChild(fi);

  fi.onchange = e => {
    const file = e.target.files[0];
    fi.remove();
    if (!file) return;
    const r = new FileReader();
    r.onload = ev => {
      try {
        const data = JSON.parse(ev.target.result);
        const jsonObs = data.obstacles || [];
        const jsonUPE = data.unitPersonalEntrenchments || [];
        if (!jsonObs.length && !jsonUPE.length) {
          alert('No "obstacles" or "unitPersonalEntrenchments" array found in this JSON, or both are empty.');
          return;
        }

        // ── Parse S17 and per-hex flag sections ────────────────────────────
        const sections = parseSections(_ab);
        if (!sections[17]) throw new Error('Section 17 not found in DAT file.');
        const s17 = getSectionData(sections[17]);
        if (!s17) throw new Error('Could not decompress Section 17.');
        const out17 = s17.slice();  // mutable copy

        // Per-hex flag sections (create writable copies)
        const s9raw  = sections[9]  ? getSectionData(sections[9])  : null;
        const s52raw = sections[52] ? getSectionData(sections[52]) : null;
        const s56raw = sections[56] ? getSectionData(sections[56]) : null;
        const out9  = s9raw  ? s9raw.slice()  : null;
        const out52 = s52raw ? s52raw.slice() : null;
        const out56 = s56raw ? s56raw.slice() : null;

        // ── Separate obstacles by type ─────────────────────────────────────
        const dtList   = jsonObs.filter(o => o.type === "Dragon's Teeth" || o.typeCode === 0);
        const mineList = jsonObs.filter(o => o.type === 'Mines'          || o.typeCode === 1);
        // BW is not in SP1 JSONs, but handle it if present
        const bwList   = jsonObs.filter(o => o.type === 'Barbed Wire');

        // ── Helper: write a 19-byte obstacle record into S17 ───────────────
        function writeObsRec(buf, recIdx, xloc, yloc, tileID, strength, typ, side) {
          const off = recIdx * 19;
          if (off + 19 > buf.length) return;
          buf[off+0]  =  xloc       & 0xFF;
          buf[off+1]  = (xloc >> 8) & 0xFF;
          buf[off+2]  =  yloc       & 0xFF;
          buf[off+3]  = (yloc >> 8) & 0xFF;
          buf[off+4]  = 0xFF; buf[off+5]  = 0xFF;  // padding
          buf[off+6]  =  tileID       & 0xFF;
          buf[off+7]  = (tileID >> 8) & 0xFF;
          buf[off+8]  = 0xFF; buf[off+9]  = 0xFF;  // padding
          buf[off+10] = 5;    buf[off+11] = 0;     // active flag = 5
          buf[off+12] =  strength       & 0xFF;
          buf[off+13] = (strength >> 8) & 0xFF;
          buf[off+14] = typ  & 0xFF;
          buf[off+15] = side & 0xFF;
          buf[off+16] = 0;
          buf[off+17] = 0xFF; buf[off+18] = 0xFF;  // padding
        }

        // ── Helper: set per-hex flag ───────────────────────────────────────
        function setHexFlag(flagBuf, xloc, yloc) {
          if (!flagBuf) return;
          const off = xloc * SPCAMO_MAPSIZE + yloc;
          if (off >= 0 && off < flagBuf.length) flagBuf[off] = 1;
        }

        // ── Find next free slot (scanning backwards from block end) ────────
        function findFreeSlots(buf, blockStart, blockEnd, count) {
          // Scan backwards from blockEnd-1 to find empty (all-0 or all-0xFF) slots
          const slots = [];
          for (let r = blockEnd - 1; r >= blockStart && slots.length < count; r--) {
            const off = r * 19;
            if (off + 19 > buf.length) continue;
            const x = buf[off] | (buf[off+1] << 8);
            const y = buf[off+2] | (buf[off+3] << 8);
            const str = buf[off+12] | (buf[off+13] << 8);
            // Slot is free if all-zero or all-0xFF, or if x,y=0xFFFF with no strength
            const allZero = (x === 0 && y === 0 && str === 0);
            const allFF   = (x === 0xFFFF && y === 0xFFFF);
            if (allZero || allFF) slots.push(r);
          }
          return slots;
        }

        // ── Write Dragon's Teeth into block 2 ──────────────────────────────
        if (dtList.length) {
          const slots = findFreeSlots(out17, S17_OBS_DT_START, S17_OBS_DT_END, dtList.length);
          if (slots.length < dtList.length) {
            alert('Warning: only ' + slots.length + ' free DT slots available (needed ' + dtList.length + ').');
          }
          dtList.forEach((o, i) => {
            if (i >= slots.length) return;
            const side = o.sideCode ?? (o.side === 'P2' ? 1 : 0);
            const str  = o.strength || (o.count || 1) * 10;
            const tile = o.tileID || (1420 + (o.count || 1));
            writeObsRec(out17, slots[i], o.xloc, o.yloc, tile, str, 0, side);
            setHexFlag(out52, o.xloc, o.yloc);
          });
        }

        // ── Write Mines into block 6 ───────────────────────────────────────
        if (mineList.length || bwList.length) {
          const needed = mineList.length + bwList.length;
          const slots = findFreeSlots(out17, S17_OBS_MN_START, S17_OBS_MN_END, needed);
          if (slots.length < needed) {
            alert('Warning: only ' + slots.length + ' free mine/BW slots available (needed ' + needed + ').');
          }
          let si = 0;
          mineList.forEach(o => {
            if (si >= slots.length) return;
            const side = o.sideCode ?? (o.side === 'P2' ? 1 : 0);
            const str  = o.strength || (o.count || 1) * 10;
            const tile = o.tileID || (1425 + (o.count || 1));
            writeObsRec(out17, slots[si++], o.xloc, o.yloc, tile, str, 1, side);
            setHexFlag(out9, o.xloc, o.yloc);
          });
          bwList.forEach(o => {
            if (si >= slots.length) return;
            const side = o.sideCode ?? (o.side === 'P2' ? 1 : 0);
            writeObsRec(out17, slots[si++], o.xloc, o.yloc, S17_OBS_TILE_BW, S17_OBS_STR_BW, 0, side);
            setHexFlag(out56, o.xloc, o.yloc);
          });
        }

        // ── Build the section patch map (map-level obstacles) ──────────────
        const patchMap = { 17: out17 };
        if (out9)  patchMap[9]  = out9;
        if (out52) patchMap[52] = out52;
        if (out56) patchMap[56] = out56;

        // ── Personal unit entrenchments (foxholes / sandbags) ──────────────
        // Map-level obstacles are imported first (above). If the JSON also
        // carries a unitPersonalEntrenchments section, ask whether to dig in
        // individual units. Verified against SP-MBT A/B saves: an entrenched
        // unit requires THREE coordinated writes —
        //   (1) S1  offset 178 = 1                  per-unit dug-in flag
        //   (2) S17 block 2 emplacement record       tile, active=5,
        //                                            strength=0xFFFF, typ=1,
        //                                            side; shares the DT block,
        //                                            appended after any DT.
        //   (3) S8  hex byte 8 |= 64 (foxhole)/128 (sandbag)   OR'd, so a hex
        //                                            with both ends at 192.
        // Matching is strict: a JSON record only digs in on-map units of the
        // same side sitting on exactly that hex.
        let upe = null;
        if (jsonUPE.length &&
            confirm('Map obstacles imported (' + dtList.length + ' DT, '
                  + mineList.length + ' mines'
                  + (bwList.length ? ', ' + bwList.length + ' BW' : '') + ').\n\n'
                  + 'This JSON also contains ' + jsonUPE.length + ' personal unit '
                  + 'entrenchment record(s) (foxholes / sandbags).\n\n'
                  + 'Import them too?\n\n'
                  + 'Units are matched strictly by hex + side — only units sitting '
                  + 'exactly on an entrenchment hex are dug in.')) {

          upe = { total: jsonUPE.length, matched: 0, placed: 0, skipped: 0, noSlot: 0, flagged: 0 };

          // Constants verified from the SP-MBT entrenchment A/B diffs
          const ENTR_OFF  = 178;   // S1 per-unit dug-in flag offset
          const REC       = 261;   // S1 record size
          const HEX_REC   = 15;    // S8 bytes per hex
          const ENTR_BYTE = 8;     // entrenchment byte within the S8 hex record
          const BIT_FOX   = 0x40;  // 64  — foxhole
          const BIT_SAND  = 0x80;  // 128 — sandbag
          const foxTile   = t => t >= 1464 && t <= 1467;
          const sandTile  = t => t >= 1430 && t <= 1433;
          const typeBit   = e => {
            const ty = String(e.entrenchmentType || '').toLowerCase();
            if (ty === 'foxhole') return BIT_FOX;
            if (ty === 'sandbag') return BIT_SAND;
            if (foxTile(e.pictureID))  return BIT_FOX;   // fall back to tile range
            if (sandTile(e.pictureID)) return BIT_SAND;
            return BIT_FOX;                              // safe default
          };
          const defTile = e =>
            (String(e.entrenchmentType || '').toLowerCase() === 'sandbag') ? 1430 : 1466;

          // Writable copies of the unit-flag (S1) and per-hex (S8) sections
          const s1raw = sections[1] ? getSectionData(sections[1]) : null;
          const out1  = s1raw ? s1raw.slice() : null;
          const s8raw = sections[8] ? getSectionData(sections[8]) : null;
          const out8  = s8raw ? s8raw.slice() : null;

          // Free emplacement slots in block 2 (scanned AFTER any DT written)
          const slots = findFreeSlots(out17, S17_OBS_DT_START, S17_OBS_DT_END, jsonUPE.length);
          let si = 0;

          jsonUPE.forEach(e => {
            const x = e.xloc, y = e.yloc;
            const sideCode = (e.sideCode != null) ? e.sideCode
                           : (e.side === 'P2' || e.side === 1) ? 1 : 0;
            if (x == null || y == null || x === 0xFFFF || y === 0xFFFF) { upe.skipped++; return; }

            // Strict hex + side: on-map units of that side sitting on the hex
            const pool = (sideCode === 1 ? (_units.p2 || []) : (_units.p1 || []));
            const hits = pool.filter(u => u.isOnMap && u.xloc === x && u.yloc === y);
            if (!hits.length) { upe.skipped++; return; }
            upe.matched++;

            // (1) per-unit flag — in memory (persisted on save) and in the S1 buffer
            hits.forEach(u => {
              if (!u.entr) upe.flagged++;
              u.entr = 1;
              if (out1) out1[u.index * REC + ENTR_OFF] = 1;
            });

            // Honor the source tile; fall back to a sensible default
            const tile = (e.pictureID && e.pictureID !== 0xFFFF) ? e.pictureID : defTile(e);
            const bit  = typeBit(e);

            // (2) S8 per-hex byte — OR the type bit (foxhole + sandbag → 192)
            if (out8) {
              const off = (x * SPCAMO_MAPSIZE + y) * HEX_REC + ENTR_BYTE;
              if (off >= 0 && off < out8.length) out8[off] |= bit;
            }

            // (3) S17 block-2 emplacement record
            if (si < slots.length) {
              writeObsRec(out17, slots[si++], x, y, tile, 0xFFFF, 1, sideCode);
              upe.placed++;
            } else {
              upe.noSlot++;
            }
          });

          if (out1) patchMap[1] = out1;
          if (out8) patchMap[8] = out8;

          if (upe.noSlot) {
            alert('Warning: ran out of free emplacement slots — ' + upe.noSlot
                + ' entrenchment(s) could not be placed.');
          }
        }

        // ── Rebuild the file with all modified sections ────────────────────
        _ab = _rebuildFile(_ab, sections, patchMap);

        // ── Re-read obstacles + refresh views ──────────────────────────────
        _obstacles = readSPCAMOObstacles(_ab);
        _markDirty();
        renderEntrenchments();
        if (typeof renderUnits === 'function') renderUnits();

        // Flash the import button
        const btn = document.getElementById('entrench-import-btn');
        if (btn) {
          const origText = btn.textContent;
          btn.textContent = '✓ IMPORTED ' + (jsonObs.length + (upe ? upe.placed : 0));
          setTimeout(() => { btn.textContent = origText; }, 2500);
        }

        let msg = 'Map obstacles: ' + dtList.length + ' DT, ' + mineList.length
                + ' mines, ' + bwList.length + ' BW.';
        if (upe) {
          msg += '\n\nUnit entrenchments: matched ' + upe.matched + ' of ' + upe.total
               + ' record(s); placed ' + upe.placed + ' emplacement(s); dug in '
               + upe.flagged + ' unit(s).';
          if (upe.skipped) msg += '\nSkipped (no unit at hex/side): ' + upe.skipped + '.';
          if (upe.noSlot)  msg += '\nUnplaced (no free slot): ' + upe.noSlot + '.';
        } else if (jsonUPE.length) {
          msg += '\n\nUnit entrenchments: ' + jsonUPE.length + ' record(s) present, skipped by user.';
        }
        alert(msg);

      } catch (err) {
        alert('Entrenchment import error:\n' + err.message);
        console.error(err);
      }
    };
    r.readAsText(file);
  };
  fi.click();
}
