import os
import sys
from pathlib import Path

TOOLS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(str(Path(TOOLS_DIR) / "build/sprite"))
from sprite_shading_profiles import SpriteShadingGroup, SpriteShadingProfile, Light

import json
import struct
from typing import Literal

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

GROUPS = [
    SpriteShadingGroup("TIK"),
    SpriteShadingGroup("KMR"),
    SpriteShadingGroup("ISK"),
    SpriteShadingGroup("HOS"),
    SpriteShadingGroup("OSR"),
    SpriteShadingGroup("KKJ"),
    SpriteShadingGroup("JSN"),
    SpriteShadingGroup("MIM"),
    SpriteShadingGroup("ARN"),
    SpriteShadingGroup("DGB"),
    SpriteShadingGroup("KZN"),
    SpriteShadingGroup("unused"),
    SpriteShadingGroup("ISK_BTL"),
]


def extract(input_data: bytes, endian: Literal["big", "little"] = "big") -> str:
    PROFILE_NAMES = [
        [
            "TIK_01",
            "TIK_02",
            "TIK_03",
            "TIK_04",
            "TIK_05",
            "TIK_06",
            "TIK_07",
            "TIK_08",
            "TIK_09",
            "TIK_10",
            "TIK_12",
            "TIK_14",
            "TIK_15",
            "TIK_18",
            "TIK_20",
            "TIK_21",
            "TIK_22",
            "TIK_23",
            "TIK_24",
            "TIK_25",
        ],
        ["KMR_30"],
        [
            "ISK_03",
            "ISK_04",
            "ISK_05",
            "ISK_06",
            "ISK_07",
            "ISK_08",
            "ISK_09",
            "ISK_10",
            "ISK_11",
            "ISK_12",
            "ISK_13",
            "ISK_14",
            "ISK_16",
            "ISK_UNUSED",
            "ISK_18",
            "ISK_19",
        ],
        ["HOS_01"],
        ["OSR_02"],
        [
            "KKJ_10",
            "KKJ_11",
            "KKJ_12",
            "KKJ_13",
            "KKJ_17",
            "KKJ_20",
            "KKJ_22",
            "KKJ_23",
            "KKJ_24",
            "KKJ_25",
            "KKJ_26",
            "KKJ_27",
        ],
        ["JAN_17", "JAN_19"],
        [
            "MIM_01",
            "MIM_02",
            "MIM_03",
            "MIM_04",
            "MIM_05",
            "MIM_06",
            "MIM_07",
            "MIM_08",
            "MIM_09",
            "MIM_11",
            "MIM_12",
        ],
        ["ARN_09", "ARN_10", "ARN_11", "ARN_12", "ARN_13"],
        ["DGB_06"],
        [
            "KZN_01",
            "KZN_02",
            "KZN_03",
            "KZN_04",
            "KZN_05",
            "KZN_06",
            "KZN_07",
            "KZN_08",
            "KZN_09",
            "KZN_10",
            "KZN_11",
            "KZN_17",
            "KZN_18",
            "KZN_19",
            "KZN_20",
            "KZN_22",
            "KZN_23",
        ],
        ["UNUSED"],
        ["BTL_ISK"],
    ]

    END = ">" if endian == "big" else "<"

    offsets_table = input_data[:0x1D0]
    lighting_data = input_data[0x1D0:]

    for g, group in enumerate(GROUPS):
        b = g * 8
        data_offset = struct.unpack(END + "i", offsets_table[b : b + 4])[0]
        pl_it = struct.unpack(END + "i", offsets_table[b + 4 : b + 8])[0]
        profile_list = []

        for _ in range(len(PROFILE_NAMES[g])):
            profile_list.append(struct.unpack(END + "i", offsets_table[pl_it : pl_it + 4])[0])
            pl_it += 4

        for j, pl_offset in enumerate(profile_list):
            profile_offset = data_offset + pl_offset
            num_lights = lighting_data[profile_offset]
            profile = SpriteShadingProfile(
                name=PROFILE_NAMES[g][j].lower(),
                ambient=[
                    lighting_data[profile_offset + 2],
                    lighting_data[profile_offset + 3],
                    lighting_data[profile_offset + 4],
                ],
                power=lighting_data[profile_offset + 5],
            )
            profile_offset += 6
            for _ in range(num_lights):
                profile.lights.append(
                    Light(
                        flags=lighting_data[profile_offset],
                        rgb=[
                            lighting_data[profile_offset + 1],
                            lighting_data[profile_offset + 2],
                            lighting_data[profile_offset + 3],
                        ],
                        pos=[
                            struct.unpack(
                                END + "h",
                                lighting_data[profile_offset + 4 : profile_offset + 6],
                            )[0],
                            struct.unpack(
                                END + "h",
                                lighting_data[profile_offset + 6 : profile_offset + 8],
                            )[0],
                            struct.unpack(
                                END + "h",
                                lighting_data[profile_offset + 8 : profile_offset + 10],
                            )[0],
                        ],
                        falloff=struct.unpack(
                            END + "f",
                            lighting_data[profile_offset + 10 : profile_offset + 14],
                        )[0],
                    )
                )
                profile_offset += 0x10
            group.profiles.append(profile)
    return json.dumps(
        [g.to_json() for g in GROUPS],
        sort_keys=False,
        indent=2,
    )


class N64SegPm_sprite_shading_profiles(Segment):
    OUT_DIR: Path = options.opts.asset_path / "sprite"

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

        self.json_out = extract(data)

    def split(self, rom_bytes):
        self.out_path().parent.mkdir(parents=True, exist_ok=True)
        with open(self.out_path(), "w") as f:
            f.write(self.json_out)

    def out_path(self) -> Path:
        return self.OUT_DIR / f"{self.name}.json"

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

        return [
            LinkerEntry(
                self,
                [self.OUT_DIR / f"{self.name}.json"],
                options.opts.asset_path / self.dir / f"{self.name}.bin",
                self.get_linker_section(),
                self.get_linker_section(),
            )
        ]
