import { solida, type Borda, type Peca, type PecaDeForma, type PecaDeTraco, type Pintura, type PontoDoTraco } from "./documento";

/*
 * Separar contorno (como o "Converter contorno em objeto" do Corel): o
 * contorno vira uma peça própria, no mesmo lugar, e sai da peça original.
 *  - "borda": só a linha do contorno, inteira (a parte de dentro da letra ou
 *    da forma fica vazia);
 *  - "cheio": a silhueta toda na cor do contorno (a letra ou a forma
 *    engordada pelo contorno, preenchida).
 * No editor, o contorno da letra e o de adesivo viram CURVAS (um desenho
 * fechado, como o "converter em curvas" do Corel: sem nenhuma ação de texto,
 * não muda quando o texto muda): `tracoDoContorno`, com as curvas que
 * silhueta.ts calcula. Sem as curvas (servidor, ou se der errado), texto
 * continua texto e o adesivo vira a mesma foto só com o contorno
 * (`silhueta`). A forma continua forma e a moldura da foto vira um retângulo
 * (ou círculo). A peça nova fica no mesmo
 * grupo, dentro da mesma peça (se a original estava dentro de outra) e com a
 * mesma animação, para continuar andando junto.
 */
export type ContornoQueSepara = "contorno" | "borda" | "adesivo" | "moldura";
export type ModoDeSeparar = "borda" | "cheio";

const temLinha = (b: Borda) => Boolean(b && b.largura > 0);

/** Os contornos da peça que dá para separar (vazio = nenhum). */
export function contornosQueSeparam(p: Peca): ContornoQueSepara[] {
  /* Letra vazada (só o contorno, sem tinta): separar deixaria a original invisível. */
  if (p.tipo === "texto") return temLinha(p.contorno) && p.pintura.tipo !== "nenhuma" ? ["contorno"] : [];
  if (p.tipo === "forma") return temLinha(p.borda) && p.pintura.tipo !== "nenhuma" ? ["borda"] : [];
  if (p.tipo === "imagem" && !p.silhueta) {
    const lista: ContornoQueSepara[] = [];
    if (p.adesivo && p.adesivo.largura > 0) lista.push("adesivo");
    /* Foto dentro de uma forma (máscara) não mostra a moldura. */
    if (temLinha(p.borda) && !(p.mascara && p.mascara !== "retangulo" && !p.circulo)) lista.push("moldura");
    return lista;
  }
  return [];
}

const semPintura = (cor: string): Pintura => ({ tipo: "nenhuma", cor, cor2: cor, angulo: 0 });

/** O nome da peça nova nas camadas. */
const nomeDoContorno = (p: Peca, padrao: string) => `Contorno · ${p.nome || padrao}`;

