/* kit/vox.jsx — os oito dispositivos tipográficos do estilo VOX.
 *
 * "A tipografia da VOX não é legenda: é cenografia." Cada peça é um
 * dispositivo, e cada um tem um comportamento próprio que significa algo:
 *
 *   palavra     palavra ancorada: carimba no beat, a tese ganha o acento e o sublinhado
 *   lista       termo — definição; o item novo entra e o anterior apaga para cinza
 *   letra       highlight na letra: dentro da manchete, uma palavra muda de cor
 *   especime    o termo em tracking largo, à máquina, com o sub-rótulo etimológico
 *   pilula      nome e subtítulo pousando na arte, ligados por uma linha que se desenha
 *   fonte       a fonte do documento em mono, com a barra de acento
 *   marcatexto  o documento lido em cascata; o marca-texto varre, a caneta circula
 *   textura     a mesma linha repetida até o documento virar fundo
 *   barras      o gráfico em tinta: barras que pousam, a maior circulada
 *
 * As coordenadas (x, y, largura) são da cena, em px. O tempo (t, de cada
 * item) é o do elemento, em segundos — o instante em que a palavra é DITA. */
import React from 'react';
import {
  carimbo, mola, entre, lerp, acaso, Traco, elipseMao, sublinhadoMao, familia, MONO,
  CURVAS_KIT, mistura, alfa, SOMBRA_CONTATO,
} from './base';

const TINTA = (el) => (el.tinta === false ? undefined : 'url(#pc-tinta)');

/* O traço de caneta que cabe em qualquer caixa: SVG esticado (preserveAspectRatio
   none) com traço que não estica (non-scaling-stroke). É assim que o
   sublinhado e o círculo abraçam uma palavra cuja largura só o navegador sabe. */
function Caneta({ d, p, cor, espessura = 5, inset = '0', opacidade = 1 }) {
  if (p <= 0) return null;
  // O SVG é elemento substituído: "inset" sozinho não o estica. Quem estica é a caixa em volta.
  return (
    <div style={{ position: 'absolute', inset, pointerEvents: 'none' }}>
      <svg viewBox="0 0 100 100" preserveAspectRatio="none" width="100%" height="100%" style={{ overflow: 'visible', display: 'block' }}>
        <path d={d} pathLength={1} fill="none" stroke={cor} strokeWidth={espessura} strokeLinecap="round" strokeLinejoin="round"
          vectorEffect="non-scaling-stroke" strokeDasharray="1 1" strokeDashoffset={1 - Math.min(1, p)} opacity={opacidade} />
      </svg>
    </div>
  );
}
const SUBLINHA = sublinhadoMao(0, 50, 100, 2);
const CIRCULO = elipseMao(50, 50, 50, 50, 4);
// O traço da mão: 10 a 16 quadros, rápido no começo e arrastando no fim.
const traco = (t, de, fps, quadros = 13) => CURVAS_KIT.arrasto(entre(t, de, de + quadros / fps));

// ── palavra ancorada ─────────────────────────────────────────────────────
export function Palavra({ el, t, fps }) {
  const ws = el.palavras || [];
  const tam = el.tamanho || 120;
  const centro = el.alinhamento === 'center';
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, width: el.largura, transform: centro ? 'translateX(-50%)' : undefined,
      display: 'flex', flexWrap: 'wrap', alignItems: 'baseline', justifyContent: centro ? 'center' : 'flex-start',
      columnGap: '0.24em', rowGap: '0.02em', fontFamily: familia(el.fonte), fontSize: tam, lineHeight: el.entrelinha || 1.02,
      color: el.cor || '#141414', letterSpacing: '-0.02em' }}>
      {ws.map((w, i) => {
        const cor = w.acento ? (el.corAcento || '#d8342b') : (w.cor || el.cor || '#141414');
        const pSub = w.acento && el.sublinhar !== false ? traco(t, w.t + 5 / fps, fps, 14) : 0;
        return (
          <React.Fragment key={i}>
            {w.quebra ? <div style={{ flexBasis: '100%', height: 0 }} /> : null}
            <span style={{ display: 'inline-block', position: 'relative', fontWeight: w.peso || (w.acento ? 800 : 600),
              fontSize: w.escala ? w.escala + 'em' : undefined, color: cor, transformOrigin: '50% 80%', filter: TINTA(el),
              ...carimbo(t, w.t, fps, i + 3, w.acento ? 1 : 0.7) }}>
              {w.texto}
              <Caneta d={SUBLINHA} p={pSub} cor={el.corAcento || '#d8342b'} espessura={Math.max(4, tam * 0.06)} inset="0.84em -0.04em -0.08em -0.04em" />
            </span>
          </React.Fragment>
        );
      })}
    </div>
  );
}

