import { geometria, solida, type PecaDeForma, type PecaDeTraco, type PontoDoTraco } from "./documento";

/*
 * "Converter em curvas" (o Ctrl+Q do Corel): a forma pronta vira um traço
 * fechado com nós, para mexer ponto a ponto com a ferramenta Forma. Usa o
 * mesmo desenho da forma (geometria do documento, numa caixa de 100 × 100):
 * polígonos viram nós retos; caminhos com curvas (C, Q, T) viram nós com
 * alças. Arcos (gota, semicírculo, anel) e os desenhos sorteados (rasgado,
 * fita) ficam de fora.
 */

const KAPPA = 0.5523;

/** Os nós do caminho SVG (só comandos absolutos M L H V C Q T Z, como os das formas). */
function nosDoCaminho(d: string): PontoDoTraco[] | null {
  const partes = d.match(/[MLHVCQTZ]|-?\d*\.?\d+/gi);
  if (!partes) return null;
  const nos: PontoDoTraco[] = [];
  let i = 0;
  let comando = "";
  let ultimoQ: { x: number; y: number } | null = null;
  const numero = () => Number(partes[i++]) / 100;
  while (i < partes.length) {
    if (/[A-Za-z]/.test(partes[i])) comando = partes[i++].toUpperCase();
    const atual = nos.at(-1);
    if (comando === "Z") break;
    if (/[^MLHVCQT]/.test(comando)) return null;
    if (comando === "M" || comando === "L") {
      nos.push({ x: numero(), y: numero() });
      ultimoQ = null;
    } else if (comando === "H" && atual) {
      nos.push({ x: numero(), y: atual.y });
      ultimoQ = null;
    } else if (comando === "V" && atual) {
      nos.push({ x: atual.x, y: numero() });
      ultimoQ = null;
    } else if (comando === "C" && atual) {
      const c1 = { x: numero(), y: numero() };
      const c2 = { x: numero(), y: numero() };
      nos[nos.length - 1] = { ...atual, s: c1 };
      nos.push({ x: numero(), y: numero(), e: c2 });
      ultimoQ = null;
    } else if ((comando === "Q" || comando === "T") && atual) {
      /* Quadrática vira cúbica: as alças a 2/3 do caminho até o ponto de controle. */
      const q: { x: number; y: number } = comando === "Q" ? { x: numero(), y: numero() } : ultimoQ ? { x: 2 * atual.x - ultimoQ.x, y: 2 * atual.y - ultimoQ.y } : { x: atual.x, y: atual.y };
      const fim = { x: numero(), y: numero() };
      nos[nos.length - 1] = { ...atual, s: { x: atual.x + (2 / 3) * (q.x - atual.x), y: atual.y + (2 / 3) * (q.y - atual.y) } };
      nos.push({ ...fim, e: { x: fim.x + (2 / 3) * (q.x - fim.x), y: fim.y + (2 / 3) * (q.y - fim.y) } });
      ultimoQ = q;
    } else return null;
  }
  /* O último nó em cima do primeiro: vira um só (a alça de entrada fica no primeiro). */
  const primeiro = nos[0];
  const ultimo = nos.at(-1);
  if (nos.length > 2 && primeiro && ultimo && Math.hypot(primeiro.x - ultimo.x, primeiro.y - ultimo.y) < 1e-6) {
    nos[0] = { ...primeiro, e: ultimo.e ?? primeiro.e };
    nos.pop();
  }
  return nos.length >= 3 ? nos : null;
}

