#!/usr/bin/env python3
"""
Read back what is actually on the device and compare it to the files we
flashed. This decisively answers "is the tool corrupting writes?".

Reads the first 64 KB (one clean read-packet) of:
  - BOOT1  (preloader)  vs  the preloader .bin
  - USER@0 (GPT/start)  vs  FULL.bin

MATCH on both  -> the tool writes correctly; a no-boot is the firmware
                  (FULL.bin from another unit, missing format, etc).
MISMATCH       -> the device content differs from what we sent = tool bug,
                  and we print the first differing offset.

Run with device in BROM mode:
    sudo python3 read_and_compare.py
"""
import os, sys, time, hashlib
from mtk_device import MTKDevice, MTKError
from da_parser import find_da_for_chip, load_da_regions

HERE = os.path.dirname(os.path.abspath(__file__))
DA_PATH = os.path.join(HERE, "DA_PL.bin")
PRELOADER = "/Users/home/Downloads/scatter-preloader-for-32g/preloader_k61v1_64_bsp.bin"
FULL = "/Users/home/mtkclient/FULL.bin"
CHUNK = 0x10000  # 64 KB — one read-packet


def log(level, msg):
    print(f"[{level:7}] {msg}")


def compare(mtk, label, addr, parttype, filepath, file_offset=0):
    if not os.path.isfile(filepath):
        log("warn", f"{label}: file not found {filepath}")
        return None
    with open(filepath, "rb") as f:
        f.seek(file_offset)
        expected = f.read(CHUNK)
    got = mtk.read_partition(addr, CHUNK, parttype=parttype)
    n = min(len(got), len(expected))
    print("-" * 64)
    log("info", f"{label}: read {len(got)} bytes, comparing {n}")
    if n == 0:
        log("error", f"{label}: readback EMPTY")
        return False
    if got[:n] == expected[:n]:
        log("success", f"{label}: MATCH ✓  device == file "
                       f"(sha {hashlib.sha256(got[:n]).hexdigest()[:16]})")
        return True
    for i in range(0, n, 16):
        if got[i:i+16] != expected[i:i+16]:
            log("error", f"{label}: MISMATCH ✗ at offset {i:#x}")
            log("error", f"  file  : {expected[i:i+16].hex()}")
            log("error", f"  device: {got[i:i+16].hex()}")
            break
    return False


def main():
    print("=" * 64)
    print("  READ-BACK COMPARE  (device content vs flashed files)")
    print("=" * 64)
    mtk = MTKDevice(log_cb=log)
    deadline = time.time() + 120
    while time.time() < deadline:
        try:
            mtk.connect(timeout_sec=10)
            mtk.brom_handshake()
            mtk.get_hw_code(); mtk.get_hw_sw_ver()
            try: mtk.get_target_config()
            except Exception: pass
            hw = mtk.hw_code
            da = find_da_for_chip(DA_PATH, hw) or \
                find_da_for_chip(DA_PATH, {0x0717:0x6761,0x0507:0x6761}.get(hw,hw))
            if not da:
                raise MTKError(f"No DA for {hw:#06x}")
            mtk.full_init(DA_PATH, load_da_regions(DA_PATH, da))
            log("success", "DA ready")
            break
        except Exception as e:
            log("warn", f"connect: {e}")
            mtk.disconnect(); time.sleep(0.5)
    else:
        log("error", "could not connect"); return 2

    results = []
    # preloader in BOOT1
    results.append(compare(mtk, "BOOT1/preloader @0", 0x0, 1, PRELOADER))
    # multiple windows across the 29GB USER image (incl. chunk boundaries)
    user_offsets = [
        0x0,            # start / GPT
        0x200000,       # 2 MB — first DA-chunk boundary
        0x40000000,     # 1 GB
        0x200000000,    # 8 GB
        0x700000000,    # 28 GB — near end
    ]
    for off in user_offsets:
        results.append(compare(mtk, f"USER @ {off:#x}", off, 8, FULL, file_offset=off))

    print("=" * 64)
    if all(r for r in results if r is not None):
        log("success", "ALL WINDOWS MATCH — the tool wrote the entire image "
                       "byte-perfect. A no-boot is NOT a writing problem "
                       "(eMMC boot-config or firmware/device match).")
    else:
        log("error", "MISMATCH found — corruption deeper in the image. "
                     "The write path is still imperfect; see offset above.")
    mtk.flush_log_report()
    return 0


if __name__ == "__main__":
    sys.exit(main())
