from __future__ import annotations

from dataclasses import dataclass
from typing import Dict, List
import struct
from io import BytesIO

from .base import MP4Box
from .hvcc import HEVCConfigurationBox
from .btrt import BitRateBox
from .colr import ColourInformationBox
from .pasp import PixelAspectRatioBox
from .fiel import FieldHandlingBox


@dataclass
class HEVCSampleEntry(MP4Box):
    """HEVC Sample Entry (``hev1``)."""

    data_reference_index: int = 0
    width: int = 0
    height: int = 0
    horizresolution: int = 0
    vertresolution: int = 0
    frame_count: int = 0
    compressorname: str = ""
    depth: int = 0

    @classmethod
    def from_parsed(
        cls,
        box_type: str,
        size: int,
        offset: int,
        data: bytes,
        children: List[MP4Box] | None = None,
    ) -> "HEVCSampleEntry":
        data_reference_index = (
            struct.unpack(">H", data[6:8])[0] if len(data) >= 8 else 0
        )
        width = struct.unpack(">H", data[24:26])[0] if len(data) >= 26 else 0
        height = struct.unpack(">H", data[26:28])[0] if len(data) >= 28 else 0
        horizresolution = struct.unpack(">I", data[28:32])[0] if len(data) >= 32 else 0
        vertresolution = struct.unpack(">I", data[32:36])[0] if len(data) >= 36 else 0
        frame_count = struct.unpack(">H", data[40:42])[0] if len(data) >= 42 else 0
        name_len = data[42] if len(data) >= 43 else 0
        compressorname = (
            data[43 : 43 + name_len].decode("utf-8", "ignore")
            if len(data) >= 43 + name_len
            else ""
        )
        depth = struct.unpack(">H", data[74:76])[0] if len(data) >= 76 else 0

        remaining = data[78:]
        child_offset_base = offset + 8 + 78
        stream = BytesIO(remaining)
        parsed_children: List[MP4Box] = []
        pos = 0
        while pos + 8 <= len(remaining):
            header = stream.read(8)
            if len(header) < 8:
                break
            child_size, child_type = struct.unpack(">I4s", header)
            if child_size < 8:
                break
            child_type_str = child_type.decode("latin-1")
            payload = stream.read(child_size - 8)
            if child_type_str == "hvcC":
                child = HEVCConfigurationBox.from_parsed(
                    child_type_str, child_size, child_offset_base + pos, payload, []
                )
            elif child_type_str == "btrt":
                child = BitRateBox.from_parsed(
                    child_type_str, child_size, child_offset_base + pos, payload, []
                )
            elif child_type_str == "colr":
                child = ColourInformationBox.from_parsed(
                    child_type_str, child_size, child_offset_base + pos, payload, []
                )
            elif child_type_str == "pasp":
                child = PixelAspectRatioBox.from_parsed(
                    child_type_str, child_size, child_offset_base + pos, payload, []
                )
            elif child_type_str == "fiel":
                child = FieldHandlingBox.from_parsed(
                    child_type_str, child_size, child_offset_base + pos, payload, []
                )
            else:
                child = MP4Box(
                    child_type_str, child_size, child_offset_base + pos, [], payload
                )
            parsed_children.append(child)
            pos += child_size
            stream.seek(pos)

        return cls(
            box_type,
            size,
            offset,
            parsed_children,
            None,
            data_reference_index,
            width,
            height,
            horizresolution,
            vertresolution,
            frame_count,
            compressorname,
            depth,
        )

    def properties(self) -> Dict[str, object]:
        props = super().properties()
        props.update(
            {
                "data_reference_index": self.data_reference_index,
                "width": self.width,
                "height": self.height,
                "horizresolution": self.horizresolution,
                "vertresolution": self.vertresolution,
                "frame_count": self.frame_count,
                "compressorname": self.compressorname,
                "depth": self.depth,
            }
        )
        return props