/** Retângulo com cantos arredondados: 8 nós (2 por canto) com as alças do quarto de círculo. */
function retangulo(raio: number, w: number, h: number): PontoDoTraco[] {
  const rx = Math.min(raio, w / 2) / w;
  const ry = Math.min(raio, h / 2) / h;
  if (rx <= 0 || ry <= 0) return [{ x: 0, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: 1 }];
  const kx = rx * KAPPA;
  const ky = ry * KAPPA;
  return [
    { x: rx, y: 0, e: { x: rx - kx, y: 0 } },
    { x: 1 - rx, y: 0, s: { x: 1 - rx + kx, y: 0 } },
    { x: 1, y: ry, e: { x: 1, y: ry - ky } },
    { x: 1, y: 1 - ry, s: { x: 1, y: 1 - ry + ky } },
    { x: 1 - rx, y: 1, e: { x: 1 - rx + kx, y: 1 } },
    { x: rx, y: 1, s: { x: rx - kx, y: 1 } },
    { x: 0, y: 1 - ry, e: { x: 0, y: 1 - ry + ky } },
    { x: 0, y: ry, s: { x: 0, y: ry - ky } },
  ];
}

function circulo(): PontoDoTraco[] {
  const k = 0.5 * KAPPA;
  return [
    { x: 0.5, y: 0, e: { x: 0.5 - k, y: 0 }, s: { x: 0.5 + k, y: 0 }, no: "suave" },
    { x: 1, y: 0.5, e: { x: 1, y: 0.5 - k }, s: { x: 1, y: 0.5 + k }, no: "suave" },
    { x: 0.5, y: 1, e: { x: 0.5 + k, y: 1 }, s: { x: 0.5 - k, y: 1 }, no: "suave" },
    { x: 0, y: 0.5, e: { x: 0, y: 0.5 + k }, s: { x: 0, y: 0.5 - k }, no: "suave" },
  ];
}

/** Os nós da forma (fração da caixa), ou null quando ela não dá para converter. */
export function nosDaForma(p: Pick<PecaDeForma, "forma" | "raio" | "pontas" | "profundidade" | "w" | "h">): PontoDoTraco[] | null {
  if (p.forma === "retangulo") return retangulo(p.raio, p.w, p.h);
  if (p.forma === "circulo") return circulo();
  if (p.forma === "rasgado" || p.forma === "fita" || p.forma === "gota" || p.forma === "semicirculo" || p.forma === "anel") return null;
  const desenho = geometria(p.forma, p.pontas, p.profundidade);
  const poligono = /points="([^"]+)"/.exec(desenho);
  if (poligono) {
    const nos = poligono[1]
      .trim()
      .split(/\s+/)
      .map((par) => par.split(",").map(Number))
      .filter((par) => par.length === 2 && par.every(Number.isFinite))
      .map(([x, y]) => ({ x: x / 100, y: y / 100 }));
    return nos.length >= 3 ? nos : null;
  }
  const caminho = /\sd="([^"]+)"/.exec(desenho);
  return caminho ? nosDoCaminho(caminho[1]) : null;
}

export function daParaConverter(p: PecaDeForma) {
  return nosDaForma(p) !== null;
}

/** A forma virando traço fechado: mesma caixa, giro, espelho, cor, borda e sombra. */
export function formaEmCurvas(p: PecaDeForma): PecaDeTraco | null {
  const pontos = nosDaForma(p);
  if (!pontos) return null;
  const borda = p.borda && p.borda.largura > 0 ? p.borda : null;
  return {
    id: p.id,
    tipo: "traco",
    nome: p.nome ? `${p.nome} (curvas)` : "Forma em curvas",
    x: p.x,
    y: p.y,
    w: p.w,
    h: p.h,
    rot: p.rot,
    opacidade: p.opacidade,
    ...(p.espelharX ? { espelharX: true } : {}),
    ...(p.espelharY ? { espelharY: true } : {}),
    ...(p.transparencia ? { transparencia: p.transparencia } : {}),
    ...(p.grupo ? { grupo: p.grupo } : {}),
    pontos,
    fechado: true,
    suave: false,
    cor: borda?.cor ?? "#141414",
    espessura: borda?.largura ?? 0,
    tracejado: "solido",
    inicio: "nada",
    fim: "nada",
    preenchimento: p.pintura.tipo === "nenhuma" ? solida("#FFD400") : p.pintura,
    sombra: p.sombra,
  };
}
