"""Build a synthetic-but-realistic LAN7800 EEPROM .bin fixture.

No hardware needed. Lays down: 0xA5 signature, a MAC, a string heap with a
manufacturer/product/serial USB string descriptor, and SS/HS/FS USB device
descriptor blocks carrying VID/PID/bcdDevice — matching the layout the parser
in eeprom.py expects, so round-trip tests have something valid to chew on.
"""
import struct
import sys
import os

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from lan7800.eeprom import (SIG_OFFSET, SIG_VALUE, MAC_OFFSET, STR_TABLE_OFFSET,
                            STR_NAMES, DESC_PTRS, MAC_LITTLE_ENDIAN)


def usb_string(s: str) -> bytes:
    payload = s.encode("utf-16-le")
    return bytes([len(payload) + 2, 0x03]) + payload


def device_descriptor(vid: int, pid: int, bcd: int) -> bytes:
    # 18-byte standard USB device descriptor; only VID/PID/bcdDevice matter here.
    return struct.pack("<BBHBBBBHHHBBBB",
                       0x12, 0x01, 0x0300, 0xFF, 0x00, 0x00, 64,
                       vid, pid, bcd, 1, 2, 3, 1)


def build(mac="00:80:0F:11:22:33", vid=0x0424, pid=0x7800, bcd=0x0300,
          size=512) -> bytes:
    buf = bytearray(size)
    buf[SIG_OFFSET] = SIG_VALUE
    octets = [int(x, 16) for x in mac.split(":")]
    stored = octets[::-1] if MAC_LITTLE_ENDIAN else octets
    buf[MAC_OFFSET:MAC_OFFSET + 6] = bytes(stored)

    # --- string heap (word-aligned) ---
    # Placed high (0xC0+) so it clears both the fixed LED/GPIO register offsets
    # (0x07/0x0B-0x0D/0x0E/0x48-0x4F/0x58-0x59) and the descriptor blocks below.
    heap = 0xC0
    strings = {"manufacturer": "Microchip", "product": "LAN7800 USB 3.1 GbE",
               "serial": "SN0000000001"}
    for i, name in enumerate(STR_NAMES):
        entry = STR_TABLE_OFFSET + i * 2
        if name in strings:
            desc = usb_string(strings[name])
            buf[heap:heap + len(desc)] = desc
            buf[entry] = len(desc)          # length field (bytes)
            buf[entry + 1] = heap // 2       # word offset
            heap += (len(desc) + 1) & ~1     # keep word alignment
        else:
            buf[entry] = 0
            buf[entry + 1] = 0

    # --- descriptor blocks for SS/HS/FS ---
    # Placed at 0x60/0x80/0xA0 (24-byte slots) so no 18-byte block overlaps the
    # fixed GPIO config region (0x48-0x4F) or LED behavior (0x58-0x59). This
    # matches how a real EEPROM's pointer table keeps descriptors clear of the
    # low config/register bytes.
    dd = device_descriptor(vid, pid, bcd)
    block = 0x60
    for kind, ptr_off in DESC_PTRS.items():
        buf[block:block + len(dd)] = dd
        buf[ptr_off] = (block // 2) & 0xFF
        buf[ptr_off + 1] = ((block // 2) >> 8) & 0xFF
        block += 24
    return bytes(buf)


if __name__ == "__main__":
    out = sys.argv[1] if len(sys.argv) > 1 else "fixture.bin"
    open(out, "wb").write(build())
    print(f"wrote {out}")
