#!/usr/bin/env python3
"""Generate tiny synthetic PNGs for the ez3d test suite.

Writes real PNGs using only zlib + struct (no PIL/numpy needed), as
8-bit grayscale or grayscale+alpha, matching the formats ez3d sees in
the wild.
"""
import struct
import sys
import zlib


def write_png(path, w, h, gray, alpha=None):
    """gray/alpha: sequences of w*h ints in 0..255, row-major from the
    top row (PNG order)."""
    has_alpha = alpha is not None
    nch = 2 if has_alpha else 1
    ctype = 4 if has_alpha else 0  # PNG color type: 0=gray, 4=gray+alpha

    raw = b""
    for y in range(h):
        row = bytearray(b"\x00")  # filter: none
        for x in range(w):
            row.append(gray[y * w + x] & 0xFF)
            if has_alpha:
                row.append(alpha[y * w + x] & 0xFF)
        raw += bytes(row)

    def chunk(tag, data):
        return (struct.pack(">I", len(data)) + tag + data
                + struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF))

    ihdr = struct.pack(">IIBBBBB", w, h, 8, ctype, 0, 0, 0)
    with open(path, "wb") as f:
        f.write(b"\x89PNG\r\n\x1a\n"
                + chunk(b"IHDR", ihdr)
                + chunk(b"IDAT", zlib.compress(raw))
                + chunk(b"IEND", b""))


def main():
    out = sys.argv[1]

    # uniform white, 4x4, no alpha
    write_png(f"{out}/white4.png", 4, 4, [255] * 16)

    # single pixel
    write_png(f"{out}/one.png", 1, 1, [255])

    # 2x2 black (for --invert)
    write_png(f"{out}/black2.png", 2, 2, [0] * 4)

    # 8x8 linear gradient along x, no alpha
    grad = [int(255 * x / 7) for y in range(8) for x in range(8)]
    write_png(f"{out}/grad8.png", 8, 8, grad)

    # 8x8 white with a transparent 4x4 block in the middle (pixels 2..5)
    alpha = []
    for y in range(8):
        for x in range(8):
            inside = 2 <= x <= 5 and 2 <= y <= 5
            alpha.append(0 if inside else 255)
    write_png(f"{out}/holed8.png", 8, 8, [255] * 64, alpha)

    # 16x16 with a large flat background and one 4x4 bump in the middle:
    # background gray 51, bump 255, alpha border ring solid only in a
    # 12x12 center (tests alpha cut + flat merging together)
    w = h = 16
    gray, alpha = [], []
    for y in range(h):
        for x in range(w):
            bump = 6 <= x <= 9 and 6 <= y <= 9
            gray.append(255 if bump else 51)
            inside = 2 <= x <= 13 and 2 <= y <= 13
            alpha.append(255 if inside else 0)
    write_png(f"{out}/bump16.png", w, h, gray, alpha)

    # two islands with a 4px transparent moat between them (decimation
    # must never bridge it): solid x in 0..5 and 10..15
    w, h = 16, 8
    gray, alpha = [], []
    for y in range(h):
        for x in range(w):
            gray.append(int(255 * y / (h - 1)))  # detail so QEM has work
            alpha.append(0 if 6 <= x <= 9 else 255)
    write_png(f"{out}/moat16.png", w, h, gray, alpha)

    # solid plate with a 1px-wide transparent slot (the "gap between
    # coils" shape): slot at x=8, y in 4..11
    w = h = 16
    gray, alpha = [], []
    for y in range(h):
        for x in range(w):
            gray.append(int(255 * x / (w - 1)))
            alpha.append(0 if x == 8 and 4 <= y <= 11 else 255)
    write_png(f"{out}/slot16.png", w, h, gray, alpha)

    # 45-degree diagonal edge: solid where x + y <= 8, a clean 1px
    # staircase that --chamfer should straighten into a true diagonal
    w = h = 10
    gray, alpha = [], []
    for y in range(h):
        for x in range(w):
            gray.append(200)
            alpha.append(255 if x + y <= 8 else 0)
    write_png(f"{out}/stairs10.png", w, h, gray, alpha)

    # one white pixel on black: cells with corner heights (10,0,0,0),
    # the canonical --fold test case (mountain midpoint 5, valley 0,
    # center 2.5)
    w = h = 2
    write_png(f"{out}/corner10.png", w, h, [255, 0, 0, 0])

    print("generated test PNGs in", out)


if __name__ == "__main__":
    main()
