#!/usr/bin/env python3
"""
Decode the A/B bootloader_control from `para` (MTK's misc) so we can see EXACTLY
why lk boots to fastboot instead of slot A: is there a valid control struct, is
slot A active/bootable (priority>0, tries>0 or successful=1), or is it
empty/marked-unbootable?

This is the deciding partition for A/B boot. Run with device in BROM mode:
    sudo python3 slot_info.py
"""
import os, sys, time, struct
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")
PARA_ADDR = 0x108000          # 'para' partition (MTK A/B boot control lives here)
BOOTCTRL_MAGIC = b"\x42\x43\x41\x42"   # 0x42414342 LE ("BCAB")


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


def connect(mtk):
    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))
            return True
        except Exception as e:
            log("warn", f"connect: {e}")
            mtk.disconnect(); time.sleep(0.5)
    return False


def decode_slot(b2):
    """slot_metadata (2 bytes): priority[3:0], tries[6:4], successful[7],
    verity_corrupted in next byte bit0."""
    b0 = b2[0]
    priority = b0 & 0x0F
    tries = (b0 >> 4) & 0x07
    successful = (b0 >> 7) & 0x01
    verity_corrupted = b2[1] & 0x01 if len(b2) > 1 else 0
    bootable = priority > 0 and (successful == 1 or tries > 0)
    return priority, tries, successful, verity_corrupted, bootable


def main():
    print("=" * 64)
    print("  A/B SLOT (bootloader_control in 'para') decoder")
    print("=" * 64)
    mtk = MTKDevice(log_cb=lambda l, m: None)
    if not connect(mtk):
        log("FAIL", "could not connect"); return 2
    log("ok", "DA ready\n")

    data = mtk.read_partition(PARA_ADDR, 0x10000, parttype=8)
    if not data:
        log("FAIL", "could not read 'para'"); return 2

    idx = data.find(BOOTCTRL_MAGIC)
    if idx == -1:
        log("FAIL", "NO bootloader_control magic ('BCAB') in para — the A/B "
            "control struct is MISSING/erased. lk has no slot to boot -> "
            "fastboot. THIS is the missing piece SP Flash Tool writes.")
        log("info", f"para @ {PARA_ADDR:#x} first 64 bytes:")
        print("   ", data[:64].hex())
        # also dump offset 0x800 (common bootctrl location)
        log("info", "para @ +0x800:")
        print("   ", data[0x800:0x840].hex())
        return 1

    hdr = idx - 4   # magic is at offset 4 of the struct (after slot_suffix[4])
    base = max(hdr, 0)
    suffix = data[base:base + 4]
    version = data[base + 8]
    nb = data[base + 9] & 0x07
    log("ok", f"bootloader_control found @ para+{base:#x}")
    log("info", f"  slot_suffix={suffix!r}  version={version}  nb_slot={nb}")
    # slot_info[] starts at offset 12 of the struct
    for s in range(min(nb or 2, 2)):
        off = base + 12 + s * 2
        pri, tries, ok_, vc, bootable = decode_slot(data[off:off + 2])
        tag = "BOOTABLE ✓" if bootable else "NOT bootable ✗"
        log("info" if bootable else "warn",
            f"  slot {chr(65+s)}: priority={pri} tries={tries} "
            f"successful={ok_} verity_corrupted={vc}  -> {tag}")

    print("=" * 64)
    pri_a, tries_a, ok_a, _, boot_a = decode_slot(data[base + 12:base + 14])
    if boot_a:
        log("ok", "Slot A is bootable. If it still won't boot, the blocker is "
            "AFTER the bootloader (kernel/vbmeta/dtbo), not the slot.")
    else:
        log("FAIL", "Slot A is NOT bootable (priority/tries/successful are 0). "
            "lk will drop to fastboot. Need to write a valid bootloader_control "
            "with slot A active — this is what SP Flash Tool does and the tool "
            "must do too.")
    print("=" * 64)
    return 0


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