// Wild Dragon — Blackmagic DeckLink port diagram.
//
// Renders an inline SVG that mirrors the real card's physical layout so
// operators can pick a port visually instead of guessing what "BM1" maps to.
//
// Usage:
//   const svg = BMDCards.render({
//     model: 'DeckLink Duo 2',
//     deviceCount: 4,
//     selectedIndex: 0,
//     onSelect: (i) => console.log('port', i),
//   });
//   container.appendChild(svg);
//
// Add new cards by appending to MODELS. Coordinates are in SVG user units —
// each card is drawn with the bracket on the left (matches a PC case fitting).

window.BMDCards = (function () {
  const NS = 'http://www.w3.org/2000/svg';

  // Each model entry describes the physical layout. `portPositions` is an
  // ordered list — port 0 is the first entry, matching ffmpeg's -i device index.
  const MODELS = {
    'DeckLink Duo 2': {
      friendly: 'DeckLink Duo 2',
      form: 'PCIe half-height',
      portKind: 'BNC SDI',
      bidirectional: true,
      width: 360, height: 180,
      bracket: { x: 8, y: 14, w: 40, h: 152 },
      portPositions: [
        { cx: 28, cy: 36,  label: '1', role: 'SDI 1' },
        { cx: 28, cy: 72,  label: '2', role: 'SDI 2' },
        { cx: 28, cy: 108, label: '3', role: 'SDI 3' },
        { cx: 28, cy: 144, label: '4', role: 'SDI 4' },
      ],
      notes: 'Each BNC is independently configurable as In or Out via Blackmagic Desktop Video Setup.',
    },
    'DeckLink Quad 2': {
      friendly: 'DeckLink Quad 2',
      form: 'PCIe full-height',
      portKind: 'BNC SDI',
      bidirectional: true,
      width: 360, height: 300,
      bracket: { x: 8, y: 14, w: 40, h: 272 },
      portPositions: [
        { cx: 28, cy: 36,  label: '1', role: 'SDI 1' },
        { cx: 28, cy: 70,  label: '2', role: 'SDI 2' },
        { cx: 28, cy: 104, label: '3', role: 'SDI 3' },
        { cx: 28, cy: 138, label: '4', role: 'SDI 4' },
        { cx: 28, cy: 172, label: '5', role: 'SDI 5' },
        { cx: 28, cy: 206, label: '6', role: 'SDI 6' },
        { cx: 28, cy: 240, label: '7', role: 'SDI 7' },
        { cx: 28, cy: 274, label: '8', role: 'SDI 8' },
      ],
      notes: '8 reconfigurable BNCs. Pair adjacent ports for 6G/12G-SDI dual-link.',
    },
    'DeckLink Mini Recorder 4K': {
      friendly: 'DeckLink Mini Recorder 4K',
      form: 'PCIe half-height',
      portKind: 'BNC SDI + HDMI',
      bidirectional: false,
      width: 360, height: 180,
      bracket: { x: 8, y: 14, w: 40, h: 152 },
      portPositions: [
        { cx: 28, cy: 50,  label: 'SDI', role: 'SDI In',  kind: 'sdi' },
        { cx: 28, cy: 130, label: 'HDMI', role: 'HDMI In', kind: 'hdmi' },
      ],
      notes: 'Input-only. SDI and HDMI share a single capture channel — pick which input ffmpeg should open.',
    },
    'DeckLink Mini Monitor 4K': {
      friendly: 'DeckLink Mini Monitor 4K',
      form: 'PCIe half-height',
      portKind: 'BNC SDI + HDMI',
      bidirectional: false,
      width: 360, height: 180,
      bracket: { x: 8, y: 14, w: 40, h: 152 },
      portPositions: [
        { cx: 28, cy: 50,  label: 'SDI',  role: 'SDI Out',  kind: 'sdi' },
        { cx: 28, cy: 130, label: 'HDMI', role: 'HDMI Out', kind: 'hdmi' },
      ],
      notes: 'Output-only — for playback monitoring, not capture.',
    },
    'UltraStudio 4K Mini': {
      friendly: 'UltraStudio 4K Mini',
      form: 'Thunderbolt 3',
      portKind: 'BNC SDI + HDMI',
      bidirectional: true,
      width: 360, height: 220,
      bracket: { x: 8, y: 14, w: 40, h: 192 },
      portPositions: [
        { cx: 28, cy: 44,  label: '1', role: 'SDI 1' },
        { cx: 28, cy: 84,  label: '2', role: 'SDI 2' },
        { cx: 28, cy: 124, label: '3', role: 'SDI 3' },
        { cx: 28, cy: 164, label: '4', role: 'SDI 4' },
      ],
      notes: '4 reconfigurable BNCs + HDMI on the rear.',
    },
  };

  function el(tag, attrs) {
    const node = document.createElementNS(NS, tag);
    for (const [k, v] of Object.entries(attrs || {})) {
      if (v !== undefined && v !== null) node.setAttribute(k, String(v));
    }
    return node;
  }

  function resolveModel(name) {
    if (!name) return null;
    const n = String(name).toLowerCase().trim();
    for (const [k, v] of Object.entries(MODELS)) {
      if (k.toLowerCase() === n) return { ...v, name: k };
    }
    // Loose match — accept things like "Blackmagic DeckLink Duo 2 (1)" or
    // "decklinkduo2" by stripping non-alphanumerics.
    const clean = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, '');
    const target = clean(n);
    for (const [k, v] of Object.entries(MODELS)) {
      const ck = clean(k);
      if (target.includes(ck) || ck.includes(target)) return { ...v, name: k };
    }
    return null;
  }

  function genericModel(portCount) {
    const ports = Math.max(1, portCount || 1);
    const h = 30 + ports * 36;
    return {
      friendly: 'Generic DeckLink',
      form: 'unknown',
      portKind: 'SDI',
      bidirectional: false,
      width: 360, height: h,
      bracket: { x: 8, y: 14, w: 40, h: h - 28 },
      portPositions: Array.from({ length: ports }, (_, i) => ({
        cx: 28, cy: 36 + i * 36, label: String(i + 1), role: `Port ${i + 1}`,
      })),
      notes: 'Card model not recognised — pick the port by its system index.',
    };
  }

  // Signal state → visual properties for the overlay dot.
  // Colors are CSS custom-property strings so they inherit the app theme.
  const SIGNAL_STYLE = {
    'receiving':   { fill: 'var(--success, #2dd4a8)', pulse: true  },
    'connecting':  { fill: 'var(--accent,  #5b7cfa)', pulse: true  },
    'lost':        { fill: 'var(--danger,  #f87171)', pulse: false },
    'error':       { fill: 'var(--danger,  #f87171)', pulse: false },
    'idle':        { fill: 'var(--text-3,  #7a8194)', pulse: false },
    'no-recorder': { fill: 'var(--text-4,  #4a4f61)', pulse: false },
  };

  /**
   * Render the SVG card.
   *
   * Options:
   *   model         model name (string)
   *   deviceCount   number of detected ports — used to size the generic fallback
   *   selectedIndex currently-selected port index
   *   onSelect      (index) => void
   *   compact       drop the model label + footer (default false)
   *   portSignals   Map<portIndex, signalState> — overlays a presence dot on
   *                 each port.  signalState is one of: 'receiving', 'connecting',
   *                 'lost', 'error', 'idle', 'no-recorder'.  Omit or pass null
   *                 to render the card without signal indicators.
   */
  function render({ model, deviceCount, selectedIndex, onSelect, compact, portSignals }) {
    const m = resolveModel(model) || genericModel(deviceCount || 2);

    // If we resolved a model but the cluster reports more ports than we
    // know about, fall back to a generic with the reported count so we
    // don't hide ports.
    const actualPorts = deviceCount || m.portPositions.length;
    const positions = actualPorts > m.portPositions.length
      ? genericModel(actualPorts).portPositions
      : m.portPositions.slice(0, actualPorts);

    const svg = el('svg', {
      viewBox: `0 0 ${m.width} ${m.height}`,
      class: 'bmd-card-svg',
      role: 'img',
      'aria-label': `${m.friendly} — ${actualPorts} ports`,
    });

    // PCB body (right of bracket)
    svg.appendChild(el('rect', {
      x: m.bracket.x + m.bracket.w + 6,
      y: 10,
      width: m.width - m.bracket.x - m.bracket.w - 18,
      height: m.height - 20,
      rx: 8,
      class: 'bmd-card-body',
    }));

    // Decorative trace lines on the PCB
    const traceCount = Math.min(positions.length, 6);
    for (let i = 0; i < traceCount; i++) {
      const y = 24 + i * ((m.height - 48) / traceCount);
      svg.appendChild(el('path', {
        d: `M ${m.bracket.x + m.bracket.w + 16} ${y} L ${m.width - 20} ${y}`,
        class: 'bmd-card-trace',
      }));
    }

    // Bracket (metal slot fitting)
    svg.appendChild(el('rect', {
      x: m.bracket.x, y: m.bracket.y,
      width: m.bracket.w, height: m.bracket.h,
      rx: 4,
      class: 'bmd-card-bracket',
    }));

    // Ports
    positions.forEach((p, i) => {
      const isSel = i === selectedIndex;
      const g = el('g', {
        class: 'bmd-port-group' + (isSel ? ' is-selected' : ''),
        'data-port': i,
      });
      if (typeof onSelect === 'function') {
        g.style.cursor = 'pointer';
        g.addEventListener('click', () => onSelect(i));
      }

      // BNC outer ring — bigger flange for SDI, smaller for HDMI
      const isHdmi = p.kind === 'hdmi';
      g.appendChild(el(isHdmi ? 'rect' : 'circle', isHdmi
        ? { x: p.cx - 14, y: p.cy - 6, width: 28, height: 12, rx: 1.5, class: 'bmd-port-ring' }
        : { cx: p.cx, cy: p.cy, r: 12, class: 'bmd-port-ring' }
      ));

      // Inner pin
      g.appendChild(el('circle', {
        cx: p.cx, cy: p.cy, r: isHdmi ? 1.5 : 4,
        class: 'bmd-port-pin',
      }));

      // Port label to the right of the connector
      g.appendChild(el('text', {
        x: p.cx + 24, y: p.cy - 2,
        class: 'bmd-port-label',
      })).textContent = p.label;
      g.appendChild(el('text', {
        x: p.cx + 24, y: p.cy + 11,
        class: 'bmd-port-sublabel',
      })).textContent = p.role;

      // Video-presence indicator — small dot overlaid on the BNC connector.
      // Shown whenever portSignals is provided, even for unassigned ports.
      if (portSignals) {
        const sig = portSignals instanceof Map
          ? portSignals.get(i)
          : (portSignals[i] ?? null);
        const sigKey = sig || 'no-recorder';
        const style = SIGNAL_STYLE[sigKey] || SIGNAL_STYLE['no-recorder'];

        // Dot positioned at top-right of the BNC ring so it doesn't obscure
        // the connector centre.  HDMI connectors get a slight offset.
        const dotR  = 4;
        const dotCx = isHdmi ? p.cx + 12 : p.cx + 9;
        const dotCy = isHdmi ? p.cy - 8  : p.cy - 9;

        const dot = el('circle', {
          cx: dotCx, cy: dotCy, r: dotR,
          class: 'bmd-port-signal' + (style.pulse ? ' bmd-port-signal--pulse' : ''),
          fill: style.fill,
          'data-signal': sigKey,
        });
        g.appendChild(dot);

        // Tooltip title on the group for hover text.
        const title = document.createElementNS(NS, 'title');
        title.textContent = sigKey.replace('-', ' ');
        g.appendChild(title);
      }

      svg.appendChild(g);
    });

    if (!compact) {
      // Model strip across the bottom of the PCB
      svg.appendChild(el('text', {
        x: m.bracket.x + m.bracket.w + 18,
        y: m.height - 14,
        class: 'bmd-card-model',
      })).textContent = m.friendly.toUpperCase();
    }

    svg.dataset.bmdModel = m.name || 'generic';
    svg.dataset.portCount = String(actualPorts);
    return svg;
  }

  function getModelInfo(name) {
    return resolveModel(name);
  }

  function listModels() {
    return Object.keys(MODELS);
  }

  return { render, resolveModel, getModelInfo, listModels, MODELS };
})();
