#!/usr/bin/env python3
"""Build a focused review of object draw flags and DirectDraw color-key boundaries."""
from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from probe_exe_scene_tables import read_sections, va_to_offset


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"
EXE = ROOT / "Hwanse2.exe"


def hx(value: int | None) -> str | None:
    if value is None:
        return None
    return f"0x{value:08x}"


def exe_bytes(exe: bytes, sections: list[dict[str, Any]], va: int, size: int = 16) -> dict[str, Any]:
    offset = va_to_offset(sections, va)
    if offset is None:
        return {
            "vaHex": hx(va),
            "fileOffsetHex": None,
            "bytes": None,
            "available": False,
        }
    blob = exe[offset : offset + size]
    return {
        "vaHex": hx(va),
        "fileOffsetHex": hx(offset),
        "bytes": blob.hex(" "),
        "available": True,
    }


def byte_evidence(exe: bytes, sections: list[dict[str, Any]]) -> list[dict[str, Any]]:
    rows = [
        (
            0x00416CEB,
            10,
            "active object source id",
            "object+0x28 low word is masked to source/frame index",
        ),
        (
            0x00416CF9,
            15,
            "active object surface slot",
            "object+0x28 high word indexes 0x0055abd8 surface-wrapper table",
        ),
        (
            0x00416D79,
            12,
            "active object scaled branch",
            "test object+0x2c against 0x0208; true branch uses alternate/scaled rect handling",
        ),
        (
            0x00416DFD,
            10,
            "centered scale/anchor bit",
            "test byte object+0x2d against 0x02 inside the scaled branch",
        ),
        (
            0x0041712D,
            12,
            "active object clip slot",
            "object+0x2c & 0x07 selects a clip/viewport rect from 0x0055ab08 + slot*0x10",
        ),
        (
            0x00419A2A,
            12,
            "generic wrapper scaled branch",
            "generic grid draw path repeats the object+0x2c 0x0208 branch test",
        ),
        (
            0x00419A3C,
            10,
            "generic wrapper centered bit",
            "generic grid draw path repeats the object+0x2d 0x02 centered correction test",
        ),
        (
            0x00419AF9,
            14,
            "generic scaled clip slot",
            "scaled branch uses object+0x2c & 0x07 to select 0x0055ab08 clipping rect",
        ),
        (
            0x00419B5F,
            14,
            "generic normal clip slot",
            "normal branch uses object+0x2c & 0x07 to select 0x0055ab08 clipping rect",
        ),
        (
            0x00417722,
            8,
            "Blt call",
            "copy/update helper calls vtable +0x14 after OR-ing caller flags with 0x01000000",
        ),
        (
            0x004177D7,
            8,
            "BltFast-like call",
            "graphics flag 0x80 path calls vtable +0x1c after OR-ing caller flags with 0x10",
        ),
        (
            0x0041784B,
            8,
            "Blt fallback call",
            "non-0x80 copy/update path calls vtable +0x14 after OR-ing caller flags with 0x01000000",
        ),
        (
            0x0041790E,
            12,
            "fill flags immediate",
            "pushes 0x01000400 with a DDBLTFX-sized local block before Blt",
        ),
        (
            0x00417938,
            8,
            "fill Blt call",
            "surface fill/clear helper calls vtable +0x14",
        ),
        (
            0x00419B29,
            16,
            "scaled branch Blt flags global",
            "scaled branch loads flags from 0x004676f8 before Blt",
        ),
        (
            0x00419BA0,
            16,
            "normal branch BltFast flags global",
            "fast branch loads flags from 0x004676f4 before vtable +0x1c call",
        ),
        (
            0x00419BD1,
            16,
            "normal branch Blt flags global",
            "normal Blt branch loads flags from 0x004676f4 before vtable +0x14 call",
        ),
    ]
    output = []
    for va, size, label, meaning in rows:
        output.append({**exe_bytes(exe, sections, va, size), "label": label, "meaning": meaning})
    return output


