//! ISOBMFF box definitions and parsing

#![allow(dead_code)]

use alloc::string::String;
use alloc::vec::Vec;

/// Four-character code identifying a box type
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FourCC(pub [u8; 4]);

impl FourCC {
    /// File type box
    pub const FTYP: Self = Self(*b"ftyp");
    /// Metadata container box
    pub const META: Self = Self(*b"meta");
    /// Handler reference box
    pub const HDLR: Self = Self(*b"hdlr");
    /// Primary item box
    pub const PITM: Self = Self(*b"pitm");
    /// Item location box
    pub const ILOC: Self = Self(*b"iloc");
    /// Item information box
    pub const IINF: Self = Self(*b"iinf");
    /// Item information entry box
    pub const INFE: Self = Self(*b"infe");
    /// Item properties box
    pub const IPRP: Self = Self(*b"iprp");
    /// Item property container box
    pub const IPCO: Self = Self(*b"ipco");
    /// Item property association box
    pub const IPMA: Self = Self(*b"ipma");
    /// Media data box
    pub const MDAT: Self = Self(*b"mdat");
    /// Image spatial extents property
    pub const ISPE: Self = Self(*b"ispe");
    /// HEVC bitstream configuration (tile variant)
    pub const HVCB: Self = Self(*b"hvcB");
    /// HEVC decoder configuration
    pub const HVCC: Self = Self(*b"hvcC");
    /// Color information property
    pub const COLR: Self = Self(*b"colr");
    /// Pixel information property
    pub const PIXI: Self = Self(*b"pixi");
    /// Item reference box
    pub const IREF: Self = Self(*b"iref");
    /// Auxiliary type property
    pub const AUXC: Self = Self(*b"auxC");
    /// Item data box
    pub const IDAT: Self = Self(*b"idat");
    /// Derived image reference
    pub const DIMG: Self = Self(*b"dimg");
    /// Clean aperture property
    pub const CLAP: Self = Self(*b"clap");
    /// Image rotation property
    pub const IROT: Self = Self(*b"irot");
    /// Auxiliary image reference
    pub const AUXL: Self = Self(*b"auxl");
    /// Image mirror property
    pub const IMIR: Self = Self(*b"imir");
    /// Thumbnail reference
    pub const THMB: Self = Self(*b"thmb");
    /// Content describes reference (metadata item → image it describes)
    pub const CDSC: Self = Self(*b"cdsc");
    /// Content Light Level Information property
    pub const CLLI: Self = Self(*b"clli");
    /// Mastering Display Colour Volume property
    pub const MDCV: Self = Self(*b"mdcv");
    /// AV1 decoder configuration
    pub const AV1C: Self = Self(*b"av1C");
    /// Uncompressed codec configuration (ISO 23001-17)
    pub const UNCC: Self = Self(*b"uncC");
    /// Generic compression configuration (ISO 23001-17)
    pub const CMPC: Self = Self(*b"cmpC");

    // --- Movie (moov) structure boxes for image sequences ---

    /// Movie container box
    pub const MOOV: Self = Self(*b"moov");
    /// Track container box
    pub const TRAK: Self = Self(*b"trak");
    /// Track header box
    pub const TKHD: Self = Self(*b"tkhd");
    /// Media container box
    pub const MDIA: Self = Self(*b"mdia");
    /// Media header box
    pub const MDHD: Self = Self(*b"mdhd");
    /// Media information box
    pub const MINF: Self = Self(*b"minf");
    /// Sample table box
    pub const STBL: Self = Self(*b"stbl");
    /// Sample description box
    pub const STSD: Self = Self(*b"stsd");
    /// Sample size box
    pub const STSZ: Self = Self(*b"stsz");
    /// Chunk offset box (32-bit)
    pub const STCO: Self = Self(*b"stco");
    /// Chunk offset box (64-bit)
    pub const CO64: Self = Self(*b"co64");
    /// Sample-to-chunk box
    pub const STSC: Self = Self(*b"stsc");
    /// Sync sample box (I-frame indices)
    pub const STSS: Self = Self(*b"stss");
    /// HEVC sample entry type
    pub const HVC1: Self = Self(*b"hvc1");
    /// HEVC sample entry type (variant)
    pub const HEV1: Self = Self(*b"hev1");
    /// Track reference box
    pub const TREF: Self = Self(*b"tref");

    /// Create from bytes
    pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
        if bytes.len() >= 4 {
            Some(Self([bytes[0], bytes[1], bytes[2], bytes[3]]))
        } else {
            None
        }
    }

    /// Convert to string for debugging
    pub fn as_str(&self) -> &str {
        core::str::from_utf8(&self.0).unwrap_or("????")
    }
}