// ── lista com travessão ──────────────────────────────────────────────────
export function Lista({ el, t, fps }) {
  const itens = el.itens || [];
  const tam = el.tamanho || 72;
  const velha = el.corVelha || '#b9b4aa';
  // O "agora" é o último item que já entrou: a hierarquia é do agora, não do primeiro.
  let agora = -1;
  itens.forEach((it, i) => { if (t >= it.t) agora = i; });
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, width: el.largura, fontFamily: familia(el.fonte), fontSize: tam,
      lineHeight: 1.08, color: el.cor || '#141414' }}>
      {itens.map((it, i) => {
        // O anterior perde cor em poucos quadros: não sai de cena, é rebaixado.
        const idade = i < agora ? entre(t, itens[i + 1].t, itens[i + 1].t + 6 / fps) : 0;
        const cor = mistura(el.cor || '#141414', velha, idade);
        const corTermo = i === agora && !it.riscar ? (el.corAcento || '#d8342b') : cor;
        const pRisco = it.riscar ? traco(t, it.t + 0.35, fps, 12) : 0;
        return (
          <div key={i} style={{ position: 'relative', display: 'block', marginBottom: '0.34em', transformOrigin: '0% 70%', ...carimbo(t, it.t, fps, i + 11, 0.6) }}>
            <span style={{ position: 'relative', display: 'inline-block', filter: TINTA(el) }}>
              <span style={{ fontWeight: 800, color: corTermo, letterSpacing: '-0.02em' }}>{it.termo}</span>
              {it.def ? <span style={{ fontWeight: 300, color: cor }}>{' — ' + it.def}</span> : null}
              <Caneta d="M 0 55 C 30 50 70 58 100 48" p={pRisco} cor={el.corAcento || '#d8342b'} espessura={Math.max(4, tam * 0.07)} inset="0 -0.1em" />
            </span>
            {it.nota ? <div style={{ fontFamily: MONO(el.fonteMono), fontSize: tam * 0.26, letterSpacing: '0.08em', color: velha, marginTop: '0.3em',
              opacity: t >= it.t + 0.2 ? 1 : 0 }}>{it.nota}</div> : null}
          </div>
        );
      })}
    </div>
  );
}