def build_report() -> dict[str, Any]:
    exe = EXE.read_bytes()
    sections = read_sections(exe)
    byte_rows = byte_evidence(exe, sections)
    byte_by_va = {row["vaHex"]: row for row in byte_rows}

    direct_draw_calls = [
        {
            "vaHex": "0x0041769b",
            "method": "Flip",
            "vtableOffsetHex": "0x2c",
            "role": "fullscreen/page-flip presentation",
            "flagSource": "literal 0, 0 arguments",
            "boundary": "presentation only; not sprite transparency",
        },
        {
            "vaHex": "0x00417722",
            "method": "Blt",
            "vtableOffsetHex": "0x14",
            "role": "surface copy/update helper",
            "flagSource": "caller flags OR 0x01000000",
            "boundary": "copy path; transparency depends on caller/global flags, not CNS alone",
        },
        {
            "vaHex": "0x004177d7",
            "method": "BltFast-like",
            "vtableOffsetHex": "0x1c",
            "role": "fast copy/update helper when graphics flag 0x80 is set",
            "flagSource": "caller flags OR 0x10",
            "boundary": "fast path; source color-key request is flag/context state",
        },
        {
            "vaHex": "0x0041784b",
            "method": "Blt",
            "vtableOffsetHex": "0x14",
            "role": "copy/update fallback when graphics flag 0x80 is clear",
            "flagSource": "caller flags OR 0x01000000",
            "boundary": "copy path fallback",
        },
        {
            "vaHex": "0x00417938",
            "method": "Blt",
            "vtableOffsetHex": "0x14",
            "role": "surface fill/clear helper",
            "flagSource": "immediate 0x01000400 with DDBLTFX-sized local block",
            "boundary": "fill/clear class; not source sprite transparency",
        },
        {
            "vaHex": "0x00417b4b",
            "method": "Blt",
            "vtableOffsetHex": "0x14",
            "role": "windowed/scaled presentation helper",
            "flagSource": "immediate 0x01000400 with DDBLTFX-sized local block",
            "boundary": "presentation/fill class",
        },
        {
            "vaHex": "0x00417b8a",
            "method": "Blt",
            "vtableOffsetHex": "0x14",
            "role": "windowed/scaled presentation helper",
            "flagSource": "immediate 0x01000400 with DDBLTFX-sized local block",
            "boundary": "presentation/fill class",
        },
        {
            "vaHex": "0x00417bdf",
            "method": "Blt",
            "vtableOffsetHex": "0x14",
            "role": "windowed/scaled presentation helper",
            "flagSource": "immediate 0x01000400 with DDBLTFX-sized local block",
            "boundary": "presentation/fill class",
        },
        {
            "vaHex": "0x00419b4a",
            "method": "Blt",
            "vtableOffsetHex": "0x14",
            "role": "generic wrapper draw, scaled/alternate branch",
            "flagSource": "global 0x004676f8",
            "boundary": "sprite/object draw; exact flag value still depends on init/runtime state",
        },
        {
            "vaHex": "0x00419bc4",
            "method": "BltFast-like",
            "vtableOffsetHex": "0x1c",
            "role": "generic wrapper draw, normal fast branch",
            "flagSource": "global 0x004676f4",
            "boundary": "sprite/object draw; exact flag value still depends on init/runtime state",
        },
        {
            "vaHex": "0x00419bf2",
            "method": "Blt",
            "vtableOffsetHex": "0x14",
            "role": "generic wrapper draw, normal fallback branch",
            "flagSource": "global 0x004676f4",
            "boundary": "sprite/object draw; exact flag value still depends on init/runtime state",
        },
    ]

    object_flag_consumers = [
        {
            "field": "object+0x28",
            "mask": "0x0000ffff",
            "consumerVaHex": "0x00416ceb",
            "meaning": "active object source/frame index",
            "status": "grounded",
        },
        {
            "field": "object+0x28",
            "mask": "0x00ff0000",
            "consumerVaHex": "0x00416cf9",
            "meaning": "active object surface-wrapper slot",
            "status": "grounded",
        },
        {
            "field": "object+0x2c",
            "mask": "0x00000208",
            "consumerVaHex": "0x00416d79 / 0x00419a2a",
            "meaning": "selects scaled/alternate rect branch and clipping helper 0x0041a742",
            "status": "grounded",
        },
        {
            "field": "object+0x2c",
            "mask": "0x00000007",
            "consumerVaHex": "0x0041712d / 0x00419af9 / 0x00419b5f",
            "meaning": "selects clip/viewport rect slot from 0x0055ab08 + (flags & 7) * 0x10",
            "status": "grounded",
        },
        {
            "field": "object+0x2d",
            "mask": "0x02",
            "consumerVaHex": "0x00416dfd / 0x00419a3c",
            "meaning": "applies centered scale/anchor correction using object+0x30/+0x34 and wrapper cell size",
            "status": "grounded",
        },
        {
            "field": "object+0x30 / object+0x34",
            "mask": None,
            "consumerVaHex": "0x00416d86 / 0x00419a46",
            "meaning": "16.16-like scale/anchor factors used only in scaled/centered branches",
            "status": "grounded-as-factor",
        },
    ]

    return {
        "kind": "hwanse-draw-flags-color-key-review",
        "status": "object-flag-and-blt-flag-boundary-reviewed",
        "source": [
            "Hwanse2.exe",
            "docs/DIRECTDRAW_NOTES.md",
            "out/directdraw_flow.json",
            "out/render_pipeline_model_review.json",
            "out/surface_wrapper_catalog_review.json",
        ],
        "summary": {
            "objectFlagField": "object+0x2c",
            "lowBitsClipSlotMask": "0x00000007",
            "scaledBranchMask": "0x00000208",
            "centeredScaleBit": "object+0x2d bit 0x02",
            "clipRectTable": "0x0055ab08 + (object+0x2c & 7) * 0x10",
            "surfaceWrapperTable": "0x0055abd8",
            "normalDrawFlagGlobal": "0x004676f4",
            "scaledDrawFlagGlobal": "0x004676f8",
            "knownBltCalls": len(direct_draw_calls),
            "byteEvidenceRows": len(byte_rows),
            "colorKeyConclusion": (
                "CNS pixels and palette entries are not enough to decide transparency. "
                "The original renderer requests opaque/fill/source-color-key behavior through DirectDraw call flags "
                "and draw context. object+0x2c controls clipping/scale branches; the exact color-key flag value is "
                "carried by caller arguments or globals 0x004676f4/0x004676f8."
            ),
        },
        "objectFlagConsumers": object_flag_consumers,
        "directDrawCallClasses": direct_draw_calls,
        "byteEvidence": byte_rows,
        "verification": {
            "hasExe": EXE.exists(),
            "object2cScaledBranchBytesAvailable": byte_by_va.get("0x00416d79", {}).get("available") is True,
            "object2cGenericBranchBytesAvailable": byte_by_va.get("0x00419a2a", {}).get("available") is True,
            "normalBltFlagGlobalGrounded": byte_by_va.get("0x00419bd1", {}).get("available") is True,
            "surfaceCopyBltCallGrounded": byte_by_va.get("0x00417722", {}).get("available") is True,
            "notSimplyTransparencyBit": True,
        },
        "modelBoundaries": [
            {
                "area": "object+0x2c",
                "conclusion": "partially decoded draw-control field: low 3 clip-slot bits plus 0x0208 branch flag; not a standalone transparency bit",
                "status": "promoted",
            },
            {
                "area": "DirectDraw flags",
                "conclusion": "call/context state carries copy/fill/source-color-key behavior; exact global values remain init/runtime dependent",
                "status": "partial",
            },
            {
                "area": "CNS transparent pixels",
                "conclusion": "source pixels remain necessary but insufficient; draw call must request source color-key semantics",
                "status": "boundary-fixed",
            },
        ],
        "openQuestions": [
            "Trace initialization/writes for globals 0x004676f4 and 0x004676f8 to name the exact DirectDraw flag set used by sprite/object draws.",
            "Trace the 0x0055ab08 rect table initializer to name clip slot 0..7 semantically.",
            "Confirm whether the vtable +0x1c fast path is the executable's BltFast-compatible slot or a wrapper-specific surface method layout.",
            "Tie individual battle/helper draw records to object+0x2c values when runtime samples or additional static writers are found.",
        ],
    }


def main() -> int:
    report = build_report()
    OUT.mkdir(exist_ok=True)
    (OUT / "draw_flags_color_key_review.json").write_text(
        json.dumps(report, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