impl core::fmt::Display for FourCC {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// Raw box header
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub struct BoxHeader {
    /// Box type
    pub box_type: FourCC,
    /// Total box size including header
    pub size: u64,
    /// Offset to box content (after header)
    pub content_offset: usize,
}

/// A parsed ISOBMFF box
#[derive(Debug)]
pub struct Box<'a> {
    /// Box header
    pub header: BoxHeader,
    /// Box content (excluding header)
    pub content: &'a [u8],
}

impl<'a> Box<'a> {
    /// Get box type
    pub fn box_type(&self) -> FourCC {
        self.header.box_type
    }

    /// Get full box version and flags (for full boxes)
    #[allow(dead_code)]
    pub fn version_flags(&self) -> Option<(u8, u32)> {
        if self.content.len() >= 4 {
            let version = self.content[0];
            let flags = u32::from_be_bytes([0, self.content[1], self.content[2], self.content[3]]);
            Some((version, flags))
        } else {
            None
        }
    }
}

/// Box iterator for parsing sequential boxes
pub struct BoxIterator<'a> {
    data: &'a [u8],
    offset: usize,
}

impl<'a> BoxIterator<'a> {
    /// Create a new box iterator
    pub fn new(data: &'a [u8]) -> Self {
        Self { data, offset: 0 }
    }
}

impl<'a> Iterator for BoxIterator<'a> {
    type Item = Box<'a>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.offset + 8 > self.data.len() {
            return None;
        }

        let data = &self.data[self.offset..];

        // Read size (4 bytes, big-endian)
        let size_32 = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
        let box_type = FourCC::from_bytes(&data[4..8])?;

        let (size, header_size): (u64, usize) = if size_32 == 1 {
            // Extended size (64-bit)
            if data.len() < 16 {
                return None;
            }
            let ext_size = u64::from_be_bytes([
                data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
            ]);
            (ext_size, 16)
        } else if size_32 == 0 {
            // Box extends to end of file
            ((self.data.len() - self.offset) as u64, 8)
        } else {
            (size_32 as u64, 8)
        };

        let size_usize = usize::try_from(size).ok()?;
        if size_usize < header_size {
            return None;
        }
        let box_end = self.offset.checked_add(size_usize)?;
        if box_end > self.data.len() {
            return None;
        }

        let content = &data[header_size..size_usize];

        let box_item = Box {
            header: BoxHeader {
                box_type,
                size,
                content_offset: self.offset + header_size,
            },
            content,
        };

        self.offset = box_end;
        Some(box_item)
    }
}

/// Item location entry from iloc box
#[derive(Debug, Clone)]
pub struct ItemLocation {
    /// Item ID
    pub item_id: u32,
    /// Construction method (0=file, 1=idat, 2=item)
    pub construction_method: u8,
    /// Base offset
    pub base_offset: u64,
    /// Extents (offset, length pairs)
    pub extents: Vec<(u64, u64)>,
}

/// Item info entry from iinf/infe boxes
#[derive(Debug, Clone)]
pub struct ItemInfo {
    /// Item ID
    pub item_id: u32,
    /// Item type (e.g., "hvc1", "grid", "Exif")
    pub item_type: FourCC,
    /// Item name (optional)
    pub item_name: String,
    /// Content type (optional)
    pub content_type: String,
    /// Hidden flag
    pub hidden: bool,
}

/// Content Light Level Information from cLLi box (ISO 14496-12 / CEA-861.3).
///
/// Contains peak and frame-average brightness for HDR content.
#[derive(Debug, Clone, Copy)]
pub struct ContentLightLevelBox {
    /// Maximum Content Light Level (MaxCLL) in cd/m².
    pub max_content_light_level: u16,
    /// Maximum Frame-Average Light Level (MaxFALL) in cd/m².
    pub max_frame_average_light_level: u16,
}

/// Mastering Display Colour Volume from mDCv box (ISO 14496-12 / SMPTE ST 2086).
///
/// Describes the mastering display's primaries, white point, and luminance range.
/// Values are stored in the raw representation from the box.
#[derive(Debug, Clone, Copy)]
pub struct MasteringDisplayBox {
    /// Display primaries in CIE 1931 xy, in 0.00002 units (divide by 50000 for float).
    /// Order per SMPTE ST 2086 encoding: `[(x, y); 3]`.
    pub primaries_xy: [(u16, u16); 3],
    /// White point in CIE 1931 xy, in 0.00002 units `(x, y)`.
    pub white_point_xy: (u16, u16),
    /// Maximum luminance in 0.0001 cd/m² units (divide by 10000 for cd/m²).
    pub max_luminance: u32,
    /// Minimum luminance in 0.0001 cd/m² units (divide by 10000 for cd/m²).
    pub min_luminance: u32,
}

