"""
Figuras do ensaio "A pressa de acreditar" (wagnerlima.cc).

hero.png    a curva que circula
image1.png  mil pares de números sorteados, agrupados em quartis
            pela convenção que Nuhfer e colegas condenam

Semente da figura publicada: 1447564174
Sem argumento, sorteia outra.

    python3 gerar-figuras.py 1447564174

Dependências: numpy, cairosvg
"""

import os
import sys

import numpy as np

CAMPO = "#101015"   # fundo do site, tema escuro
LINHA = "#F4F4F5"   # zinc-100
EIXO = "#9F9FA9"    # zinc-400
TEAL = "#00D5BE"    # teal-400
W, H = 1200, 630


def catmull(pts, n=400):
    """Spline suave passando por todos os pontos de controle."""
    p = [pts[0]] + list(pts) + [pts[-1]]
    out = []
    passo = n // (len(p) - 3)
    for i in range(len(p) - 3):
        p0, p1, p2, p3 = p[i], p[i + 1], p[i + 2], p[i + 3]
        for k in range(passo):
            t = k / passo
            t2, t3 = t * t, t * t * t
            x = 0.5 * ((2 * p1[0]) + (-p0[0] + p2[0]) * t +
                       (2 * p0[0] - 5 * p1[0] + 4 * p2[0] - p3[0]) * t2 +
                       (-p0[0] + 3 * p1[0] - 3 * p2[0] + p3[0]) * t3)
            y = 0.5 * ((2 * p1[1]) + (-p0[1] + p2[1]) * t +
                       (2 * p0[1] - 5 * p1[1] + 4 * p2[1] - p3[1]) * t2 +
                       (-p0[1] + 3 * p1[1] - 3 * p2[1] + p3[1]) * t3)
            out.append((x, y))
    out.append(pts[-1])
    return out


def fmt(pts):
    return " ".join(f"{x:.1f},{y:.1f}" for x, y in pts)


def hero(path):
    """A curva popular. Composição deslocada para a direita: os 38% da
    esquerda ficam livres para o texto do cartão de compartilhamento."""
    x0, x1 = 500, 1120
    y0, y1 = 92, 512
    ctrl = [(0.00, 0.04), (0.035, 0.56), (0.075, 0.96), (0.120, 0.83),
            (0.200, 0.50), (0.320, 0.11), (0.460, 0.21), (0.640, 0.44),
            (0.820, 0.61), (1.000, 0.70)]
    pts = catmull([(x0 + t * (x1 - x0), y1 - v * (y1 - y0)) for t, v in ctrl])
    ticks = "".join(
        f'<line x1="{x0 + i * (x1 - x0) / 4:.0f}" y1="{y1 + 30}" '
        f'x2="{x0 + i * (x1 - x0) / 4:.0f}" y2="{y1 + 44}"/>' for i in range(5))
    svg = f'''<svg xmlns="http://www.w3.org/2000/svg" width="{W}" height="{H}" viewBox="0 0 {W} {H}">
  <rect width="{W}" height="{H}" fill="{CAMPO}"/>
  <g stroke="{EIXO}" stroke-width="3" stroke-linecap="round" opacity="0.38" fill="none">
    <line x1="{x0 - 40}" y1="{y0 - 30}" x2="{x0 - 40}" y2="{y1 + 30}"/>
    <line x1="{x0 - 40}" y1="{y1 + 30}" x2="{x1 + 40}" y2="{y1 + 30}"/>
  </g>
  <g stroke="{EIXO}" stroke-width="3" stroke-linecap="round" opacity="0.22" fill="none">{ticks}</g>
  <polyline points="{fmt(pts)}" fill="none" stroke="{LINHA}" stroke-width="8"
            stroke-linecap="round" stroke-linejoin="round"/>
</svg>'''
    open(path, "w").write(svg)


def ruido(path, seed):
    """Ruído puro pela convenção condenada: agregar em quantis."""
    rng = np.random.default_rng(seed)
    n = 1000
    real = rng.uniform(0, 100, n)     # desempenho, aleatório
    achou = rng.uniform(0, 100, n)    # autoavaliação, aleatória e INDEPENDENTE
    q = np.array_split(np.argsort(real), 4)
    m_real = [real[g].mean() for g in q]
    m_achou = [achou[g].mean() for g in q]

    x0, x1, y0, y1 = 210, 1010, 110, 500
    xs = [x0 + i * (x1 - x0) / 3 for i in range(4)]

    def Y(v):
        return y1 - (v / 100) * (y1 - y0)

    pr = [(x, Y(v)) for x, v in zip(xs, m_real)]
    pa = [(x, Y(v)) for x, v in zip(xs, m_achou)]
    grid = "".join(f'<line x1="{x0 - 30}" y1="{Y(v)}" x2="{x1 + 30}" y2="{Y(v)}"/>'
                   for v in (25, 50, 75))
    dots = "".join(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="9" fill="{CAMPO}" '
                   f'stroke="{EIXO}" stroke-width="5"/>' for x, y in pr)
    dots += "".join(f'<circle cx="{x:.1f}" cy="{y:.1f}" r="9" fill="{CAMPO}" '
                    f'stroke="{TEAL}" stroke-width="5"/>' for x, y in pa)
    svg = f'''<svg xmlns="http://www.w3.org/2000/svg" width="{W}" height="{H}" viewBox="0 0 {W} {H}">
  <rect width="{W}" height="{H}" fill="{CAMPO}"/>
  <g stroke="{EIXO}" stroke-width="2" opacity="0.16" stroke-dasharray="10 9" fill="none">{grid}</g>
  <g stroke="{EIXO}" stroke-width="3" stroke-linecap="round" opacity="0.38" fill="none">
    <line x1="{x0 - 30}" y1="{y0 - 30}" x2="{x0 - 30}" y2="{y1 + 30}"/>
    <line x1="{x0 - 30}" y1="{y1 + 30}" x2="{x1 + 30}" y2="{y1 + 30}"/>
  </g>
  <polyline points="{fmt(pr)}" fill="none" stroke="{EIXO}" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
  <polyline points="{fmt(pa)}" fill="none" stroke="{TEAL}" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/>
  {dots}
</svg>'''
    open(path, "w").write(svg)
    return m_real, m_achou


if __name__ == "__main__":
    import cairosvg

    seed = int(sys.argv[1]) if len(sys.argv) > 1 else int.from_bytes(os.urandom(4), "big")
    aqui = os.path.dirname(os.path.abspath(__file__))
    imagens = os.path.join(aqui, "imagens")
    os.makedirs(imagens, exist_ok=True)

    hero(os.path.join(imagens, "hero.svg"))
    mr, ma = ruido(os.path.join(imagens, "image1.svg"), seed)

    for nome in ("hero", "image1"):
        cairosvg.svg2png(url=os.path.join(imagens, f"{nome}.svg"),
                         write_to=os.path.join(imagens, f"{nome}.png"),
                         output_width=2400, output_height=1260)

    print("semente:", seed)
    print("desempenho real por quartil:", [round(v, 1) for v in mr])
    print("autoavaliação por quartil:  ", [round(v, 1) for v in ma])