// ── highlight na letra ───────────────────────────────────────────────────
export function Letra({ el, t, fps }) {
  const ws = String(el.texto || '').split(/\s+/).filter(Boolean);
  const alvo = String(el.destaque || '').toLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
  const passo = (el.passo || 4) / fps;
  const t0 = el.t || 0;
  const tD = el.tDestaque != null ? el.tDestaque : t0 + ws.length * passo + 0.25;
  const tam = el.tamanho || 96;
  const centro = el.alinhamento === 'center';
  const direita = el.alinhamento === 'right';
  // x é a borda de onde o texto nasce: a esquerda, o centro ou a direita.
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, width: el.largura, transform: centro ? 'translateX(-50%)' : (direita ? 'translateX(-100%)' : undefined),
      textAlign: centro ? 'center' : (direita ? 'right' : 'left'), fontFamily: familia(el.fonte), fontSize: tam, lineHeight: 1.04, letterSpacing: '-0.025em',
      color: el.cor || '#141414' }}>
      {ws.map((w, i) => {
        const chave = w.toLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
        const ehAlvo = alvo && chave === alvo;
        const aceso = ehAlvo && t >= tD;
        const leve = w.length <= 3 && !ehAlvo;
        return (
          <React.Fragment key={i}>
            <span style={{ display: 'inline-block', position: 'relative', fontWeight: ehAlvo ? 800 : (leve ? 300 : 600),
              color: aceso ? (el.corAcento || '#d8342b') : undefined, textTransform: ehAlvo && el.caixaAlta ? 'uppercase' : undefined,
              transformOrigin: '50% 80%', filter: TINTA(el), ...carimbo(t, t0 + i * passo, fps, i + 21, 0.45) }}>
              {w}
              <Caneta d={SUBLINHA} p={ehAlvo ? traco(t, tD + 2 / fps, fps, 14) : 0} cor={el.corAcento || '#d8342b'} espessura={Math.max(4, tam * 0.07)}
                inset="0.84em -0.02em -0.08em -0.02em" />
            </span>
            {i < ws.length - 1 ? ' ' : null}
          </React.Fragment>
        );
      })}
    </div>
  );
}

// ── cartão-espécime ──────────────────────────────────────────────────────
export function Especime({ el, t, fps }) {
  const termo = String(el.termo || '').toUpperCase();
  const t0 = el.t || 0;
  const porLetra = 2 / fps;                       // a máquina de escrever: uma letra a cada 2 quadros
  const n = Math.max(0, Math.min(termo.length, Math.floor((t - t0) / porLetra) + 1));
  const fimTermo = t0 + termo.length * porLetra;
  const sub = String(el.sub || '');
  const nSub = Math.max(0, Math.min(sub.length, Math.floor((t - fimTermo - 0.25) * fps * 1.5)));
  const cursor = t < fimTermo + 0.6 && Math.floor(t * fps / 8) % 2 === 0;
  const tam = el.tamanho || 150;
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, transform: 'translate(-50%, -50%)', textAlign: 'center', whiteSpace: 'nowrap' }}>
      <div style={{ fontFamily: familia(el.fonte), fontWeight: 700, fontSize: tam, letterSpacing: '0.42em', marginRight: '-0.42em',
        color: el.cor || '#f2efe8', lineHeight: 1, filter: TINTA(el) }}>
        <span>{termo.slice(0, n)}</span>
        <span style={{ opacity: 0 }}>{termo.slice(n)}</span>
        <span style={{ position: 'absolute', marginLeft: '-0.36em', opacity: cursor ? 1 : 0, fontWeight: 300 }}>|</span>
      </div>
      {sub ? (
        <div style={{ fontFamily: MONO(el.fonteMono), fontSize: Math.max(18, tam * 0.16), letterSpacing: '0.1em', marginTop: tam * 0.42,
          color: el.corSub || alfa(el.cor || '#f2efe8', 0.6) }}>
          {sub.slice(0, nSub)}<span style={{ opacity: 0 }}>{sub.slice(nSub)}</span>
        </div>
      ) : null}
    </div>
  );
}