/// Clean aperture from clap box (ISO 14496-12)
#[derive(Debug, Clone, Copy)]
pub struct CleanAperture {
    /// Clean aperture width numerator
    pub width_n: u32,
    /// Clean aperture width denominator
    pub width_d: u32,
    /// Clean aperture height numerator
    pub height_n: u32,
    /// Clean aperture height denominator
    pub height_d: u32,
    /// Horizontal offset numerator (signed)
    pub horiz_off_n: i32,
    /// Horizontal offset denominator
    pub horiz_off_d: u32,
    /// Vertical offset numerator (signed)
    pub vert_off_n: i32,
    /// Vertical offset denominator
    pub vert_off_d: u32,
}

/// Image rotation from irot box
#[derive(Debug, Clone, Copy)]
pub struct ImageRotation {
    /// Rotation angle in degrees counter-clockwise (0, 90, 180, 270)
    pub angle: u16,
}

/// Image mirror from imir box
#[derive(Debug, Clone, Copy)]
pub struct ImageMirror {
    /// Mirror axis: 0 = vertical axis (left-right flip), 1 = horizontal axis (top-bottom flip)
    pub axis: u8,
}

/// Image spatial extents from ispe box
#[derive(Debug, Clone, Copy)]
pub struct ImageSpatialExtents {
    /// Width in pixels
    pub width: u32,
    /// Height in pixels
    pub height: u32,
}

/// HEVC decoder configuration from hvcC box
#[derive(Debug, Clone)]
pub struct HevcDecoderConfig {
    /// Configuration version
    pub config_version: u8,
    /// General profile space
    pub general_profile_space: u8,
    /// General tier flag
    pub general_tier_flag: bool,
    /// General profile IDC
    pub general_profile_idc: u8,
    /// General profile compatibility flags
    pub general_profile_compatibility_flags: u32,
    /// General constraint indicator flags
    pub general_constraint_indicator_flags: u64,
    /// General level IDC
    pub general_level_idc: u8,
    /// Chroma format
    pub chroma_format: u8,
    /// Bit depth luma minus 8
    pub bit_depth_luma_minus8: u8,
    /// Bit depth chroma minus 8
    pub bit_depth_chroma_minus8: u8,
    /// Length size minus one
    pub length_size_minus_one: u8,
    /// NAL units (VPS, SPS, PPS, etc.)
    pub nal_units: Vec<Vec<u8>>,
}

/// AV1 decoder configuration from av1C box (ISO 14496-12 / AV1 Codec ISO Media File Format)
#[derive(Debug, Clone)]
pub struct Av1DecoderConfig {
    /// AV1 sequence profile (0=Main, 1=High, 2=Professional)
    pub seq_profile: u8,
    /// AV1 sequence level index for operating point 0
    pub seq_level_idx_0: u8,
    /// High bitdepth flag (10 or 12 bit when true)
    pub high_bitdepth: bool,
    /// Twelve bit flag (only meaningful when high_bitdepth is true)
    pub twelve_bit: bool,
    /// Monochrome flag (no chroma planes)
    pub monochrome: bool,
    /// Chroma subsampling X (true = subsampled horizontally)
    pub chroma_subsampling_x: bool,
    /// Chroma subsampling Y (true = subsampled vertically)
    pub chroma_subsampling_y: bool,
    /// Raw OBU data to prepend before decode (sequence header, metadata OBUs, etc.)
    pub config_obus: Vec<u8>,
}

impl Av1DecoderConfig {
    /// Bit depth derived from the config flags
    pub fn bit_depth(&self) -> u8 {
        if !self.high_bitdepth {
            8
        } else if self.twelve_bit {
            12
        } else {
            10
        }
    }

    /// Chroma format: 0=mono, 1=4:2:0, 2=4:2:2, 3=4:4:4
    pub fn chroma_format(&self) -> u8 {
        if self.monochrome {
            0
        } else if self.chroma_subsampling_x && self.chroma_subsampling_y {
            1 // 4:2:0
        } else if self.chroma_subsampling_x {
            2 // 4:2:2
        } else {
            3 // 4:4:4
        }
    }
}

