/* area-editor.jsx — interaktivní editor detekční zóny nad živým videem.
 *
 * Tento soubor je SCHVÁLNĚ napsán jako čisté JS (React.createElement, žádné
 * JSX) a obalen do IIFE. Díky tomu jej lze do stránky vložit za běhu prostým
 * <script src="app/area-editor.jsx"> bez nutnosti transpilace Babelem —
 * index.html se needituje. Konfigurační stránky (samy transpilované Babelem)
 * jej načtou pomocným loaderem a používají <PatAreaEditor> jako běžnou
 * komponentu.
 *
 * Komponenta zobrazí ŽIVÉ video kanálu (přes window.patPlayer, stejně jako
 * CamVideo v app.jsx) a nad ním překryvný <canvas>, do kterého uživatel kreslí
 * detekční oblast — tak, jak to dělalo původní rozhraní zařízení.
 *
 * Vlastnosti (props):
 *   channel  — číslo kanálu (0…N)
 *   mode     — 'grid' | 'rect' | 'line' | 'region'
 *   value    — aktuální data zóny v nativním formátu dané stránky
 *   onChange — callback(newValue) hlásí změnu (opět v nativním formátu)
 *   height   — výška editoru v px (výchozí 320)
 *   options  — doplňková konfigurace dle režimu (viz níže)
 *
 * Souřadnicové modely (zjištěno z původního firmwaru webdev/):
 *   grid   — mřížka 22×18 buněk. value = pole 18 řetězců (řádky), v každém
 *            řetězci je dekadické číslo, bit `sloupec` = buňka zapnuta.
 *            (normalEvents.js: area[row] |= (1<<col); MD_BLK_NUM_X/Y)
 *   rect   — obdélníky. options.coordSpace:
 *              'pixel'   → [{enable,x,y,w,h}] v pixelech vůči options.refW×refH
 *                          (privátní maska, channel/privacy-mask)
 *              'percent' → [{x1,y1,x2,y2}] v procentech 0–100
 *                          (sabotáž videa, event/video-cover)
 *            options.maxRects = max. počet obdélníků.
 *   line   — úsečka o 2 bodech. value = ruleList, edituje se ruleList[ruleIdx]
 *            { x1,y1,x2,y2 } v normalizovaném prostoru 0–10000 (TEN_THOUSAND_POINTS).
 *   region — mnohoúhelník. value = ruleList, edituje se ruleList[ruleIdx]
 *            { x1..xN, y1..yN } v prostoru 0–10000; nevyužitý bod = -1.
 *            options.maxPoints = max. počet vrcholů (z firmware obvykle 4 nebo 8).
 */