// ── pill branca + linha-guia ─────────────────────────────────────────────
export function Pilula({ el, t, fps }) {
  const t0 = el.t || 0;
  const tam = el.tamanho || 44;
  const alvo = el.alvo || { x: el.x, y: el.y - tam * 4 };
  const pLinha = traco(t, t0, fps, 12);
  const tPill = t0 + 10 / fps;
  // A linha vai do detalhe até a borda de cima da pill: ela pousa no fim da linha.
  const topo = el.y - tam * 0.95;
  return (
    <>
      <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible', pointerEvents: 'none' }}>
        <Traco d={'M ' + alvo.x + ' ' + alvo.y + ' L ' + el.x + ' ' + topo} p={pLinha} cor={el.corLinha || '#1a1a1a'} espessura={2} />
        {t >= t0 ? <circle cx={alvo.x} cy={alvo.y} r={6 * (0.4 + 0.6 * mola(t, t0, fps))} fill={el.corLinha || '#1a1a1a'} /> : null}
      </svg>
      <div style={{ position: 'absolute', left: el.x, top: el.y, transform: 'translate(-50%, -50%)', textAlign: 'center' }}>
        <div style={{ display: 'inline-block', background: el.cor || '#ffffff', color: el.corTexto || '#111', borderRadius: tam * 0.32,
          padding: '0.34em 0.8em', fontFamily: familia(el.fonte), fontWeight: 800, fontSize: tam, letterSpacing: '0.04em',
          textTransform: 'uppercase', boxShadow: SOMBRA_CONTATO, whiteSpace: 'nowrap', transformOrigin: '50% 100%', ...carimbo(t, tPill, fps, 31, 0.6) }}>
          {el.texto}
        </div>
        {el.sub ? (
          <div style={{ marginTop: tam * 0.3, display: 'inline-block', background: alfa('#ffffff', 0.86), borderRadius: 6, padding: '0.3em 0.7em',
            fontFamily: MONO(el.fonteMono), fontSize: tam * 0.42, letterSpacing: '0.06em', color: '#3a3a3a', whiteSpace: 'nowrap',
            opacity: t >= tPill + 5 / fps ? 1 : 0 }}>{el.sub}</div>
        ) : null}
      </div>
    </>
  );
}

// ── source tag ───────────────────────────────────────────────────────────
export function Fonte({ el, t, fps }) {
  const t0 = el.t || 0;
  const tam = el.tamanho || 22;
  const txt = String(el.texto || '');
  const pBarra = CURVAS_KIT.editorial(entre(t, t0, t0 + 6 / fps));
  const n = Math.max(0, Math.min(txt.length, Math.floor((t - t0 - 4 / fps) * fps * 1.6)));
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, display: 'flex', alignItems: 'stretch', gap: tam * 0.6,
      transform: el.alinhamento === 'right' ? 'translateX(-100%)' : undefined }}>
      <div style={{ width: Math.max(3, tam * 0.18), background: el.corAcento || '#d8342b', transform: 'scaleY(' + pBarra + ')', transformOrigin: '50% 0%' }} />
      <div style={{ fontFamily: MONO(el.fonteMono), fontSize: tam, letterSpacing: '0.08em', lineHeight: 1.35, color: el.cor || '#4a4740',
        textTransform: 'uppercase', whiteSpace: 'pre', padding: '0.1em 0' }}>
        {txt.slice(0, n)}<span style={{ opacity: 0 }}>{txt.slice(n)}</span>
      </div>
    </div>
  );
}

