#!/usr/bin/env python3
"""Run this on your machine: python3 diagnose_pm4n.py Dummy.pm4n"""
import struct, sys

path = sys.argv[1] if len(sys.argv) > 1 else 'Dummy.pm4n'
data = open(path, 'rb').read()
fsize = len(data)
print(f"File: {path}  ({fsize} bytes)")
print()

# Parse ANYCUBIC header
magic        = data[0:8]
version      = struct.unpack_from('<I', data, 8)[0]
n_sections   = struct.unpack_from('<I', data, 12)[0]
header_size  = struct.unpack_from('<I', data, 16)[0]
print(f"Magic:         {magic}")
print(f"Bytes 8-11:    {data[8:12].hex()}  (={version})")
print(f"Section count: {n_sections}")
print(f"Header size:   {header_size}  (0x{header_size:X})")
print()

# Section table: n_sections entries of (offset:u32, length:u32) starting at byte 20
print(f"Section table ({n_sections} entries from offset 20):")
sections = []
for i in range(n_sections):
    base = 20 + i * 8
    if base + 8 > fsize:
        break
    off = struct.unpack_from('<I', data, base)[0]
    ln  = struct.unpack_from('<I', data, base+4)[0]
    sections.append((off, ln))

for idx, (off, ln) in enumerate(sections):
    tag = data[off:off+4] if off + 4 <= fsize else b'????'
    tag_str = tag.decode('ascii', errors='replace')
    print(f"  [{idx:2d}]  offset=0x{off:06X} ({off:7d})  length={ln:7d}  tag@offset={tag_str!r}")

print()
# Also peek at each section start for tag-like content
print("Content at each section offset (first 32 bytes):")
for idx, (off, ln) in enumerate(sections):
    if off + 16 <= fsize:
        chunk = data[off:off+32]
        # Try to find sub-tags
        for sub_off in range(0, min(32, len(chunk))-3):
            sub_tag = chunk[sub_off:sub_off+4]
            if all(32 <= b < 127 for b in sub_tag):
                sub_len = struct.unpack_from('<I', chunk, sub_off+4)[0] if sub_off+8 <= len(chunk) else 0
                print(f"  section[{idx}]+0x{sub_off:02X}  tag={sub_tag.decode()!r}  next_u32={sub_len}")
                break
        else:
            print(f"  section[{idx}]  hex: {chunk[:16].hex()}")

print()
# Look for exposure-like floats (1.0 to 600.0) across the whole file
print("Float values in range [1.0 .. 600.0] across whole file:")
for off in range(0, fsize - 3, 4):
    v = struct.unpack_from('<f', data, off)[0]
    if 1.0 <= v <= 600.0 and v == round(v, 1):
        # Show context
        section_hint = next((f"sec[{i}]+{off-s:d}" for i,(s,l) in enumerate(sections) if s <= off < s+l), "outside")
        print(f"  0x{off:06X}  {v:.1f}  ({section_hint})")

# ---------------------------------------------------------------------------
# Extra: dump LAYE entries with CORRECT field layout
# (verified from binary probe: HDR=0x20, exposure@+0x00, data_off@+0x14, data_size@+0x18)
# ---------------------------------------------------------------------------
def find_tag2(d, tag):
    i = d.find(tag)
    return i if i >= 0 else None

LAYE_HDR_SIZE2 = 0x20
ENTRY_STRIDE2  = 0x20

laye2 = find_tag2(data, b'LAYE')
if laye2 is not None:
    n_entries2     = struct.unpack_from('<I', data, laye2 + 0x10)[0]
    composite_off2 = struct.unpack_from('<I', data, laye2 + 0x14)[0]
    block_size2    = struct.unpack_from('<I', data, laye2 + 0x18)[0]
    print(f"\nLAYE (correct layout) at 0x{laye2:06X}:")
    print(f"  n_entries    = {n_entries2}")
    print(f"  composite_off= 0x{composite_off2:06X}")
    print(f"  block_size   = {block_size2}")
    expected_off = composite_off2
    ok = True
    for i in range(n_entries2):
        base = laye2 + LAYE_HDR_SIZE2 + i * ENTRY_STRIDE2
        if base + ENTRY_STRIDE2 > fsize:
            break
        exp     = struct.unpack_from('<f', data, base + 0x00)[0]
        z_pos   = struct.unpack_from('<f', data, base + 0x04)[0]
        d_off   = struct.unpack_from('<I', data, base + 0x14)[0]
        d_sz    = struct.unpack_from('<I', data, base + 0x18)[0]
        expected_off += block_size2
        match = "OK" if d_off == expected_off and d_sz == block_size2 else "*** MISMATCH ***"
        if match != "OK":
            ok = False
        print(f"  entry[{i:2d}]: exp={exp:.1f}s  z={z_pos:.2f}mm  "
              f"data_off=0x{d_off:06X} (exp 0x{expected_off:06X})  "
              f"size={d_sz}  {match}")
    if ok:
        print("  All entries consistent ✓")
    # Also confirm global header px dimensions
    gw = struct.unpack_from('<I', data, 0x7C)[0]
    gh = struct.unpack_from('<I', data, 0x80)[0]
    print(f"\nGlobal header px dimensions: {gw}×{gh}  "
          f"({'✓ correct for Mono 4' if gw==9024 and gh==5120 else '*** WRONG ***'})")