/// Uncompressed codec configuration from uncC box (ISO 23001-17)
#[derive(Debug, Clone)]
pub struct UncompressedConfig {
    /// Profile (0 for base)
    pub profile: u32,
    /// Component descriptors
    pub components: Vec<UncompressedComponent>,
    /// Sampling type: 0=no subsampling, 1=4:2:2, 2=4:2:0, 3=4:1:1
    pub sampling_type: u8,
    /// Interleave type: 0=component, 1=pixel, 2=mixed, 3=row, 4=tile, 5=multi-y
    pub interleave_type: u8,
    /// Block size (0=no blocks)
    pub block_size: u8,
    /// Components stored in little-endian byte order
    pub components_little_endian: bool,
    /// Block padding is at LSB (vs MSB)
    pub block_pad_lsb: bool,
    /// Blocks stored in little-endian
    pub block_little_endian: bool,
    /// Block byte order is reversed
    pub block_reversed: bool,
    /// Padding bits have unknown/arbitrary values
    pub pad_unknown: bool,
    /// Fixed pixel size in bytes (0=variable)
    pub pixel_size: u8,
    /// Row alignment in bytes (0=no alignment)
    pub row_align_size: u32,
    /// Tile alignment in bytes (0=no alignment)
    pub tile_align_size: u32,
    /// Number of tile columns minus one
    pub num_tile_cols_minus_one: u32,
    /// Number of tile rows minus one
    pub num_tile_rows_minus_one: u32,
}

/// Component descriptor within an uncompressed codec configuration
#[derive(Debug, Clone)]
pub struct UncompressedComponent {
    /// Component index (identifies the color channel)
    pub component_index: u16,
    /// Bit depth minus one
    pub component_bit_depth_minus_one: u8,
    /// Component format: 0=unsigned int, 1=float
    pub component_format: u8,
    /// Component alignment in bytes (0=none)
    pub component_align_size: u8,
}

/// Generic compression configuration from cmpC box (ISO 23001-17)
#[derive(Debug, Clone)]
pub struct CompressionConfig {
    /// Compression type FourCC: `defl` (deflate), `zlib`, `brot` (brotli)
    pub compression_type: FourCC,
}

/// Color information from colr box
#[derive(Debug, Clone)]
pub enum ColorInfo {
    /// ICC profile
    IccProfile(Vec<u8>),
    /// nclx color parameters
    Nclx {
        /// Color primaries
        color_primaries: u16,
        /// Transfer characteristics
        transfer_characteristics: u16,
        /// Matrix coefficients
        matrix_coefficients: u16,
        /// Full range flag
        full_range: bool,
    },
}

/// Ordered transformative property for an item.
/// HEIF spec requires these be applied in the order listed in ipma.
#[derive(Debug, Clone, Copy)]
pub enum Transform {
    /// Clean aperture crop (clap)
    CleanAperture(CleanAperture),
    /// Image mirror (imir)
    Mirror(ImageMirror),
    /// Image rotation (irot)
    Rotation(ImageRotation),
}

/// Item property (indexed in ipco)
#[derive(Debug, Clone)]
pub enum ItemProperty {
    /// Image spatial extents (ispe)
    ImageExtents(ImageSpatialExtents),
    /// HEVC decoder config (hvcC)
    HevcConfig(HevcDecoderConfig),
    /// AV1 decoder config (av1C)
    Av1Config(Av1DecoderConfig),
    /// Uncompressed codec config (uncC)
    UncompressedConfig(UncompressedConfig),
    /// Generic compression config (cmpC)
    CompressionConfig(CompressionConfig),
    /// Color info (colr)
    ColorInfo(ColorInfo),
    /// Clean aperture (clap)
    CleanAperture(CleanAperture),
    /// Image rotation (irot)
    Rotation(ImageRotation),
    /// Image mirror (imir)
    Mirror(ImageMirror),
    /// Auxiliary type (auxC) — URN string + optional subtype data
    AuxiliaryType(AuxiliaryTypeProperty),
    /// Content Light Level Information (cLLi)
    ContentLightLevel(ContentLightLevelBox),
    /// Mastering Display Colour Volume (mDCv)
    MasteringDisplay(MasteringDisplayBox),
    /// Unknown property
    Unknown,
}

/// Parsed `auxC` (auxiliary type) property box.
///
/// Contains the null-terminated URN string that identifies the auxiliary type
/// and any additional subtype data bytes that follow.
#[derive(Debug, Clone)]
pub struct AuxiliaryTypeProperty {
    /// Auxiliary type URN (e.g., `urn:mpeg:hevc:2015:auxid:2` for depth)
    pub aux_type: String,
    /// Subtype data bytes following the null-terminated URN.
    /// For depth images, this contains the depth representation info.
    pub subtype_data: Vec<u8>,
}

/// Item property association
#[derive(Debug, Clone)]
pub struct PropertyAssociation {
    /// Item ID
    pub item_id: u32,
    /// Property indices (1-based, essential flag)
    pub properties: Vec<(u16, bool)>,
}

/// Item reference (from iref box)
#[derive(Debug, Clone)]
pub struct ItemReference {
    /// Reference type (e.g., "dimg" for derived image)
    pub reference_type: FourCC,
    /// Source item ID
    pub from_item_id: u32,
    /// Referenced item IDs
    pub to_item_ids: Vec<u32>,
}
