/*
 * O fundo verde (ou azul) que some: chroma key da foto e do vídeo. A conta é
 * linear por pixel (a "dominância" da cor da tela: G - (R+B)/2 no verde), o
 * que cabe num feColorMatrix do SVG — o editor e o Chrome do servidor usam o
 * filtro, e o MP4 aplica a MESMA conta nos quadros do vídeo (pixel a pixel).
 * Junto, tira o reflexo verde da borda (o "despill").
 */

export type ChromaDaFoto = { cor: "verde" | "azul"; forca: number };

/* Abaixo deste tanto de dominância, nada some (a pele, a roupa); `forca` (0 a 100) deixa o corte mais duro. */
const LIMIAR = 0.06;
const coeficientes = (c: ChromaDaFoto) => {
  const s = 3 + (Math.max(0, Math.min(100, c.forca)) / 100) * 9;
  const k = 0.25 + (Math.max(0, Math.min(100, c.forca)) / 100) * 0.35;
  return { s, k };
};

/** O filtro SVG (id `crm<id>`): a transparência pela dominância, o reflexo tirado e o recorte pela transparência original. */
export function filtroDoChroma(c: ChromaDaFoto, id: string): string {
  const { s, k } = coeficientes(c);
  const f = (v: number) => (Math.round(v * 10000) / 10000).toString();
  /* Linhas R, G, B, A da matriz (a cor da tela fica com a média das outras duas, em parte). */
  const linhas =
    c.cor === "azul"
      ? [`1 0 0 0 0`, `0 1 0 0 0`, `${f(k / 2)} ${f(k / 2)} ${f(1 - k)} 0 0`, `${f(s / 2)} ${f(s / 2)} ${f(-s)} 0 ${f(1 + s * LIMIAR)}`]
      : [`1 0 0 0 0`, `${f(k / 2)} ${f(1 - k)} ${f(k / 2)} 0 0`, `0 0 1 0 0`, `${f(s / 2)} ${f(-s)} ${f(s / 2)} 0 ${f(1 + s * LIMIAR)}`];
  /* O reflexo só sai (nunca entra): o "escurecer" fica com o menor de cada canal, a cor de antes ou a limpa. */
  const [r, g, b, a] = linhas;
  return `<filter id="crm${id}" color-interpolation-filters="sRGB" x="0" y="0" width="100%" height="100%"><feColorMatrix in="SourceGraphic" type="matrix" values="${r}  ${g}  ${b}  0 0 0 1 0" result="sem"/><feBlend in="SourceGraphic" in2="sem" mode="darken" result="limpo"/><feColorMatrix in="SourceGraphic" type="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  ${a}" result="chave"/><feComposite in="limpo" in2="chave" operator="in"/></filter>`;
}

/** A mesma conta nos pixels (RGBA de 0 a 255, sem pré-multiplicar), no lugar. */
export function aplicarChroma(dados: Uint8ClampedArray, c: ChromaDaFoto) {
  const { s, k } = coeficientes(c);
  const azul = c.cor === "azul";
  for (let i = 0; i < dados.length; i += 4) {
    const r = dados[i] / 255;
    const g = dados[i + 1] / 255;
    const b = dados[i + 2] / 255;
    const tela = azul ? b : g;
    const outras = azul ? (r + g) / 2 : (r + b) / 2;
    const alfa = Math.max(0, Math.min(1, 1 + s * LIMIAR - s * (tela - outras)));
    if (azul) dados[i + 2] = Math.round(Math.min(b, (1 - k) * b + k * outras) * 255);
    else dados[i + 1] = Math.round(Math.min(g, (1 - k) * g + k * outras) * 255);
    dados[i + 3] = Math.round(dados[i + 3] * alfa);
  }
}

export function chromaLimpo(bruto: unknown): ChromaDaFoto | null {
  if (!bruto || typeof bruto !== "object") return null;
  const c = bruto as Record<string, unknown>;
  const forca = Number(c.forca);
  return { cor: c.cor === "azul" ? "azul" : "verde", forca: Number.isFinite(forca) ? Math.max(0, Math.min(100, Math.round(forca))) : 50 };
}
