/**
 * The parametric Adom molecule nameplate.
 *
 * Give it a name's polygon geometry and it sizes and builds the whole board.
 * `boards/<slug>/index.tsx` is a five-line entrypoint; everything real is here
 * so a fix lands on every nameplate at once.
 *
 * ── The one idea ──────────────────────────────────────────────────────────
 *
 * The name IS the ground plane. A rectangular GND copper pour covers the top
 * layer and the soldermask is etched away in the shape of the name, so bare
 * plated copper shows through. There is deliberately NO second ground path —
 * no bottom ground pour — so every LED's return current physically crosses
 * the letters. "Your name is part of the electrical path that lights up the
 * LEDs" is a statement about the netlist, not a slogan. Do not add a bottom
 * ground pour "for good measure"; it would quietly make the claim false.
 *
 * ── Molecule rules (adom/molecule-design-guide, adom/base-scaffold-system) ─
 *
 *   - a large machine pin at every corner
 *   - pin box spans are exact multiples of 32 mm, the base scaffold's
 *     large-contact pitch (18 x 18 field over 576 x 576 mm)
 *   - minimal wings: WING mm from corner pin to board edge
 *   - the lower-left corner pin is the conventional molecule XY origin; the
 *     board is drawn about its centre, so that origin is (-PIN_X, -PIN_Y)
 *
 * Assembly: EVERYTHING is top side. The line does single-sided PnP, so the
 * bottom carries copper and silkscreen only.
 */

import { MachinePinLargeStandard } from "@tsci/adom-inc.library"
import { SideViewLed } from "./SideViewLed"
import { UsbCReceptacle, usbCPadRowForEdge } from "./UsbCReceptacle"
import { modelResolver } from "./model-url"

export type NamePolygon = { x: number; y: number }[]

export interface NameGeometry {
  NAME_TEXT: string
  NAME_INK_WIDTH_MM: number
  NAME_INK_HEIGHT_MM: number
  NAME_POLYGONS: NamePolygon[]
}

/**
 * Board edge to corner pin.
 *
 * Bounded from below by the pin: a 3.45 mm press-fit hole carries a 5.2 mm
 * annular ring, so 2.6 mm of the wing is already pad. 6 mm leaves 3.4 mm of
 * solid laminate outboard of the ring — about 2x board thickness, which is
 * what a press-fit needs so insertion force does not crack the edge out.
 * Do not go below ~5.5.
 */
export const WING = 6
export const SCAFFOLD_PITCH = 32
/** Clear space wanted between the ink and the corner pins, per side. */
const NAME_CLEARANCE = 12
const LED_PITCH = 20
/** 5 V through 150 R into a 3.2 V green side-view LED = 12 mA. */
const LED_SERIES_R = "150"
const R_BEHIND = 4

/** Smallest pin-box half-span, on the 32 mm grid, that clears the ink. */
export const pinXForInk = (inkWidth: number) =>
  (Math.ceil((inkWidth + 2 * NAME_CLEARANCE) / SCAFFOLD_PITCH) * SCAFFOLD_PITCH) / 2

type Led = {
  ref: string
  x: number
  y: number
  rot: 0 | 90 | 180 | 270
  schX: number
  schY: number
}

const rect = (hx: number, hy: number) => [
  { x: -hx, y: -hy },
  { x: hx, y: -hy },
  { x: hx, y: hy },
  { x: -hx, y: hy },
]

