import os
import struct
import sys
from pathlib import Path
from typing import List, Tuple

TOOLS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(str(Path(TOOLS_DIR) / "build/imgfx"))
from imgfx_data import Anim, Triangle, Vertex

from splat.segtypes.segment import Segment
from splat.util import log, options


class N64SegPm_imgfx_data(Segment):
    anims: List[Anim] = []

    OUT_DIR: Path = options.opts.asset_path / "imgfx"

    def scan(self, rom_bytes) -> None:
        data = rom_bytes[self.rom_start : self.rom_end]

        for name, offset in self.yaml.get("animations"):
            pos = offset

            # Animations start with vtx data which is followed by gfx data and ends with the header at `offset`
            (
                vtx_offset,
                gfx_offset,
                vtx_count,
                gfx_count,
                keyframes,
                flags,
            ) = struct.unpack(">IIHHHH", data[pos : pos + 16])

            frames: List[List[Vertex]] = []

            pos = vtx_offset
            for i in range(keyframes):
                frame: List[Vertex] = []

                for j in range(vtx_count):
                    x, y, z, u, v, r, g, b, a = struct.unpack(">hhhBBbbbB", data[pos : pos + 12])
                    pos += 12
                    frame.append(Vertex(j, x, y, z, u, v, r, g, b, a))

                frames.append(frame)

            triangles: List[Triangle] = []
            vtx_buf: List[int] = []

            # Align to 8 bytes
            pos = (pos + 7) & ~(8 - 1)
            assert pos == gfx_offset

            def unpack_tri(word: int, vtx_buf: List[int]) -> Tuple[int, int, int]:
                i = (word >> 16) & 0xFF
                j = (word >> 8) & 0xFF
                k = word & 0xFF

                i = vtx_buf[int(i / 2)]
                j = vtx_buf[int(j / 2)]
                k = vtx_buf[int(k / 2)]

                return (i, j, k)

            for i in range(gfx_count):
                w0, w1 = struct.unpack(">II", data[pos : pos + 8])
                pos += 8

                op = (w0 >> 24) & 0xFF

                if op == 1:  # G_VTX
                    num = w0 >> 12 & 0xFF
                    end = int((w0 & 0xFF) / 2)
                    src_idx = int((w1 - vtx_offset) / 12)
                    start = end - num

                    for j in range(num):
                        to_place_pos = start + j
                        while len(vtx_buf) <= to_place_pos:
                            vtx_buf.append(0)
                        vtx_buf[to_place_pos] = src_idx + j
                elif op == 5:  # G_TRI1
                    triangles.append(Triangle(*unpack_tri(w0, vtx_buf)))
                elif op == 6:  # G_TRI2
                    triangles.append(Triangle(*unpack_tri(w0, vtx_buf)))
                    triangles.append(Triangle(*unpack_tri(w1, vtx_buf)))
                elif op == 0xDF:  # G_ENDDL
                    pass
                else:
                    log.error(f"Unknown op: {op}")

            self.anims.append(
                Anim(
                    name,
                    offset,
                    vtx_offset,
                    gfx_offset,
                    vtx_count,
                    gfx_count,
                    keyframes,
                    flags,
                    frames,
                    triangles,
                )
            )

    def split(self, rom_bytes):
        self.OUT_DIR.mkdir(parents=True, exist_ok=True)
        for anim in self.anims:
            with open(f"{self.OUT_DIR}/{anim.name}.json", "w") as f:
                f.write(anim.toJSON())

    def get_linker_entries(self):
        from splat.segtypes.linker_entry import LinkerEntry

        return [
            LinkerEntry(
                self,
                [self.OUT_DIR / f"{name}.json" for name, _ in self.yaml.get("animations")],
                options.opts.asset_path / "imgfx" / f"{self.name}.c",
                self.get_linker_section(),
                self.get_linker_section(),
            )
        ]