// ── marca-texto e caneta ─────────────────────────────────────────────────
export function MarcaTexto({ el, t, fps }) {
  const linhas = el.linhas || [];
  const t0 = el.t || 0;
  const passo = 4 / fps;
  const alvo = el.marca == null ? -1 : el.marca;
  const tMarca = el.tMarca != null ? el.tMarca : t0 + linhas.length * passo + 0.3;
  const tam = el.tamanho || 44;
  const pMarca = CURVAS_KIT.arrasto(entre(t, tMarca, tMarca + 12 / fps));
  const pCirculo = traco(t, tMarca + 14 / fps, fps, 15);
  const papel = el.fundo || '#f7f4ee';
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, width: el.largura, background: papel, boxShadow: SOMBRA_CONTATO,
      padding: tam * 1.1 + 'px ' + tam * 1.2 + 'px', transform: 'rotate(' + (el.rotacao ?? -0.6) + 'deg)', transformOrigin: '50% 50%',
      opacity: t >= t0 - 0.05 ? 1 : 0 }}>
      {el.titulo ? (
        <div style={{ fontFamily: MONO(el.fonteMono), fontSize: tam * 0.42, letterSpacing: '0.16em', color: '#8a8378', textTransform: 'uppercase',
          borderBottom: '1px solid #d6d0c4', paddingBottom: tam * 0.4, marginBottom: tam * 0.6 }}>{el.titulo}</div>
      ) : null}
      {linhas.map((ln, i) => {
        const tL = t0 + i * passo;
        // A linha é impressa da esquerda para a direita, em 5 quadros: o documento é LIDO.
        const pL = CURVAS_KIT.editorial(entre(t, tL, tL + 5 / fps));
        const ehAlvo = i === alvo;
        return (
          <div key={i} style={{ position: 'relative', fontFamily: familia(el.fonte, 'Georgia, serif'), fontSize: tam, lineHeight: 1.36,
            color: el.cor || '#23201b' }}>
            <span style={{ position: 'relative', display: 'inline-block' }}>
              {ehAlvo ? <span style={{ position: 'absolute', left: '-0.12em', top: '0.12em', bottom: '0.06em', width: 'calc(100% + 0.24em)',
                background: el.corMarca || alfa(el.corAcento || '#d8342b', 0.32), transform: 'scaleX(' + pMarca + ')', transformOrigin: '0% 50%',
                mixBlendMode: 'multiply', borderRadius: '0.1em' }} /> : null}
              {/* O recorte que imprime a linha fica no TEXTO: no bloco, cortaria o círculo da caneta. */}
              <span style={{ position: 'relative', display: 'inline-block', clipPath: 'inset(-0.2em ' + (100 - pL * 100).toFixed(1) + '% -0.2em 0)', filter: TINTA(el) }}>{ln}</span>
              {ehAlvo ? <Caneta d={CIRCULO} p={pCirculo} cor={el.corAcento || '#d8342b'} espessura={Math.max(3, tam * 0.08)} inset="-0.32em -0.55em -0.28em -0.5em" /> : null}
            </span>
          </div>
        );
      })}
    </div>
  );
}

// ── texto-textura ────────────────────────────────────────────────────────
export function Textura({ el, t, fps }) {
  const W = el.largura || 1920, H = el.altura || 1080;
  const tam = el.tamanho || 96;
  const passo = tam * (el.entrelinha || 1.12);
  const linhas = el.linhas || Math.ceil(H / passo) + 2;
  const sep = el.separador ?? '  ·  ';
  const unidade = String(el.texto || '') + sep;
  const repete = Math.ceil((W * 2.2) / Math.max(1, unidade.length * tam * 0.55)) + 2;
  const meio = el.destaque ?? Math.floor(linhas / 2);
  const cor = el.cor || '#141414';
  const t0 = el.t || 0;
  const contorno = el.modo === 'contorno';
  const tMarca = el.tMarca ?? t0 + 0.5;
  const top0 = (H - linhas * passo) / 2;
  return (
    <div style={{ position: 'absolute', left: el.x || 0, top: el.y || 0, width: W, height: H, overflow: 'hidden' }}>
      {Array.from({ length: linhas }).map((_, i) => {
        const ehMeio = i === meio;
        // As linhas andam em sentidos alternados: o campo respira, não fica chapado.
        const vel = (el.velocidade ?? 40) * (i % 2 ? 1 : -1) * (ehMeio ? 0.5 : 1);
        const x = -((acaso(i + 3) * unidade.length * tam * 0.5) % (unidade.length * tam * 0.55)) + vel * t;
        const aparece = t >= t0 + Math.abs(i - meio) * 2 / fps;
        const estilo = ehMeio
          ? { color: el.corDestaque || cor, fontWeight: 900 }
          : contorno
            ? { color: 'transparent', WebkitTextStroke: Math.max(1.5, tam * 0.018) + 'px ' + alfa(cor, 0.55), fontWeight: 900 }
            : { color: alfa(cor, 0.16), fontWeight: 600 };
        return (
          <div key={i} style={{ position: 'absolute', left: x, top: top0 + i * passo, whiteSpace: 'nowrap', fontFamily: familia(el.fonte),
            fontSize: tam, lineHeight: 1, letterSpacing: el.espacamento ?? '-0.01em', textTransform: el.caixaAlta === false ? undefined : 'uppercase',
            opacity: aparece ? 1 : 0, ...estilo }}>
            {ehMeio && el.marcar ? (
              <>
                {unidade.repeat(1)}
                <span style={{ position: 'relative', display: 'inline-block' }}>
                  <span style={{ position: 'absolute', left: '-0.15em', right: '-0.15em', top: '0.08em', bottom: '0.02em',
                    background: alfa(el.corAcento || '#d8342b', 0.35), mixBlendMode: 'multiply',
                    transform: 'scaleX(' + CURVAS_KIT.arrasto(entre(t, tMarca, tMarca + 12 / fps)) + ')', transformOrigin: '0% 50%' }} />
                  <span style={{ position: 'relative' }}>{el.texto}</span>
                  <Caneta d={CIRCULO} p={traco(t, tMarca + 14 / fps, fps, 15)} cor={el.corAcento || '#d8342b'} espessura={Math.max(3, tam * 0.05)} inset="-0.3em -0.5em" />
                </span>
                {sep + unidade.repeat(repete)}
              </>
            ) : unidade.repeat(repete)}
          </div>
        );
      })}
    </div>
  );
}

