/*
 * Texto que não cabe na caixa: medido no próprio palco (o navegador diz quanto
 * o texto ocupa com aquela fonte). A largura é medida sem quebrar palavra: uma
 * palavra maior que a caixa quebrava no meio ("CORP" / "O") e a sobra saía da
 * caixa, sumindo quando a letra é recheada com foto. A altura vale como está
 * desenhado (com as quebras de linha de verdade).
 */
import type { PecaDeTexto } from "@/lib/banner-editor/documento";

/** O campo das letras e a caixa em volta dele, da peça no palco. */
export function camposDoTexto(raiz: ParentNode | null | undefined, id: string) {
  const campo = raiz?.querySelector<HTMLElement>(`[data-peca="${CSS.escape(id)}"] [data-texto]`);
  const caixa = campo?.parentElement;
  return campo && caixa ? { campo, caixa } : null;
}

/** Quanto falta, em pixels da arte, para o texto caber (zero ou menos = cabe). */
export function faltaNoTexto(caixa: HTMLElement, campo: HTMLElement) {
  const estilo = getComputedStyle(caixa);
  const largura = caixa.clientWidth - parseFloat(estilo.paddingLeft) - parseFloat(estilo.paddingRight);
  const altura = caixa.clientHeight - parseFloat(estilo.paddingTop) - parseFloat(estilo.paddingBottom);
  const antes = campo.style.overflowWrap;
  campo.style.overflowWrap = "normal";
  const precisaLargura = campo.scrollWidth;
  campo.style.overflowWrap = antes;
  const precisaAltura = campo.offsetHeight;
  return { largura: precisaLargura - largura, altura: precisaAltura - altura, razao: Math.max(precisaLargura / Math.max(1, largura), precisaAltura / Math.max(1, altura)) };
}

/* Folga da altura: caixa justa nos modelos (a linha do texto um pouco maior que ela) não é texto que não cabe. */
export function naoCabe(p: PecaDeTexto, falta: { largura: number; altura: number }) {
  return falta.largura > 2 || falta.altura > Math.max(3, p.tamanho * p.linha * 0.3);
}

/**
 * O maior fator da letra (até 1) com que o texto cabe inteiro na caixa: busca
 * binária mudando o tamanho só no palco, sem redesenhar a peça (os trechos com
 * tamanho próprio encolhem junto), e devolve tudo como estava.
 */
export function fatorQueCabe(p: PecaDeTexto, caixa: HTMLElement, campo: HTMLElement) {
  const comTamanho = [campo, ...campo.querySelectorAll<HTMLElement>("[style*='font-size']")];
  const antes = comTamanho.map((el) => el.style.fontSize);
  const base = comTamanho.map((el) => (el.style.fontSize.endsWith("px") ? parseFloat(el.style.fontSize) : 0));
  const cabe = (f: number) => {
    comTamanho.forEach((el, i) => {
      if (base[i]) el.style.fontSize = `${base[i] * f}px`;
    });
    const falta = faltaNoTexto(caixa, campo);
    return falta.largura <= 1 && falta.altura <= 1;
  };
  let f = 1;
  if (!cabe(1)) {
    let baixo = Math.max(0.02, 6 / Math.max(6, p.tamanho));
    let alto = 1;
    for (let i = 0; i < 14; i += 1) {
      const meio = (baixo + alto) / 2;
      if (cabe(meio)) baixo = meio;
      else alto = meio;
    }
    f = baixo;
  }
  comTamanho.forEach((el, i) => {
    el.style.fontSize = antes[i];
  });
  return f;
}

/** Os campos do texto com a letra (e os trechos com tamanho próprio) no fator dado. */
export function encolherTexto(p: PecaDeTexto, f: number): Partial<PecaDeTexto> {
  return {
    encaixe: Math.max(0.05, (p.encaixe ?? 1) * f),
    tamanho: Math.max(6, p.tamanho * f),
    html: p.html ? p.html.replace(/font-size:([\d.]+)px/g, (_, v) => `font-size:${Math.max(4, Math.round(Number(v) * f * 10) / 10)}px`) : p.html,
  };
}

/** Texto que passa da caixa de propósito (letra gigante apagada, só contorno, em curva): sem aviso. */
export function textoDeEnfeite(p: PecaDeTexto) {
  return Boolean(p.curva) || p.opacidade < 0.35 || (p.pintura.tipo === "nenhuma" && !p.contorno?.largura);
}