/* Os trechos do texto sem as cores e os contornos próprios (a peça do contorno pinta tudo de uma cor). */
function trechosSemCor(html: string | null | undefined) {
  if (!html) return html ?? null;
  return html.replace(/(?<=^|[;"\s])(?:-webkit-text-fill-color|color|-webkit-text-stroke|paint-order):[^;"]*;?/gi, "");
}

/**
 * Separa o contorno: a peça original sem ele e a peça do contorno. `acima`
 * diz se a peça nova entra por cima da original (a linha que era pintada
 * por cima: borda da forma e moldura da foto) ou por baixo (o contorno da
 * letra e o de adesivo, pintados atrás; e as silhuetas cheias, que cobririam
 * a original).
 */
export function separarContorno(p: Peca, qual: ContornoQueSepara, modo: ModoDeSeparar, id: string): { original: Peca; contorno: Peca; acima: boolean } | null {
  if (!contornosQueSeparam(p).includes(qual)) return null;
  const acima = (qual === "borda" || qual === "moldura") && modo === "borda";
  if (p.tipo === "texto" && qual === "contorno") {
    const c = p.contorno!;
    const contorno: Peca = {
      ...p,
      id,
      nome: nomeDoContorno(p, "texto"),
      pintura: modo === "borda" ? semPintura(c.cor) : solida(c.cor),
      contorno: c,
      html: trechosSemCor(p.html),
      sombra: null,
      efeitos: undefined,
      efeito: null,
      fundo: null,
      destaque: null,
      sublinhado: false,
      riscado: false,
    };
    return { original: { ...p, contorno: null }, contorno, acima };
  }
  if (p.tipo === "forma" && qual === "borda") {
    const b = p.borda!;
    const contorno: Peca = { ...p, id, nome: nomeDoContorno(p, "forma"), pintura: modo === "borda" ? semPintura(b.cor) : solida(b.cor), borda: b, sombra: null, efeitos: undefined };
    return { original: { ...p, borda: null }, contorno, acima };
  }
  if (p.tipo === "imagem" && qual === "adesivo") {
    const contorno: Peca = { ...p, id, nome: nomeDoContorno(p, "foto"), silhueta: modo === "borda" ? "borda" : "cheia", borda: null, sombra: null, efeitos: undefined, moldura: undefined };
    return { original: { ...p, adesivo: null }, contorno, acima };
  }
  if (p.tipo === "imagem" && qual === "moldura") {
    const b = p.borda!;
    const contorno: PecaDeForma = {
      id,
      tipo: "forma",
      nome: nomeDoContorno(p, "foto"),
      x: p.x,
      y: p.y,
      w: p.w,
      h: p.h,
      rot: p.rot,
      opacidade: p.opacidade,
      forma: p.circulo ? "circulo" : "retangulo",
      pintura: modo === "borda" ? semPintura(b.cor) : solida(b.cor),
      raio: p.raio,
      borda: b,
      sombra: null,
      pontas: 16,
      profundidade: 0.2,
      ...(p.grupo ? { grupo: p.grupo } : {}),
      ...(p.dentroDe ? { dentroDe: p.dentroDe } : {}),
      ...(p.animacao ? { animacao: p.animacao } : {}),
      ...(p.fixa ? { fixa: p.fixa } : {}),
      ...(p.distorcao ? { distorcao: p.distorcao } : {}),
      ...(p.perspectiva ? { perspectiva: p.perspectiva } : {}),
    };
    return { original: { ...p, borda: null }, contorno, acima };
  }
  return null;
}

/**
 * O contorno separado em curvas: o desenho fechado (traço) com os contornos
 * dados (px na caixa da peça, podem passar dela), pintado da cor do
 * contorno, exatamente no lugar da peça (girado como ela; o espelho já vai
 * nas curvas). A caixa nova é a dos contornos, com o centro levado pelo giro.
 */
export function tracoDoContorno(p: Peca, contornos: PontoDoTraco[][], cor: string, id: string, jaEspelhado = false): PecaDeTraco | null {
  /* A letra vira inteira com o espelho da peça (as curvas saem sem ele); a foto já sai espelhada no próprio desenho. */
  const espelhar = (q: { x: number; y: number }) => (jaEspelhado ? q : { x: p.espelharX ? p.w - q.x : q.x, y: p.espelharY ? p.h - q.y : q.y });
  const lista = contornos.filter((c) => c.length >= 3).map((c) => c.map((q) => ({ ...q, ...espelhar(q), ...(q.e ? { e: espelhar(q.e) } : {}), ...(q.s ? { s: espelhar(q.s) } : {}) })));
  if (!lista.length) return null;
  let x0 = Infinity;
  let y0 = Infinity;
  let x1 = -Infinity;
  let y1 = -Infinity;
  for (const c of lista)
    for (const q of c)
      for (const v of [q, q.e, q.s]) {
        if (!v) continue;
        x0 = Math.min(x0, v.x);
        y0 = Math.min(y0, v.y);
        x1 = Math.max(x1, v.x);
        y1 = Math.max(y1, v.y);
      }
  const w = Math.max(1, x1 - x0);
  const h = Math.max(1, y1 - y0);
  /* O centro da caixa nova, girado em volta do centro da peça. */
  const r = (p.rot * Math.PI) / 180;
  const dx = x0 + w / 2 - p.w / 2;
  const dy = y0 + h / 2 - p.h / 2;
  const cx = p.x + p.w / 2 + dx * Math.cos(r) - dy * Math.sin(r);
  const cy = p.y + p.h / 2 + dx * Math.sin(r) + dy * Math.cos(r);
  const fracao = (v: { x: number; y: number }) => ({ x: Math.round(((v.x - x0) / w) * 1e5) / 1e5, y: Math.round(((v.y - y0) / h) * 1e5) / 1e5 });
  const [pontos, ...resto] = lista.map((c) => c.map((q) => ({ ...fracao(q), ...(q.e ? { e: fracao(q.e) } : {}), ...(q.s ? { s: fracao(q.s) } : {}) })));
  return {
    id,
    tipo: "traco",
    nome: nomeDoContorno(p, p.tipo === "texto" ? "texto" : "foto"),
    x: Math.round((cx - w / 2) * 100) / 100,
    y: Math.round((cy - h / 2) * 100) / 100,
    w: Math.round(w * 100) / 100,
    h: Math.round(h * 100) / 100,
    rot: p.rot,
    opacidade: p.opacidade,
    pontos,
    ...(resto.length ? { contornos: resto } : {}),
    fechado: true,
    suave: false,
    cor,
    espessura: 0,
    tracejado: "solido",
    inicio: "nada",
    fim: "nada",
    preenchimento: solida(cor),
    sombra: null,
    ...(p.grupo ? { grupo: p.grupo } : {}),
    ...(p.dentroDe ? { dentroDe: p.dentroDe } : {}),
    ...(p.animacao ? { animacao: p.animacao } : {}),
    ...(p.fixa ? { fixa: p.fixa } : {}),
  };
}
