#!/usr/bin/env python3
# Ch352 diagnostic (Codex's test): replace the SH3 texture indices with a LARGE, UNMISTAKABLE
# 16x16-cell pattern using strongly-contrasting indices from the REAL SH3 CSM1 palette, and emit it
# through the EXACT same linear LPDDR texture file the uploader streams. Geometry / CLUT / feeder are
# untouched. If the cells render as clean flat colors -> the board photo is authentic high-frequency
# SH3 texture, NOT timing corruption. If the flat cells speckle -> real corruption.
#
# LOCAL/gitignored (palette is dump-derived). Backs up the real texture to *.REAL.bak first.
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from gs_localmem import ct32_addr           # CT32 grid de-swizzle (CSM1 palette layout)

DATA = os.path.join(os.path.dirname(__file__), "..", "sim", "data", "top_psmct32_raster_demo")
TEX  = os.path.join(DATA, "sh3_real_tex_lpddr.mem")
CLUT = os.path.join(DATA, "sh3_real_clut.mem")
TW = TH = 512
CELL = 16
NCHOSEN = 8

def load_words(path):
    out = []
    with open(path) as f:
        for ln in f:
            s = ln.strip()
            if not s or s.startswith("/"):
                continue
            out.append(int(s, 16) & 0xFFFFFFFF)
    return out

# ---- 1) de-grid the CSM1 palette to index order, get each index's RGB ----
clut_words = load_words(CLUT)
clut_bytes = bytearray()
for w in clut_words:
    clut_bytes += (w & 0xFFFFFFFF).to_bytes(4, "little")
pal = [0]*256
for i in range(256):
    a = ct32_addr(0, 1, i & 15, i >> 4)           # palette entry i sits at grid (i%16, i//16)
    pal[i] = int.from_bytes(clut_bytes[a:a+4], "little") if a+4 <= len(clut_bytes) else 0
rgb = [(p & 0xFF, (p >> 8) & 0xFF, (p >> 16) & 0xFF) for p in pal]   # PSMCT32 low byte = R

# ---- 2) greedy farthest-point pick of NCHOSEN maximally-contrasting indices ----
def d2(a, b): return sum((a[k]-b[k])**2 for k in range(3))
chosen = [max(range(256), key=lambda i: rgb[i][0]+rgb[i][1]+rgb[i][2])]  # start brightest
while len(chosen) < NCHOSEN:
    nxt = max(range(256), key=lambda i: min(d2(rgb[i], rgb[c]) for c in chosen))
    chosen.append(nxt)
print("[banded] chosen indices + RGB:", [(c, rgb[c]) for c in chosen])

# ---- 3) build the 16x16-cell pattern as LINEAR raster indices (PSMT8_SWIZZLE=0) ----
idx = bytearray(TW*TH)
for y in range(TH):
    cy = y // CELL
    for x in range(TW):
        cx = x // CELL
        idx[y*TW + x] = chosen[(cx + cy) % NCHOSEN]   # diagonal bands of contrasting flat cells

# ---- 4) pack 4 indices / LE word, write through the exact uploader format ----
if os.path.exists(TEX) and not os.path.exists(TEX + ".REAL.bak"):
    os.rename(TEX, TEX + ".REAL.bak")
    print("[banded] backed up real texture ->", os.path.basename(TEX) + ".REAL.bak")
nwords = (TW*TH)//4
with open(TEX, "w") as f:
    f.write("// Ch352 DIAGNOSTIC banded 16x16-cell texture (contrasting SH3 palette indices). "
            "LINEAR de-swizzled, PSMT8_SWIZZLE=0. Restore from sh3_real_tex_lpddr.mem.REAL.bak. gitignored.\n")
    sm = 0
    for w in range(nwords):
        word = idx[4*w] | (idx[4*w+1] << 8) | (idx[4*w+2] << 16) | (idx[4*w+3] << 24)
        sm = (sm + word) & 0xFFFFFFFF
        f.write("%08x\n" % word)
print(f"[banded] wrote {nwords} words to {TEX}  sum32=0x{sm:08x}")
print("[banded] upload as usual; on HDMI expect a perspective-warped grid of FLAT contrasting cells.")
print("[banded] CLEAN flat cells => authentic texture (not corruption). SPECKLED cells => real corruption.")
