#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-codec-h265x -- H.265/HEVC NAL Unit Parser
#[derive(Debug)] pub enum H265Error { InvalidNal(String), TruncatedData(String) }
impl std::fmt::Display for H265Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::InvalidNal(e)|Self::TruncatedData(e) => write!(f, "{e}") } } }
impl std::error::Error for H265Error {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum NalType { TrailN=0, TrailR=1, TsaN=2, TsaR=3, StsaN=4, StsaR=5, RadlN=6, RadlR=7, BlaWLp=16, IdrWRadl=19, IdrNLp=20, CraNut=21, VpsPkt=32, SpsPkt=33, PpsPkt=34, Sei=39, Unknown=63 }
impl NalType {
    pub fn from_u8(v: u8) -> Self { match v { 0=>Self::TrailN,1=>Self::TrailR,2=>Self::TsaN,3=>Self::TsaR,4=>Self::StsaN,5=>Self::StsaR,6=>Self::RadlN,7=>Self::RadlR,16=>Self::BlaWLp,19=>Self::IdrWRadl,20=>Self::IdrNLp,21=>Self::CraNut,32=>Self::VpsPkt,33=>Self::SpsPkt,34=>Self::PpsPkt,39=>Self::Sei,_=>Self::Unknown } }
    pub fn is_keyframe(&self) -> bool { matches!(self, Self::IdrWRadl|Self::IdrNLp|Self::CraNut|Self::BlaWLp) }
    pub fn is_vcl(&self) -> bool { (*self as u8) < 32 }
}
#[derive(Debug, Clone)] pub struct NalUnit { pub nal_type: NalType, pub nuh_layer_id: u8, pub nuh_temporal_id: u8, pub payload: Vec<u8> }
pub struct H265Parser;
impl H265Parser {
    pub fn parse_nal(data: &[u8]) -> Result<NalUnit, H265Error> {
        if data.len() < 2 { return Err(H265Error::TruncatedData("Need >= 2 bytes".into())); }
        let forbidden_zero = (data[0] >> 7) & 1;
        if forbidden_zero != 0 { return Err(H265Error::InvalidNal("Forbidden bit set".into())); }
        let nal_type_val = (data[0] >> 1) & 0x3F;
        let nuh_layer_id = ((data[0] & 1) << 5) | ((data[1] >> 3) & 0x1F);
        let nuh_temporal_id = (data[1] & 0x07).saturating_sub(1);
        Ok(NalUnit { nal_type: NalType::from_u8(nal_type_val), nuh_layer_id, nuh_temporal_id, payload: data[2..].to_vec() })
    }
    pub fn find_start_codes(data: &[u8]) -> Vec<usize> {
        let mut positions = Vec::new();
        let mut i = 0;
        while i + 2 < data.len() {
            if data[i] == 0 && data[i+1] == 0 && data[i+2] == 1 { positions.push(i + 3); i += 3; }
            else if i + 3 < data.len() && data[i] == 0 && data[i+1] == 0 && data[i+2] == 0 && data[i+3] == 1 { positions.push(i + 4); i += 4; }
            else { i += 1; }
        }
        positions
    }
}
#[cfg(test)] mod tests {
    use super::*;
    #[test] fn test_parse_idr() { let data = [0x26, 0x01, 0xFF]; let nal = H265Parser::parse_nal(&data).unwrap(); assert_eq!(nal.nal_type, NalType::IdrWRadl); assert!(nal.nal_type.is_keyframe()); }
    #[test] fn test_start_codes() { let data = [0,0,0,1,0x26,0x01,0,0,1,0x28,0x01]; let pos = H265Parser::find_start_codes(&data); assert_eq!(pos.len(), 2); }
}
