/*
 * A perspectiva do grupo (rodar: npx tsx --test src/lib/banner-editor/perspectiva-do-grupo.test.ts).
 */
import assert from "node:assert/strict";
import { test } from "node:test";
import { caixaDasPecas, cantosDaCaixa, cantosDoGrupo, distorcoesDoGrupo, homografiaDaCaixa, pelaHomografia, perspectivaLimpa, type Quadrilatero } from "./perspectiva-do-grupo";

const perto = (a: number, b: number, folga = 1e-6) => Math.abs(a - b) <= folga;

test("a homografia leva os cantos da caixa nos cantos pedidos", () => {
  const caixa = { x: 100, y: 50, w: 400, h: 200 };
  const cantos: Quadrilatero = [[120, 80], [520, 20], [480, 300], [90, 240]];
  const m = homografiaDaCaixa(caixa, cantos);
  cantosDaCaixa(caixa).forEach(([x, y], i) => {
    const [px, py] = pelaHomografia(m, x, y);
    assert.ok(perto(px, cantos[i][0], 1e-6) && perto(py, cantos[i][1], 1e-6), `canto ${i}: ${px},${py}`);
  });
});

test("cada peça entorta igual ao grupo, mesmo girada", () => {
  const pecas = [
    { id: "a", x: 100, y: 100, w: 200, h: 80, rot: 0 },
    { id: "b", x: 180, y: 220, w: 120, h: 120, rot: 30 },
  ];
  const caixa = caixaDasPecas(pecas);
  const cantos: Quadrilatero = [[caixa.x + 40, caixa.y], [caixa.x + caixa.w, caixa.y + 30], [caixa.x + caixa.w - 20, caixa.y + caixa.h], [caixa.x, caixa.y + caixa.h - 50]];
  const grupo = homografiaDaCaixa(caixa, cantos);
  for (const d of distorcoesDoGrupo(pecas, caixa, cantos)) {
    const p = pecas.find((q) => q.id === d.id)!;
    /* A distorção da peça (na caixa dela) e depois o lugar dela na arte, para um ponto qualquer de dentro. */
    const local = homografiaDaCaixa({ x: 0, y: 0, w: p.w, h: p.h }, d.distorcao.cantos.map(([x, y]) => [x * p.w, y * p.h]) as Quadrilatero);
    const r = (p.rot * Math.PI) / 180;
    const naArte = (lx: number, ly: number) => [p.x + p.w / 2 + (lx - p.w / 2) * Math.cos(r) - (ly - p.h / 2) * Math.sin(r), p.y + p.h / 2 + (lx - p.w / 2) * Math.sin(r) + (ly - p.h / 2) * Math.cos(r)] as const;
    for (const [lx, ly] of [[0.3, 0.6], [0.9, 0.1], [0.5, 0.5]].map(([a, b]) => [a * p.w, b * p.h])) {
      const [dx, dy] = pelaHomografia(local, lx, ly);
      const pelaPeca = naArte(dx, dy);
      const peloGrupo = pelaHomografia(grupo, ...naArte(lx, ly));
      assert.ok(perto(pelaPeca[0], peloGrupo[0], 1e-6) && perto(pelaPeca[1], peloGrupo[1], 1e-6), `${d.id}: ${pelaPeca} x ${peloGrupo}`);
    }
  }
});

test("o grupo reabre com os cantos guardados, levados para onde o grupo andou", () => {
  const pecas = [
    { id: "a", x: 0, y: 0, w: 100, h: 100, rot: 0 },
    { id: "b", x: 100, y: 0, w: 100, h: 100, rot: 0 },
  ];
  const caixa = caixaDasPecas(pecas);
  const cantos: Quadrilatero = [[10, 10], [200, 0], [190, 100], [0, 90]];
  const feitas = distorcoesDoGrupo(pecas, caixa, cantos);
  const comPerspectiva = pecas.map((p) => ({ ...p, perspectiva: feitas.find((f) => f.id === p.id)!.perspectiva }));
  assert.deepEqual(cantosDoGrupo(comPerspectiva).cantos, cantos);
  /* O grupo andou 50 para a direita. */
  const andou = comPerspectiva.map((p) => ({ ...p, x: p.x + 50 }));
  assert.deepEqual(cantosDoGrupo(andou).cantos, cantos.map(([x, y]) => [x + 50, y]));
  /* Sem perspectiva igual em todas: os cantos da caixa. */
  assert.equal(cantosDoGrupo([andou[0], pecas[1]]).tem, false);
});

test("a limpeza recusa número inválido e caixa vazia", () => {
  assert.equal(perspectivaLimpa({ caixa: { x: 0, y: 0, w: 0, h: 10 }, cantos: [[0, 0], [1, 0], [1, 1], [0, 1]] }), null);
  assert.equal(perspectivaLimpa({ caixa: { x: 0, y: 0, w: 10, h: 10 }, cantos: [[0, 0], [1, 0], [1, "x"], [0, 1]] }), null);
  assert.deepEqual(perspectivaLimpa({ caixa: { x: 0, y: 0, w: 10, h: 10 }, cantos: [[0, 0], [1, 0], [1, 1], [0, 1]] }), { caixa: { x: 0, y: 0, w: 10, h: 10 }, cantos: [[0, 0], [1, 0], [1, 1], [0, 1]] });
});