// ── o gráfico em tinta ───────────────────────────────────────────────────
export function Barras({ el, t, fps }) {
  const barras = el.barras || [];
  const max = Math.max(1, ...barras.map((b) => Number(b.valor) || 0));
  const iMax = barras.findIndex((b) => Number(b.valor) === max);
  const W = el.largura || 900;
  const tam = el.tamanho || 30;
  const alt = el.alturaBarra || tam * 1.5;
  const rotuloW = el.rotuloLargura || W * 0.28;
  const t0 = el.t || 0;
  const fim = t0 + barras.length * 5 / fps + 0.35;
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, width: W }}>
      {barras.map((b, i) => {
        const tb = t0 + i * 5 / fps;
        const m = mola(t, tb, fps);
        const w = (W - rotuloW - tam * 4) * (Number(b.valor) || 0) / max;
        const ehMax = i === iMax;
        return (
          <div key={i} style={{ display: 'flex', alignItems: 'center', height: alt, marginBottom: alt * 0.45 }}>
            <div style={{ width: rotuloW, fontFamily: MONO(el.fonteMono), fontSize: tam * 0.8, letterSpacing: '0.06em', color: '#5a554c',
              textTransform: 'uppercase', opacity: t >= tb ? 1 : 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{b.rotulo}</div>
            <div style={{ height: alt, width: Math.max(0, w * m), background: ehMax ? (el.corAcento || '#d8342b') : (el.cor || '#1a1a1a'),
              boxShadow: '0 1px 1px rgba(0,0,0,0.25)', filter: TINTA(el) }} />
            <div style={{ position: 'relative', marginLeft: tam * 0.6, fontFamily: familia(el.fonte), fontWeight: 800, fontSize: tam * 1.2,
              color: ehMax ? (el.corAcento || '#d8342b') : (el.cor || '#1a1a1a'), ...carimbo(t, tb + 6 / fps, fps, i + 41, 0.5) }}>
              {b.texto || b.valor}
              {ehMax ? <Caneta d={CIRCULO} p={traco(t, fim, fps, 15)} cor={el.corAcento || '#d8342b'} espessura={Math.max(3, tam * 0.12)} inset="-0.35em -0.6em" /> : null}
            </div>
          </div>
        );
      })}
    </div>
  );
}

export const VOX = {
  palavra: Palavra, lista: Lista, letra: Letra, especime: Especime, pilula: Pilula,
  fonte: Fonte, marcatexto: MarcaTexto, textura: Textura, barras: Barras,
};