(function () {
  'use strict';

  var React = window.React;
  if (!React) { return; }
  var h = React.createElement;
  var useState = React.useState;
  var useEffect = React.useEffect;
  var useRef = React.useRef;

  var TEN_THOUSAND = 10000;       // TEN_THOUSAND_POINTS — normalizační prostor
  var GRID_COLS = 22;             // MD_BLK_NUM_X
  var GRID_ROWS = 18;             // MD_BLK_NUM_Y

  // Barvy – odpovídají ladění tmavého motivu Patronum.
  var COL = {
    bg: '#11151c',
    zone: '#2BD9C8',
    zoneFill: 'rgba(43,217,200,0.25)',
    handle: '#ffffff',
    line: '#FFB020',
    grid: 'rgba(255,255,255,0.14)',
    gridOn: 'rgba(43,217,200,0.5)',
    vertex: '#2BD9C8',
    danger: '#E5484D',
  };

  // ── živé video ──────────────────────────────────────────────────────────
  // Stejný postup jako CamVideo v app.jsx: patPlayer.ready → mount → play.
  function LiveVideo(props) {
    var hostRef = useRef(null);
    var playerRef = useRef(null);
    var stateTuple = useState('init');   // init|connecting|live|error|unsupported
    var state = stateTuple[0], setState = stateTuple[1];
    var channel = props.channel;

    useEffect(function () {
      if (!window.patPlayer) { setState('unsupported'); return undefined; }
      var disposed = false;
      var handle = null;
      setState('connecting');
      window.patPlayer.ready().then(function () {
        if (disposed || !hostRef.current) return;
        handle = window.patPlayer.mount(hostRef.current, {});
        playerRef.current = handle;
        handle.on('playing', function () { if (!disposed) setState('live'); });
        handle.on('error', function () { if (!disposed) setState('error'); });
        handle.play(channel, 1).catch(function () { if (!disposed) setState('error'); });
      }).catch(function () { if (!disposed) setState('unsupported'); });
      return function () {
        disposed = true;
        if (handle) { try { handle.destroy(); } catch (e) {} }
        playerRef.current = null;
      };
    }, [channel]);

    var overlayMsg = null;
    if (state === 'connecting') overlayMsg = '● PŘIPOJOVÁNÍ KE KAMEŘE…';
    else if (state === 'error') overlayMsg = '⚠ ŽIVÉ VIDEO NEDOSTUPNÉ';
    else if (state === 'unsupported') overlayMsg = '⚠ PŘEHRÁVAČ SE NENAČETL';

    return h('div', { style: { position: 'absolute', inset: 0, background: COL.bg } },
      h('div', {
        ref: hostRef,
        style: {
          position: 'absolute', inset: 0,
          opacity: state === 'live' ? 1 : 0, transition: 'opacity .25s',
        },
      }),
      overlayMsg ? h('div', {
        style: {
          position: 'absolute', inset: 0, display: 'flex', alignItems: 'center',
          justifyContent: 'center', textAlign: 'center', padding: 10,
          fontFamily: 'ui-monospace,monospace', fontSize: 11, fontWeight: 700,
          letterSpacing: 1, color: state === 'error' || state === 'unsupported'
            ? COL.danger : COL.zone,
        },
      }, overlayMsg) : null
    );
  }

  // ── pomocné: mapování souřadnic ─────────────────────────────────────────
  function clamp(v, lo, hi) { return v < lo ? lo : (v > hi ? hi : v); }

  // pozice myši v pixelech vůči canvasu
  function evtPos(canvas, e) {
    var r = canvas.getBoundingClientRect();
    var cx = (e.touches && e.touches[0]) ? e.touches[0].clientX : e.clientX;
    var cy = (e.touches && e.touches[0]) ? e.touches[0].clientY : e.clientY;
    return {
      x: clamp(cx - r.left, 0, r.width),
      y: clamp(cy - r.top, 0, r.height),
      w: r.width, hgt: r.height,
    };
  }

  // ── režim GRID ──────────────────────────────────────────────────────────
  // value = pole GRID_ROWS řetězců; bit `col` v area[row].
  function gridFromValue(value) {
    var cells = [];
    var r, c;
    for (r = 0; r < GRID_ROWS; r++) {
      var rowVal = 0;
      if (value && value[r] != null) rowVal = parseInt(value[r], 10) || 0;
      for (c = 0; c < GRID_COLS; c++) {
        cells.push((rowVal & (1 << c)) !== 0);
      }
    }
    return cells;   // ploché pole GRID_ROWS*GRID_COLS booleanů
  }
  function gridToValue(cells) {
    var out = [];
    var r, c;
    for (r = 0; r < GRID_ROWS; r++) {
      var rowVal = 0;
      for (c = 0; c < GRID_COLS; c++) {
        if (cells[r * GRID_COLS + c]) rowVal |= (1 << c);
      }
      out.push(String(rowVal));
    }
    return out;
  }

  function GridEditor(props) {
    var canvasRef = useRef(null);
    var cellsRef = useRef(gridFromValue(props.value));
    var paintRef = useRef(null);     // hodnota, kterou se maluje (true/false)
    var redrawTuple = useState(0);
    var redraw = redrawTuple[1];

    // při změně props.value zvenčí synchronizuj
    useEffect(function () {
      cellsRef.current = gridFromValue(props.value);
      paint();
    }, [props.value]);

    function paint() {
      var cv = canvasRef.current;
      if (!cv) return;
      var ctx = cv.getContext('2d');
      var W = cv.width, H = cv.height;
      var cw = W / GRID_COLS, chh = H / GRID_ROWS;
      ctx.clearRect(0, 0, W, H);
      var cells = cellsRef.current;
      var r, c;
      for (r = 0; r < GRID_ROWS; r++) {
        for (c = 0; c < GRID_COLS; c++) {
          if (cells[r * GRID_COLS + c]) {
            ctx.fillStyle = COL.gridOn;
            ctx.fillRect(c * cw, r * chh, cw, chh);
          }
        }
      }
      ctx.strokeStyle = COL.grid;
      ctx.lineWidth = 1;
      for (c = 0; c <= GRID_COLS; c++) {
        ctx.beginPath(); ctx.moveTo(c * cw, 0); ctx.lineTo(c * cw, H); ctx.stroke();
      }
      for (r = 0; r <= GRID_ROWS; r++) {
        ctx.beginPath(); ctx.moveTo(0, r * chh); ctx.lineTo(W, r * chh); ctx.stroke();
      }
    }

    useEffect(function () { paint(); });

    function cellAt(e) {
      var cv = canvasRef.current;
      var p = evtPos(cv, e);
      var c = Math.floor(p.x / (p.w / GRID_COLS));
      var r = Math.floor(p.y / (p.hgt / GRID_ROWS));
      c = clamp(c, 0, GRID_COLS - 1);
      r = clamp(r, 0, GRID_ROWS - 1);
      return r * GRID_COLS + c;
    }

    function apply(idx) {
      var cells = cellsRef.current.slice();
      cells[idx] = paintRef.current;
      cellsRef.current = cells;
      redraw(function (n) { return n + 1; });
    }

    function down(e) {
      e.preventDefault();
      var idx = cellAt(e);
      paintRef.current = !cellsRef.current[idx];   // přepínáme dle první buňky
      apply(idx);
    }
    function move(e) {
      if (paintRef.current === null) return;
      apply(cellAt(e));
    }
    function up() {
      if (paintRef.current === null) return;
      paintRef.current = null;
      props.onChange(gridToValue(cellsRef.current));
    }

    return h('canvas', {
      ref: canvasRef,
      width: 880, height: 720,
      onMouseDown: down, onMouseMove: move, onMouseUp: up, onMouseLeave: up,
      onTouchStart: down, onTouchMove: move, onTouchEnd: up,
      style: {
        position: 'absolute', inset: 0, width: '100%', height: '100%',
        cursor: 'crosshair', touchAction: 'none', zIndex: 5,
      },
    });
  }

  // ── režim RECT ──────────────────────────────────────────────────────────
  // Interní model: pole {enable,x,y,w,h} v poměru 0..1.
  function rectsFromValue(value, opts) {
    var list = Array.isArray(value) ? value : [];
    var out = [];
    var i;
    if (opts.coordSpace === 'percent') {
      for (i = 0; i < list.length; i++) {
        var z = list[i];
        var x1 = (z.x1 || 0) / 100, y1 = (z.y1 || 0) / 100;
        var x2 = (z.x2 || 0) / 100, y2 = (z.y2 || 0) / 100;
        out.push({
          enable: true,
          x: Math.min(x1, x2), y: Math.min(y1, y2),
          w: Math.abs(x2 - x1), h: Math.abs(y2 - y1),
        });
      }
    } else {
      var rw = opts.refW || 1920, rh = opts.refH || 1080;
      for (i = 0; i < list.length; i++) {
        var p = list[i];
        out.push({
          enable: p.enable !== false && !!(p.w || p.h) || !!p.enable,
          x: (p.x || 0) / rw, y: (p.y || 0) / rh,
          w: (p.w || 0) / rw, h: (p.h || 0) / rh,
        });
      }
    }
    return out;
  }
  function rectsToValue(rects, opts) {
    var out = [];
    var i;
    if (opts.coordSpace === 'percent') {
      for (i = 0; i < rects.length; i++) {
        var r = rects[i];
        out.push({
          x1: Math.round(r.x * 100), y1: Math.round(r.y * 100),
          x2: Math.round((r.x + r.w) * 100), y2: Math.round((r.y + r.h) * 100),
        });
      }
    } else {
      var rw = opts.refW || 1920, rh = opts.refH || 1080;
      for (i = 0; i < rects.length; i++) {
        var z = rects[i];
        out.push({
          enable: !!z.enable,
          x: Math.round(z.x * rw), y: Math.round(z.y * rh),
          w: Math.round(z.w * rw), h: Math.round(z.h * rh),
        });
      }
    }
    return out;
  }

  function RectEditor(props) {
    var opts = props.options || {};
    var maxRects = opts.maxRects || 3;
    var canvasRef = useRef(null);
    var rectsRef = useRef(rectsFromValue(props.value, opts));
    var dragRef = useRef(null);   // {mode:'new'|'move'|'resize', idx, ox,oy, sx,sy}
    var redrawTuple = useState(0);
    var redraw = redrawTuple[1];
    var selTuple = useState(0);
    var sel = selTuple[0], setSel = selTuple[1];

    useEffect(function () {
      rectsRef.current = rectsFromValue(props.value, opts);
      paint();
    }, [props.value]);

    function paint() {
      var cv = canvasRef.current;
      if (!cv) return;
      var ctx = cv.getContext('2d');
      var W = cv.width, H = cv.height;
      ctx.clearRect(0, 0, W, H);
      var rects = rectsRef.current;
      for (var i = 0; i < rects.length; i++) {
        var r = rects[i];
        if (r.w <= 0 && r.h <= 0) continue;
        var x = r.x * W, y = r.y * H, w = r.w * W, hh = r.h * H;
        ctx.fillStyle = r.enable ? COL.zoneFill : 'rgba(120,120,120,0.18)';
        ctx.fillRect(x, y, w, hh);
        ctx.strokeStyle = i === sel ? COL.zone : (r.enable ? COL.zone : '#888');
        ctx.lineWidth = i === sel ? 2.5 : 1.5;
        ctx.strokeRect(x, y, w, hh);
        // úchyt v pravém dolním rohu
        ctx.fillStyle = COL.handle;
        ctx.fillRect(x + w - 6, y + hh - 6, 12, 12);
        // popisek
        ctx.fillStyle = COL.zone;
        ctx.font = '700 12px ui-monospace,monospace';
        ctx.fillText('#' + (i + 1), x + 4, y + 14);
      }
    }
    useEffect(function () { paint(); });

    function hitTest(px, py, W, H) {
      var rects = rectsRef.current;
      for (var i = rects.length - 1; i >= 0; i--) {
        var r = rects[i];
        var x = r.x * W, y = r.y * H, w = r.w * W, hh = r.h * H;
        if (px >= x + w - 9 && px <= x + w + 9 && py >= y + hh - 9 && py <= y + hh + 9) {
          return { idx: i, mode: 'resize' };
        }
        if (px >= x && px <= x + w && py >= y && py <= y + hh) {
          return { idx: i, mode: 'move' };
        }
      }
      return null;
    }

    function down(e) {
      e.preventDefault();
      var cv = canvasRef.current;
      var p = evtPos(cv, e);
      var hit = hitTest(p.x, p.y, p.w, p.hgt);
      if (hit) {
        setSel(hit.idx);
        var r = rectsRef.current[hit.idx];
        dragRef.current = {
          mode: hit.mode, idx: hit.idx,
          ox: p.x / p.w - r.x, oy: p.y / p.hgt - r.y,
        };
      } else {
        // nový obdélník — vybereme volný slot nebo přepíšeme aktuální
        var rects = rectsRef.current.slice();
        var idx = -1;
        for (var i = 0; i < rects.length; i++) {
          if (rects[i].w <= 0 && rects[i].h <= 0) { idx = i; break; }
        }
        if (idx < 0) {
          if (rects.length < maxRects) { rects.push(blankRect()); idx = rects.length - 1; }
          else { idx = sel; }
        }
        rects[idx] = { enable: true, x: p.x / p.w, y: p.y / p.hgt, w: 0, h: 0 };
        rectsRef.current = rects;
        setSel(idx);
        dragRef.current = { mode: 'new', idx: idx, sx: p.x / p.w, sy: p.y / p.hgt };
        redraw(function (n) { return n + 1; });
      }
    }
    function move(e) {
      var d = dragRef.current;
      if (!d) return;
      var cv = canvasRef.current;
      var p = evtPos(cv, e);
      var nx = p.x / p.w, ny = p.y / p.hgt;
      var rects = rectsRef.current.slice();
      var r = Object.assign({}, rects[d.idx]);
      if (d.mode === 'new') {
        r.x = Math.min(d.sx, nx); r.y = Math.min(d.sy, ny);
        r.w = Math.abs(nx - d.sx); r.h = Math.abs(ny - d.sy);
      } else if (d.mode === 'move') {
        r.x = clamp(nx - d.ox, 0, 1 - r.w);
        r.y = clamp(ny - d.oy, 0, 1 - r.h);
      } else if (d.mode === 'resize') {
        r.w = clamp(nx - r.x, 0.01, 1 - r.x);
        r.h = clamp(ny - r.y, 0.01, 1 - r.y);
      }
      rects[d.idx] = r;
      rectsRef.current = rects;
      redraw(function (n) { return n + 1; });
    }
    function up() {
      if (!dragRef.current) return;
      dragRef.current = null;
      // zahodit nulové obdélníky vzniklé pouhým kliknutím
      var rects = rectsRef.current.map(function (r) {
        if (r.w < 0.01 || r.h < 0.01) return blankRect();
        return r;
      });
      rectsRef.current = rects;
      props.onChange(rectsToValue(rects, opts));
    }
    function blankRect() { return { enable: false, x: 0, y: 0, w: 0, h: 0 }; }

    function clearSel() {
      var rects = rectsRef.current.slice();
      if (rects[sel]) rects[sel] = blankRect();
      rectsRef.current = rects;
      props.onChange(rectsToValue(rects, opts));
      redraw(function (n) { return n + 1; });
    }
    function clearAll() {
      var rects = rectsRef.current.map(function () { return blankRect(); });
      rectsRef.current = rects;
      props.onChange(rectsToValue(rects, opts));
      redraw(function (n) { return n + 1; });
    }

    return [
      h('canvas', {
        key: 'cv', ref: canvasRef, width: 880, height: 500,
        onMouseDown: down, onMouseMove: move, onMouseUp: up, onMouseLeave: up,
        onTouchStart: down, onTouchMove: move, onTouchEnd: up,
        style: {
          position: 'absolute', inset: 0, width: '100%', height: '100%',
          cursor: 'crosshair', touchAction: 'none', zIndex: 5,
        },
      }),
      h('div', {
        key: 'tools',
        style: {
          position: 'absolute', right: 8, bottom: 8, display: 'flex', gap: 6,
          zIndex: 6,
        },
      },
        toolBtn('Smazat výběr', clearSel),
        toolBtn('Smazat vše', clearAll)
      ),
    ];
  }

  // ── režim LINE ──────────────────────────────────────────────────────────
  // edituje value.ruleList[ruleIdx] { x1,y1,x2,y2 } v prostoru 0..10000
  function LineEditor(props) {
    var opts = props.options || {};
    var ruleIdx = opts.ruleIdx || 0;
    var canvasRef = useRef(null);
    var dragRef = useRef(null);   // 'p1'|'p2'|'new'
    var lineRef = useRef(lineFromValue(props.value, ruleIdx));
    var redrawTuple = useState(0);
    var redraw = redrawTuple[1];

    function lineFromValue(value, idx) {
      var rl = (value && value.ruleList) || [];
      var r = rl[idx] || {};
      return {
        x1: (r.x1 || 0) / TEN_THOUSAND, y1: (r.y1 || 0) / TEN_THOUSAND,
        x2: (r.x2 || 0) / TEN_THOUSAND, y2: (r.y2 || 0) / TEN_THOUSAND,
      };
    }

    useEffect(function () {
      lineRef.current = lineFromValue(props.value, ruleIdx);
      paint();
    }, [props.value, ruleIdx]);

    function paint() {
      var cv = canvasRef.current;
      if (!cv) return;
      var ctx = cv.getContext('2d');
      var W = cv.width, H = cv.height;
      ctx.clearRect(0, 0, W, H);
      var l = lineRef.current;
      if (l.x1 === 0 && l.y1 === 0 && l.x2 === 0 && l.y2 === 0) return;
      var x1 = l.x1 * W, y1 = l.y1 * H, x2 = l.x2 * W, y2 = l.y2 * H;
      ctx.strokeStyle = COL.line;
      ctx.lineWidth = 3;
      ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke();
      // body A a B
      drawPoint(ctx, x1, y1, 'A');
      drawPoint(ctx, x2, y2, 'B');
    }
    function drawPoint(ctx, x, y, label) {
      ctx.fillStyle = COL.handle;
      ctx.beginPath(); ctx.arc(x, y, 7, 0, Math.PI * 2); ctx.fill();
      ctx.strokeStyle = COL.line; ctx.lineWidth = 2;
      ctx.beginPath(); ctx.arc(x, y, 7, 0, Math.PI * 2); ctx.stroke();
      ctx.fillStyle = COL.line;
      ctx.font = '700 12px ui-monospace,monospace';
      ctx.fillText(label, x + 10, y - 8);
    }
    useEffect(function () { paint(); });

    function down(e) {
      e.preventDefault();
      var cv = canvasRef.current;
      var p = evtPos(cv, e);
      var l = lineRef.current;
      var nx = p.x / p.w, ny = p.y / p.hgt;
      var d1 = Math.hypot(nx - l.x1, ny - l.y1);
      var d2 = Math.hypot(nx - l.x2, ny - l.y2);
      var hasLine = !(l.x1 === 0 && l.y1 === 0 && l.x2 === 0 && l.y2 === 0);
      if (hasLine && d1 < 0.04 && d1 <= d2) { dragRef.current = 'p1'; }
      else if (hasLine && d2 < 0.04) { dragRef.current = 'p2'; }
      else {
        // nová čára
        lineRef.current = { x1: nx, y1: ny, x2: nx, y2: ny };
        dragRef.current = 'new';
        redraw(function (n) { return n + 1; });
      }
    }
    function move(e) {
      if (!dragRef.current) return;
      var cv = canvasRef.current;
      var p = evtPos(cv, e);
      var nx = p.x / p.w, ny = p.y / p.hgt;
      var l = Object.assign({}, lineRef.current);
      if (dragRef.current === 'p1') { l.x1 = nx; l.y1 = ny; }
      else { l.x2 = nx; l.y2 = ny; }
      lineRef.current = l;
      redraw(function (n) { return n + 1; });
    }
    function up() {
      if (!dragRef.current) return;
      dragRef.current = null;
      commit();
    }
    function commit() {
      var l = lineRef.current;
      var value = props.value || {};
      var rl = (value.ruleList || []).map(function (r, i) {
        if (i !== ruleIdx) return r;
        return Object.assign({}, r, {
          x1: Math.round(l.x1 * TEN_THOUSAND), y1: Math.round(l.y1 * TEN_THOUSAND),
          x2: Math.round(l.x2 * TEN_THOUSAND), y2: Math.round(l.y2 * TEN_THOUSAND),
        });
      });
      props.onChange(Object.assign({}, value, { ruleList: rl }));
    }
    function clearLine() {
      lineRef.current = { x1: 0, y1: 0, x2: 0, y2: 0 };
      redraw(function (n) { return n + 1; });
      commit();
    }

    return [
      h('canvas', {
        key: 'cv', ref: canvasRef, width: 880, height: 500,
        onMouseDown: down, onMouseMove: move, onMouseUp: up, onMouseLeave: up,
        onTouchStart: down, onTouchMove: move, onTouchEnd: up,
        style: {
          position: 'absolute', inset: 0, width: '100%', height: '100%',
          cursor: 'crosshair', touchAction: 'none', zIndex: 5,
        },
      }),
      h('div', {
        key: 'tools',
        style: { position: 'absolute', right: 8, bottom: 8, display: 'flex', gap: 6, zIndex: 6 },
      }, toolBtn('Smazat čáru', clearLine)),
    ];
  }

  // ── režim REGION ────────────────────────────────────────────────────────
  // edituje value.ruleList[ruleIdx] { x1..xN, y1..yN } v prostoru 0..10000
  function RegionEditor(props) {
    var opts = props.options || {};
    var ruleIdx = opts.ruleIdx || 0;
    var maxPoints = opts.maxPoints || 8;
    var canvasRef = useRef(null);
    var ptsRef = useRef(regionFromValue(props.value, ruleIdx, maxPoints));
    var dragRef = useRef(null);   // index vrcholu při tažení
    var redrawTuple = useState(0);
    var redraw = redrawTuple[1];

    // ruleList[idx] může nést buď x1..xN/y1..yN (-1 = nepoužito) nebo
    // pole pointList:[{x,y}] — viz původní getExtremelyData/extremelyDataToCoords.
    function regionFromValue(value, idx, maxN) {
      var rl = (value && value.ruleList) || [];
      var r = rl[idx] || {};
      var pts = [];
      if (opts.pointList && Array.isArray(r.pointList)) {
        for (var p = 0; p < r.pointList.length; p++) {
          var pt = r.pointList[p];
          if (pt && pt.x != null && pt.y != null) {
            pts.push({ x: pt.x / TEN_THOUSAND, y: pt.y / TEN_THOUSAND });
          }
        }
        return pts;
      }
      for (var i = 1; i <= maxN; i++) {
        var x = r['x' + i], y = r['y' + i];
        if (x != null && y != null && x !== -1 && y !== -1) {
          pts.push({ x: x / TEN_THOUSAND, y: y / TEN_THOUSAND });
        }
      }
      return pts;
    }

    useEffect(function () {
      ptsRef.current = regionFromValue(props.value, ruleIdx, maxPoints);
      paint();
    }, [props.value, ruleIdx]);

    function paint() {
      var cv = canvasRef.current;
      if (!cv) return;
      var ctx = cv.getContext('2d');
      var W = cv.width, H = cv.height;
      ctx.clearRect(0, 0, W, H);
      var pts = ptsRef.current;
      if (pts.length >= 2) {
        ctx.beginPath();
        ctx.moveTo(pts[0].x * W, pts[0].y * H);
        for (var i = 1; i < pts.length; i++) ctx.lineTo(pts[i].x * W, pts[i].y * H);
        if (pts.length >= 3) ctx.closePath();
        ctx.fillStyle = COL.zoneFill;
        if (pts.length >= 3) ctx.fill();
        ctx.strokeStyle = COL.zone;
        ctx.lineWidth = 2;
        ctx.stroke();
      }
      for (var j = 0; j < pts.length; j++) {
        var x = pts[j].x * W, y = pts[j].y * H;
        ctx.fillStyle = COL.handle;
        ctx.beginPath(); ctx.arc(x, y, 6, 0, Math.PI * 2); ctx.fill();
        ctx.strokeStyle = COL.vertex; ctx.lineWidth = 2;
        ctx.beginPath(); ctx.arc(x, y, 6, 0, Math.PI * 2); ctx.stroke();
        ctx.fillStyle = COL.zone;
        ctx.font = '700 11px ui-monospace,monospace';
        ctx.fillText(String(j + 1), x + 9, y - 7);
      }
    }
    useEffect(function () { paint(); });

    function vertexAt(nx, ny) {
      var pts = ptsRef.current;
      for (var i = 0; i < pts.length; i++) {
        if (Math.hypot(nx - pts[i].x, ny - pts[i].y) < 0.035) return i;
      }
      return -1;
    }

    function down(e) {
      e.preventDefault();
      var cv = canvasRef.current;
      var p = evtPos(cv, e);
      var nx = p.x / p.w, ny = p.y / p.hgt;
      var vi = vertexAt(nx, ny);
      if (vi >= 0) { dragRef.current = vi; return; }
      // přidat vrchol, je-li místo
      var pts = ptsRef.current.slice();
      if (pts.length < maxPoints) {
        pts.push({ x: nx, y: ny });
        ptsRef.current = pts;
        dragRef.current = pts.length - 1;
        redraw(function (n) { return n + 1; });
      }
    }
    function move(e) {
      if (dragRef.current === null) return;
      var cv = canvasRef.current;
      var p = evtPos(cv, e);
      var pts = ptsRef.current.slice();
      pts[dragRef.current] = { x: p.x / p.w, y: p.y / p.hgt };
      ptsRef.current = pts;
      redraw(function (n) { return n + 1; });
    }
    function up() {
      if (dragRef.current === null) return;
      dragRef.current = null;
      commit();
    }

    function commit() {
      var pts = ptsRef.current;
      var value = props.value || {};
      var rl = (value.ruleList || []).map(function (r, i) {
        if (i !== ruleIdx) return r;
        var nr = Object.assign({}, r);
        if (opts.pointList) {
          nr.pointList = pts.map(function (p) {
            return { x: Math.round(p.x * TEN_THOUSAND), y: Math.round(p.y * TEN_THOUSAND) };
          });
        } else {
          for (var k = 1; k <= maxPoints; k++) {
            if (pts[k - 1]) {
              nr['x' + k] = Math.round(pts[k - 1].x * TEN_THOUSAND);
              nr['y' + k] = Math.round(pts[k - 1].y * TEN_THOUSAND);
            } else {
              nr['x' + k] = -1; nr['y' + k] = -1;
            }
          }
        }
        return nr;
      });
      props.onChange(Object.assign({}, value, { ruleList: rl }));
    }
    function clearRegion() {
      ptsRef.current = [];
      redraw(function (n) { return n + 1; });
      commit();
    }
    function removeLast() {
      var pts = ptsRef.current.slice();
      pts.pop();
      ptsRef.current = pts;
      redraw(function (n) { return n + 1; });
      commit();
    }

    return [
      h('canvas', {
        key: 'cv', ref: canvasRef, width: 880, height: 500,
        onMouseDown: down, onMouseMove: move, onMouseUp: up, onMouseLeave: up,
        onTouchStart: down, onTouchMove: move, onTouchEnd: up,
        style: {
          position: 'absolute', inset: 0, width: '100%', height: '100%',
          cursor: 'crosshair', touchAction: 'none', zIndex: 5,
        },
      }),
      h('div', {
        key: 'tools',
        style: { position: 'absolute', right: 8, bottom: 8, display: 'flex', gap: 6, zIndex: 6 },
      },
        toolBtn('Zpět bod', removeLast),
        toolBtn('Smazat oblast', clearRegion)
      ),
    ];
  }

  // tlačítko v panelu nástrojů
  function toolBtn(label, onClick) {
    return h('button', {
      key: label,
      onClick: onClick,
      style: {
        background: 'rgba(0,0,0,0.65)', color: '#fff', border: '1px solid rgba(255,255,255,0.25)',
        borderRadius: 5, padding: '5px 10px', fontSize: 11, fontWeight: 700,
        fontFamily: 'ui-monospace,monospace', cursor: 'pointer',
      },
    }, label);
  }

  // ── nápověda dle režimu ─────────────────────────────────────────────────
  function hintFor(mode) {
    if (mode === 'grid') return 'Tažením myši zapnete nebo vypnete buňky detekční mřížky (22×18).';
    if (mode === 'rect') return 'Tažením nakreslíte obdélník; tažením uvnitř jej posunete, za bílý roh změníte velikost.';
    if (mode === 'line') return 'Tažením nakreslíte čáru; koncové body A a B lze přetahovat.';
    if (mode === 'region') return 'Klikáním přidáte vrcholy oblasti; vrcholy lze přetahovat.';
    return '';
  }

  // ── hlavní komponenta ───────────────────────────────────────────────────
  function PatAreaEditor(props) {
    var mode = props.mode || 'rect';
    var height = props.height || 320;
    var channel = props.channel != null ? props.channel : 0;

    var editor = null;
    var common = {
      channel: channel, value: props.value,
      onChange: props.onChange || function () {},
      options: props.options || {},
    };
    if (mode === 'grid') editor = h(GridEditor, common);
    else if (mode === 'rect') editor = h(RectEditor, common);
    else if (mode === 'line') editor = h(LineEditor, common);
    else if (mode === 'region') editor = h(RegionEditor, common);

    return h('div', { style: { width: '100%' } },
      h('div', {
        style: {
          position: 'relative', width: '100%', height: height,
          borderRadius: 8, overflow: 'hidden', background: COL.bg,
          border: '1px solid rgba(255,255,255,0.12)',
        },
      },
        h(LiveVideo, { channel: channel }),
        editor
      ),
      h('div', {
        style: {
          marginTop: 7, fontSize: 11.5, color: '#8b97a6', lineHeight: 1.5,
          display: 'flex', gap: 6,
        },
      },
        h('span', { style: { color: COL.zone } }, '✎'),
        h('span', null, hintFor(mode))
      )
    );
  }

  window.PatAreaEditor = PatAreaEditor;
})();
