"""Independent stdlib PNG decode and source-over verification of authored assets."""

import json
import math
import struct
import sys
import zlib
from pathlib import Path

root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).parent


def png(name):
    b = (root / name).read_bytes()
    assert b[:8] == b"\x89PNG\r\n\x1a\n"
    p, packed, header = 8, b"", None
    while p < len(b):
        n = struct.unpack(">I", b[p : p + 4])[0]
        kind, data = b[p + 4 : p + 8], b[p + 8 : p + 8 + n]
        assert zlib.crc32(kind + data) == struct.unpack(">I", b[p + 8 + n : p + 12 + n])[0]
        if kind == b"IHDR":
            header = struct.unpack(">IIBBBBB", data)
        if kind == b"IDAT":
            packed += data
        p += n + 12
    assert header == (512, 384, 8, 6, 0, 0, 0)
    raw = zlib.decompress(packed)
    stride, prior, decoded = 512 * 4, bytearray(512 * 4), bytearray()
    assert len(raw) == 384 * (stride + 1)

    def paeth(a, b, c):
        p = a + b - c
        d = [abs(p - a), abs(p - b), abs(p - c)]
        return [a, b, c][d.index(min(d))]

    for y in range(384):
        start = y * (stride + 1)
        filt, row = raw[start], bytearray(raw[start + 1 : start + 1 + stride])
        assert filt in range(5)
        for i in range(stride):
            a, up, corner = row[i - 4] if i >= 4 else 0, prior[i], prior[i - 4] if i >= 4 else 0
            delta = [0, a, up, (a + up) // 2, paeth(a, up, corner)][filt]
            row[i] = (row[i] + delta) & 255
        decoded.extend(row)
        prior = row
    return decoded


layers = [png(f"layereditor-{n}.png") for n in ["background", "product", "seal"]]
target = png("layereditor-direct-target.png")
assert all(target[i] == 255 for i in range(3, len(target), 4))


def compose(stack):
    result = bytearray(len(target))
    for p in range(0, len(result), 4):
        colors = [255.0] * 3
        for layer in stack:
            opacity = layer[p + 3] / 255
            colors = [layer[p + c] * opacity + colors[c] * (1 - opacity) for c in range(3)]
        result[p : p + 4] = bytes([math.floor(v + 0.5) for v in colors] + [255])
    return result


wrong = [layers[1], layers[2], layers[0]]
proof = []
for mode, stack in [("correct", layers), ("wrong", wrong), ("masked", wrong + [target])]:
    expected = compose(stack)
    actual = png(f"layereditor-{mode}.png")
    assert actual == expected
    if mode == "correct":
        assert target == actual
    changes, total, maximum = 0, 0, 0
    for p in range(0, len(actual), 4):
        d = [abs(actual[p + c] - target[p + c]) for c in range(3)]
        changes += any(d)
        total += sum(d)
        maximum = max(maximum, *d)
    r = json.loads((root / f"layereditor-{mode}.json").read_text())
    assert r["changedPixels"] == changes and r["maximumChannelError"] == maximum
    assert abs(r["meanAbsoluteRGBByteError"] - total / (512 * 384 * 3)) < 1e-12
    proof.append(
        {
            "mode": mode,
            "changedPixels": changes,
            "meanRGBByteError": total / (512 * 384 * 3),
            "pixelsChecked": 512 * 384,
        }
    )
assert proof[0]["changedPixels"] == proof[2]["changedPixels"] == 0 and proof[1]["changedPixels"] > 0
print(
    json.dumps(
        {
            "cases": proof,
            "scope": "Exact decoded authored pixels and declared byte-space formula not a LayerEditor model run or usability certification",
        },
        indent=2,
    )
)