export const Nameplate = ({
  geometry,
  modelsBase,
}: {
  geometry: NameGeometry
  /** Absolute URL of this board's models/ dir. See lib/model-url.ts. */
  modelsBase: string
}) => {
  const modelUrl = modelResolver(modelsBase)
  const { NAME_TEXT, NAME_INK_WIDTH_MM, NAME_INK_HEIGHT_MM, NAME_POLYGONS } = geometry

  // ---- sizing, all derived from the ink box ----
  const PIN_X = pinXForInk(NAME_INK_WIDTH_MM)
  const PIN_Y = 32 // 64 mm apart = 2 x 32; fits a 40 mm cap height with room for the LED ring
  const BOARD_W = 2 * (PIN_X + WING)
  const BOARD_H = 2 * (PIN_Y + WING)

  // The plate is sized to physically contain EVERY ground pad, so they bond by
  // geometry and the router never touches the ground net. See the trap note in
  // the skill: left to itself the router solves `to="net.GND"` as a spanning
  // tree, daisy-chains cathode to cathode and routes the return current around
  // the plate instead of through it.
  const PLATE_X = BOARD_W / 2 - 3
  const PLATE_Y = BOARD_H / 2 - 3

  const LED_ROW_Y = 27
  const LED_END_X = PIN_X - 6
  const LED_END_Y = [-14, 14]
  const maxRowX = PIN_X - 22
  const nRow = Math.floor((2 * maxRowX) / LED_PITCH) + 1
  const rowSpan = ((nRow - 1) * LED_PITCH) / 2
  const LED_ROW_X = Array.from({ length: nRow }, (_, i) => -rowSpan + i * LED_PITCH)

  const USB_X = usbCPadRowForEdge(BOARD_W / 2)

  const leds: Led[] = [
    ...LED_ROW_X.map((x, i) => ({
      ref: `D${i + 1}`, x, y: LED_ROW_Y, rot: 180 as const,
      schX: -18 + i * 4, schY: 10,
    })),
    ...LED_END_Y.map((y, i) => ({
      ref: `D${nRow + i + 1}`, x: -LED_END_X, y, rot: 270 as const,
      schX: -18 + i * 4, schY: 2,
    })),
    ...LED_END_Y.map((y, i) => ({
      ref: `D${nRow + 3 + i}`, x: LED_END_X, y, rot: 90 as const,
      schX: -10 + i * 4, schY: 2,
    })),
  ]

  const behind = (led: Led, back: number) => {
    const rad = (led.rot * Math.PI) / 180
    return { x: led.x + back * Math.sin(rad), y: led.y - back * Math.cos(rad) }
  }
  const resistorPos = (led: Led) => behind(led, R_BEHIND)

  /**
   * The +5 V via for a string, placed so its ring OVERLAPS that resistor's
   * pin2 pad rather than sitting near it.
   *
   * Overlap, not proximity. A via is not addressable as a trace endpoint
   * (`to=".V1"` -> "Could not find port") and the router will not route to one,
   * so a via placed a polite 1.5 mm away is dead copper. Same-net copper that
   * physically touches is bonded with no router involvement — the same trick
   * the ground plate uses.
   *
   * But the via CENTRE must stay OUTSIDE the pad. Put it inside and tscircuit
   * raises `pcb_placement_error: Via is inside SMD pad` and then — this is the
   * nasty part — **skips autorouting for the entire board**, so every trace on
   * every net silently disappears. The build still exits 0. Only
   * check-connectivity.mjs catches it.
   *
   * 0402 pin2 centre is 0.51 mm from the part centre and the pad is 0.54 wide,
   * so its outer edge is at 0.78. 0.92 puts the centre 0.14 mm clear of the
   * pad while the 0.6 mm ring still laps 0.16 mm onto it.
   */
  const viaPos = (led: Led) => {
    const rp = resistorPos(led)
    const rad = (led.rot * Math.PI) / 180
    return { x: rp.x + 0.92 * Math.cos(rad), y: rp.y + 0.92 * Math.sin(rad) }
  }

  const Note = (p: { text: string; x: number; y: number; size?: number }) => (
    <silkscreentext text={p.text} pcbX={p.x} pcbY={p.y} fontSize={p.size ?? 1.5} layer="top" />
  )

  return (
    <board
      width={`${BOARD_W}mm`}
      height={`${BOARD_H}mm`}
      layers={2}
      borderRadius="4mm"
      defaultTraceWidth="0.3mm"
      autorouter="auto-local"
    >
      {/* ---- the nameplate: rectangular top-layer GND pour ---- */}
      <copperpour name="NAMEPLATE_GND" layer="top" connectsTo="net.GND"
        outline={rect(PLATE_X, PLATE_Y)} coveredWithSolderMask />

      {/*
        The name, as soldermask openings over that pour. Letters with counters
        are polygons-with-holes, which no PCB primitive expresses, so each is
        ear-clipped into simple polygons whose UNION is the letter; the counters
        are excluded by the decomposition and stay masked. clearance/margins are
        pinned to 0 so abutting pieces stay flush — at the default 0.2 mm each
        piece shrinks away from its neighbours and opens hairline gaps down the
        middle of every stroke.
      */}
      {NAME_POLYGONS.map((points, i) => (
        <copperpour key={i} layer="top" connectsTo="net.GND" outline={points}
          clearance={0} padMargin={0} traceMargin={0} boardEdgeMargin={0}
          coveredWithSolderMask={false} />
      ))}

      {/*
        Render-only decal. tscircuit's texture baker does not give exposed
        copper the bare-metal shading it gives pads — a pour and a polygon pad
        both come out flat pale green — so the name is invisible in the 3D tab
        even though the gerbers are right. This lays the SAME polygons down as
        gold geometry 45 um proud of the mask. No footprint, so no copper, no
        pads, no netlist.

        The component is parked in the bottom margin rather than at the centre
        on purpose: the adom-tsci viewer draws a ghost cuboid for a
        footprint-less part, and at (0,0) that ghost sits square on the letters
        and reads as a mystery component. positionOffset puts the ARTWORK back
        at the centre while the ghost stays out of the way.
      */}
      <chip name="ART1" pcbX={0} pcbY={-(PLATE_Y - 2.5)} schX={-30} schY={-20}
        cadModel={{ glbUrl: modelUrl("nameplate.glb"),
                    positionOffset: { x: 0, y: PLATE_Y - 2.5, z: 0 } }}
        footprint={<footprint />} />

      {/* ---- +5 V lives on the bottom layer ---- */}
      <copperpour name="VBUS_PLANE" layer="bottom" connectsTo="net.V5"
        outline={rect(BOARD_W / 2 - 2, BOARD_H / 2 - 2)} coveredWithSolderMask />

      {/* ---- corner machine pins, on the 32 mm base-scaffold grid ---- */}
      <MachinePinLargeStandard name="MP1" pcbX={-PIN_X} pcbY={-PIN_Y} schX={-30} schY={-12} />
      <MachinePinLargeStandard name="MP2" pcbX={PIN_X} pcbY={-PIN_Y} schX={-24} schY={-12} />
      <MachinePinLargeStandard name="MP3" pcbX={PIN_X} pcbY={PIN_Y} schX={-18} schY={-12} />
      <MachinePinLargeStandard name="MP4" pcbX={-PIN_X} pcbY={PIN_Y} schX={-12} schY={-12} />

      {/* ---- USB-C, east edge ---- */}
      <UsbCReceptacle name="J1" pcbX={USB_X} pcbY={0} pcbRotation={90} schX={12} schY={-12} />

      {/* Rd on both CC lines — without these a charger never enables VBUS. */}
      <resistor name="R_CC1" resistance="4.7k" footprint="0402"
        pcbX={USB_X - 5} pcbY={8} schX={22} schY={-10} />
      <resistor name="R_CC2" resistance="4.7k" footprint="0402"
        pcbX={USB_X - 5} pcbY={-8} schX={26} schY={-10} />
      <capacitor name="C1" capacitance="10uF" footprint="0603"
        pcbX={USB_X - 9.5} pcbY={6} schX={22} schY={-16} />
      <capacitor name="C2" capacitance="100nF" footprint="0402"
        pcbX={USB_X - 9.5} pcbY={-6} schX={26} schY={-16} />
      {/* Same overlap trick as the LED strings, for the connector end's +5 V
          group. pin1 of a cap sits at (pitch/2) from centre and its pad is
          `w` wide, so the pad's outer edge is at pitch/2 + w/2; putting the via
          centre 0.15 mm inside that edge guarantees a solid ring-on-pad bond
          whatever the package. */}
      {/* Same rule: centre 0.13 mm OUTSIDE the cap's pin1 outer edge, ring
          lapping onto it. 0603 pin1 edge = 0.825 + 0.4; 0402 = 0.51 + 0.27. */}
      <via name="VIA_VBUS1" pcbX={USB_X - 9.5 - (0.825 + 0.4 + 0.13)} pcbY={6}
        holeDiameter="0.3mm" outerDiameter="0.6mm" connectsTo="net.V5" />
      <via name="VIA_VBUS2" pcbX={USB_X - 9.5 - (0.51 + 0.27 + 0.13)} pcbY={-6}
        holeDiameter="0.3mm" outerDiameter="0.6mm" connectsTo="net.V5" />

      {/* ---- LED ring + series resistors, all top side ---- */}
      {leds.map((led) => (
        <SideViewLed key={led.ref} name={led.ref} pcbX={led.x} pcbY={led.y}
          pcbRotation={led.rot} schX={led.schX} schY={led.schY}
          glbUrl={modelUrl("side-view-led.glb")} />
      ))}
      {leds.map((led, i) => {
        const rp = resistorPos(led)
        return (
          <resistor key={`R${i + 1}`} name={`R${i + 1}`} resistance={LED_SERIES_R}
            footprint="0402" pcbX={rp.x} pcbY={rp.y} pcbRotation={led.rot}
            schX={led.schX} schY={led.schY - 2} />
        )
      })}
      {leds.map((led, i) => {
        const v = viaPos(led)
        return (
          <via key={`v${i}`} name={`VIA${i + 1}`} pcbX={v.x} pcbY={v.y}
            holeDiameter="0.3mm" outerDiameter="0.6mm" connectsTo="net.V5" />
        )
      })}

      {/* Per-LED silkscreen: refdes plus which way its beam actually points. */}
      {leds.map((led) => {
        const aim = led.rot === 180 ? "v" : led.rot === 0 ? "^" : led.rot === 90 ? "<" : ">"
        const t = behind(led, -2.4)
        return (
          <silkscreentext key={`s${led.ref}`} layer="top" pcbX={t.x} pcbY={t.y}
            fontSize={1.1} text={`${led.ref}${aim}`} />
        )
      })}
      {leds.map((led, i) => {
        const rp = behind(led, R_BEHIND + 2.3)
        return (
          <silkscreentext key={`sr${i}`} layer="top" pcbX={rp.x} pcbY={rp.y}
            fontSize={1.1} text={`R${i + 1} 150R`} />
        )
      })}

      {/* ---- nets ---- */}
      {leds.map((led, i) => (
        <trace key={`ta${i}`} name={`T_${led.ref}_A`}
          from={`.${led.ref} > .anode`} to={`.R${i + 1} > .pin1`} />
      ))}
      {leds.map((led, i) => (
        <trace key={`tk${i}`} name={`T_${led.ref}_K`} from={`.${led.ref} > .cathode`} to="net.GND" />
      ))}
      {leds.map((_, i) => (
        <trace key={`tr${i}`} name={`T_R${i + 1}`} from={`.R${i + 1} > .pin2`} to="net.V5" />
      ))}

      {/* Aim VBUS at the bulk cap's pad, not at "net.V5". Given a net the
          router is free to satisfy it by hopping to the nearest same-net pad —
          here it just bonded adjacent VBUS pads to each other and stopped,
          leaving the connector islanded off the rest of +5 V. A concrete
          pad-to-pad target is also the physically right answer: bulk goes
          straight to the connector. */}
      <trace name="T_VBUS1" from=".J1 > .VBUS1" to=".C1 > .pin1" />
      <trace name="T_VBUS2" from=".J1 > .VBUS2" to=".C2 > .pin1" />
      <trace name="T_GND1" from=".J1 > .GND1" to="net.GND" />
      <trace name="T_GND2" from=".J1 > .GND2" to="net.GND" />
      {/* VBUS1/2 and GND1/2 each land on two pads; the label selector resolves
          only one, so the second is wired by its A/B alias — otherwise the pour
          treats it as foreign copper and rings it instead of bonding it. */}
      <trace name="T_VBUS1B" from=".J1 > .A9" to=".C1 > .pin1" />
      <trace name="T_VBUS2B" from=".J1 > .B4" to=".C2 > .pin1" />
      <trace name="T_GND1B" from=".J1 > .A12" to="net.GND" />
      <trace name="T_GND2B" from=".J1 > .B1" to="net.GND" />
      <trace name="T_CC1A" from=".J1 > .CC1" to=".R_CC1 > .pin1" />
      <trace name="T_CC1B" from=".R_CC1 > .pin2" to="net.GND" />
      <trace name="T_CC2A" from=".J1 > .CC2" to=".R_CC2 > .pin1" />
      <trace name="T_CC2B" from=".R_CC2 > .pin2" to="net.GND" />
      <trace name="T_C1P" from=".C1 > .pin1" to="net.V5" />
      <trace name="T_C1N" from=".C1 > .pin2" to="net.GND" />
      <trace name="T_C2P" from=".C2 > .pin1" to="net.V5" />
      <trace name="T_C2N" from=".C2 > .pin2" to="net.GND" />
      <trace name="T_MP1" from=".MP1 > .pin1" to="net.GND" />
      <trace name="T_MP4" from=".MP4 > .pin1" to="net.GND" />
      <trace name="T_MP2" from=".MP2 > .pin1" to="net.V5" />
      <trace name="T_MP3" from=".MP3 > .pin1" to="net.V5" />

      {/* ---- silkscreen: say what each thing is and what it does ---- */}
      <Note text="WURTH WL-SMSW 155060GS75300 SIDE-VIEW LED — THE BEAM LEAVES THE PACKAGE SIDEWAYS AND GRAZES THE NAME" x={0} y={34.4} size={1.4} />
      <Note text="EACH LED: 150R TO 12mA, THEN A VIA DOWN TO THE +5V PLANE ON THE BACK" x={0} y={-34.4} size={1.4} />
      <Note text={`MP4 GND · LARGE MACHINE PIN · ${SCAFFOLD_PITCH}mm GRID`} x={-PIN_X + 42} y={23.5} size={1.2} />
      <Note text={`MP3 +5V · LARGE MACHINE PIN · ${SCAFFOLD_PITCH}mm GRID`} x={PIN_X - 42} y={23.5} size={1.2} />
      <Note text="MP1 GND" x={-PIN_X + 8} y={-24} size={1.2} />
      <Note text="MP2 +5V" x={PIN_X - 8} y={-24} size={1.2} />
      <Note text="J1 USB-C 5V IN" x={PIN_X - 10} y={-19} size={1.2} />
      <Note text="R_CC1/2 4K7 Rd — TELLS THE CHARGER TO ENABLE VBUS" x={PIN_X - 46} y={-21.5} size={1.2} />
      <Note text="C1 10uF BULK · C2 100nF HF" x={PIN_X - 34} y={-24.5} size={1.2} />
      <Note text={`${NAME_TEXT.toUpperCase()} IS A SOLDERMASK OPENING OVER THE TOP-LAYER GND PLANE —`} x={-26} y={20.5} size={1.4} />
      <Note text="EVERY LED RETURNS ITS CURRENT THROUGH THESE LETTERS. NO SECOND GROUND PATH." x={-22} y={-20.5} size={1.4} />

      {/* ---- back side: build block ---- */}
      <silkscreentext layer="bottom" pcbX={0} pcbY={12} fontSize={2.6}
        text={`ADOM MOLECULE NAMEPLATE — ${NAME_TEXT.toUpperCase()}`} />
      <silkscreentext layer="bottom" pcbX={0} pcbY={7} fontSize={1.7}
        text={`${NAME_INK_WIDTH_MM.toFixed(0)} x ${NAME_INK_HEIGHT_MM} mm · FAMILJEN GROTESK BOLD · BARE ENIG COPPER`} />
      <silkscreentext layer="bottom" pcbX={0} pcbY={2} fontSize={1.7}
        text={`${2 * PIN_X} x ${2 * PIN_Y} mm PIN BOX · ${SCAFFOLD_PITCH}mm BASE SCAFFOLD GRID · 4x LARGE MACHINE PIN`} />
      <silkscreentext layer="bottom" pcbX={0} pcbY={-3} fontSize={1.7}
        text="TOP = GND PLANE (THE NAME) · BOTTOM = +5V PLANE · TOP-SIDE PnP ONLY" />
      <silkscreentext layer="bottom" pcbX={0} pcbY={-8} fontSize={1.7}
        text={`${leds.length}x SIDE-VIEW LED @ 12mA = ${leds.length * 12}mA · USB-C 5V OR MP2/MP3 FROM THE SCAFFOLD`} />
      <silkscreentext layer="bottom" pcbX={0} pcbY={-13} fontSize={1.7}
        text="SPECIFY ENIG — THE LETTERS ARE BARE COPPER AND HASL WILL LEAVE THEM LUMPY" />
    </board>
  )
}
