use super::{bcd::BCDValue, error_correction::{ECC, EDC}, lsb_msb::LittleEndianI16, time::Time};

pub const SECTOR_SIZE:usize = 2352;

const _: () = assert!(std::mem::size_of::<Mode0>()      == SECTOR_SIZE, "Mode0 wrong Sector size");
const _: () = assert!(std::mem::size_of::<Mode1>()      == SECTOR_SIZE, "Mode1 wrong Sector size");
const _: () = assert!(std::mem::size_of::<Mode2Form1>() == SECTOR_SIZE, "Mode2From1 wrong Sector size");
const _: () = assert!(std::mem::size_of::<Mode2Form2>() == SECTOR_SIZE, "Mode2From2 wrong Sector size");

macro_rules! bit_getter_setter {
    ($value:expr, $bit:expr, $name:expr) => {
        paste::item! {
            pub fn [< set_ $name >](&mut self) {
                self.$value |= (1 << $bit);
            }

            pub fn [< clear_ $name >](&mut self) {
                self.$value &= !(1 << $bit);
            }

            pub fn [< is_ $name >](&self) -> bool {
                (self.$value & (1 << $bit)) != 0
            }        
        }
    };
}

pub enum HeaderMode {
    Mode0 = 0,
    Mode1 = 1,
    Mode2 = 2,
}

pub enum SubHeaderForm {
    Form1,
    Form2
}

#[repr(packed(1))]
#[derive(Clone, Copy)]
pub struct Sync {
    value: [u8; 12],
}

impl Sync {
    pub fn new() -> Sync {
        Sync{value: [0x0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0]}
    }
}

impl std::fmt::Display for Sync {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "< ")?;
        for byte in self.value {
            write!(f, "{} ", byte)?;
        }
        write!(f, ">")
    }
}

#[repr(packed(1))]
#[derive(Clone, Copy)]
pub struct Header {
    minute: BCDValue,
    second: BCDValue,
    sector: BCDValue,
    mode:   u8
}

impl Header {
    pub fn new(mode: u8) -> Header {
        Header{minute: BCDValue::from(0), second: BCDValue::from(0), sector: BCDValue::from(0), mode}
    }

    pub fn set_time(&mut self, time: Time) {
        let (min, sec, sector) = time.to_bcd();

        self.minute = min;
        self.second = sec;
        self.sector = sector;
    }

    pub fn get_time(&self) -> Time {
        Time::from((self.minute.clone(), self.second.clone(), self.sector.clone()))
    }

    pub fn get_mode(&self) -> Result<HeaderMode, u8> {
        match self.mode {
            0 => Ok(HeaderMode::Mode0),
            1 => Ok(HeaderMode::Mode1),
            2 => Ok(HeaderMode::Mode2),
            _ => Err(self.mode),
        }
    }
}

impl std::fmt::Display for Header {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "min: {}, sec: {}, sector: {}, mode: {}", u8::from(&self.minute), u8::from(&self.second), u8::from(&self.sector), self.mode)
    }
}

impl Default for Header {
    fn default() -> Self {
        Header{minute: BCDValue::from(0), second: BCDValue::from(0), sector: BCDValue::from(0), mode: 0}
    }
}

#[repr(packed(1))]
#[derive(Clone, Copy)]
pub struct SubHeader {
    pub file_number:    u8,
    pub channel_number: u8,
    pub sub_mode:       SubMode,
    pub coding_info:    CodingInfo,
}

impl SubHeader {
    pub fn default_form1() -> SubHeader {
        SubHeader{file_number: 0, channel_number: 0, sub_mode: SubMode::default_form1(), coding_info: CodingInfo::default_form1()}
    }

    pub fn default_form2() -> SubHeader {
        SubHeader{file_number: 0, channel_number: 0, sub_mode: SubMode::default_form2(), coding_info: CodingInfo::default_form2()}
    }

    pub fn get_form(&self) -> SubHeaderForm {
        if self.sub_mode.is_form2() {
            SubHeaderForm::Form2
        }

        else {
            SubHeaderForm::Form1
        }
    }
}

impl std::fmt::Display for SubHeader {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "file: {}, channel: {}\nSubmode:\n{}\nCodinginfo:\n\t{}", self.file_number, self.channel_number, self.sub_mode, {
            if self.sub_mode.is_data() {
                &"Reserved (0x00)" as &dyn std::fmt::Display
            }

            else {
                &self.coding_info as &dyn std::fmt::Display
            }
        })
    }
}

impl Default for SubHeader {
    fn default() -> Self {
        SubHeader{file_number: 0, channel_number: 0, sub_mode: SubMode::default(), coding_info: CodingInfo::default()}
    }
}

#[repr(packed(1))]
#[derive(Clone, Copy)]
pub struct SubMode {
    value: u8
}

impl SubMode {
    pub fn default_form1() -> SubMode {
        let mut sub_mode = SubMode{value: 0};

        sub_mode.set_data();
        sub_mode.clear_form2();
        sub_mode
    }

    pub fn default_form2() -> SubMode {
        let mut sub_mode = SubMode{value: 0};

        sub_mode.set_audio();
        sub_mode.set_form2();
        sub_mode
    }

    bit_getter_setter!(value, 0, eor);
    bit_getter_setter!(value, 1, video);
    bit_getter_setter!(value, 2, audio);
    bit_getter_setter!(value, 3, data);
    bit_getter_setter!(value, 4, trigger);
    bit_getter_setter!(value, 5, form2);
    bit_getter_setter!(value, 6, real_time);
    bit_getter_setter!(value, 7, eof);
}

impl std::fmt::Display for SubMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "\tEOR:      {}, Video: {}\n\tAudio:    {}, Data:  {}\n\tTrigger:  {}, Form2: {}\n\tRealTime: {}, EOF:   {}", 
        self.is_eor(), self.is_video(), self.is_audio(), self.is_data(), self.is_trigger(), self.is_form2(), self.is_real_time(), self.is_eof())
    }
}

impl Default for SubMode {
    fn default() -> Self {
        SubMode{value: 0}
    }
}

#[repr(packed(1))]
#[derive(Clone, Copy)]
pub struct CodingInfo {
    value: u8
}

impl CodingInfo {
    pub fn default_form1() -> CodingInfo {
        CodingInfo{value: 0}
    }

    pub fn default_form2() -> CodingInfo {
        CodingInfo{value: 1}
    }

    pub fn get_sound_type(&self) -> XAADPCMSound {
        match self.value & (0b11) {
            0 => XAADPCMSound::Mono,
            1 => XAADPCMSound::Stereo,
            _ => XAADPCMSound::Reserved
        }
    }

    pub fn set_sound_type(&mut self, sound_type: XAADPCMSound) {
        self.value &= !0b11;
        self.value |= sound_type as u8;
    }

    pub fn get_sample_rate(&self) -> XAADPCMSampleRate {
        match (self.value >> 2) & 0b11 {
            0 => XAADPCMSampleRate::Freq37800Hz,
            1 => XAADPCMSampleRate::Freq18900Hz,
            _ => XAADPCMSampleRate::Reserved
        }
    }

    pub fn set_sample_rate(&mut self, sample_rate: XAADPCMSampleRate) {
        self.value &= !(0b11 << 2);
        self.value |= (sample_rate as u8) << 2;
    }

    pub fn get_bits_per_sample(&self) -> XAADPCMBitsPerSample {
        match (self.value >> 4) & 0b11 {
            0 => XAADPCMBitsPerSample::Normal,
            1 => XAADPCMBitsPerSample::High,
            _ => XAADPCMBitsPerSample::Reserved
        }
    }

    pub fn set_bits_per_sample(&mut self, bits: XAADPCMBitsPerSample) {
        self.value &= !(0b11 << 4);
        self.value |= (bits as u8) << 4;
    }

    pub fn has_emphasis(&self) -> bool {
        self.value & (1 << 6) != 0
    }

    pub fn set_emphasis(&mut self, has_emphasis: bool) {
        let emphasis = {
            if has_emphasis {
                1 << 6
            }

            else {
                0
            }
        };

        self.value &= !(1 << 6);
        self.value |= emphasis;
    }
}

impl std::fmt::Display for CodingInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Sound: {} Sample Rate: {} Bits per Sample: {}, Emphasis: {}", self.get_sound_type().as_str(), self.get_sample_rate().as_str(), self.get_bits_per_sample().as_str(), self.has_emphasis())
    }
}

impl Default for CodingInfo {
    fn default() -> Self {
        CodingInfo{value: 0}
    }
}

#[repr(packed(1))]
#[derive(Copy, Clone, Debug)]
pub struct AudioSample {
    pub left_sample:  LittleEndianI16,
    pub right_sample: LittleEndianI16
}

impl AudioSample {
    pub fn new(left: i16, right: i16) -> AudioSample {
        AudioSample{left_sample: LittleEndianI16::new(left), right_sample: LittleEndianI16::new(right)}
    }
}

impl std::default::Default for AudioSample {
    fn default() -> AudioSample {
        AudioSample::new(0, 0)
    }
}

#[repr(packed(1))]
#[derive(Clone)]
pub struct Audio {
    pub samples: [AudioSample; Self::SAMPLE_SIZE]
}

impl Audio {
    pub const DATA_SIZE:usize   = 2352;
    pub const SAMPLE_SIZE:usize = Self::DATA_SIZE/std::mem::size_of::<AudioSample>();

    pub fn new() -> Audio {
        Audio{samples: [AudioSample::default(); Self::SAMPLE_SIZE]}
    }

    pub fn get_raw_samples(&self) -> &[u8] {
        unsafe {
            std::mem::transmute::<&[AudioSample; 588], &[u8; Self::DATA_SIZE]>(&self.samples)
        }
    }
}

#[repr(packed(1))]
#[derive(Clone)]
pub struct Mode0 {
    pub sync:   Sync,
    pub header: Header,
    pub zero:   [u8;Mode0::DATA_SIZE]
}

impl Mode0 {
    pub const DATA_SIZE:usize = 2336;

    pub fn new() -> Mode0 {
        Mode0{sync: Sync::new(), header: Header::new(0), zero: [0; 2336]}
    }
}

#[repr(packed(1))]
#[derive(Clone)]
pub struct Mode1 {
    pub sync:   Sync,
    pub header: Header,
    pub data:   [u8; Mode1::DATA_SIZE],
    pub edc:    EDC,
    pub zero:   [u8; 8],
    pub ecc:    ECC
}

impl Mode1 {
    pub const DATA_SIZE:usize = 2048;

    pub fn new() -> Mode1 {
        Mode1{sync: Sync::new(), header: Header::new(1), data: [0; Self::DATA_SIZE], edc: EDC::default(), zero: [0u8; 8], ecc: ECC::default()}
    }

    pub fn finalize(&mut self) {
        self.calculate_edc();
        self.calculate_ecc();
    }

    pub fn calculate_edc(&mut self) {
        unsafe {
            let raw  = std::mem::transmute::<&Mode1, &[u8;std::mem::size_of::<Mode1>()]>(self);
            self.edc = EDC::calculate_for(&raw[0..=0x80F]);
        }
    }

    pub fn calculate_ecc(&mut self) {
        ECC::calculate_for(self);
    }
}

pub type Mode2 = Mode2Form1;

#[repr(packed(1))]
#[derive(Clone)]
pub struct Mode2Form1 {
    pub sync:            Sync,
    pub header:          Header,
    pub sub_header:      SubHeader,
    pub copy_sub_header: SubHeader,
    pub data:            [u8; Mode2Form1::DATA_SIZE],
    pub edc:             EDC,
    pub ecc:             ECC
}

impl Mode2Form1 {
    pub const DATA_SIZE:usize = 2048;

    pub fn new() -> Mode2Form1 {
        let sub_header      = SubHeader::default_form1();
        let copy_sub_header = sub_header.clone();

        Mode2Form1{sync: Sync::new(), header: Header::new(2), sub_header, copy_sub_header, data: [0; Self::DATA_SIZE], edc: EDC::default(), ecc: ECC::default()}
    }

    pub fn set_end_of_file(&mut self) {
        self.sub_header.sub_mode.set_eor();
        self.sub_header.sub_mode.set_eof();
    }

    pub fn finalize(&mut self) {
        self.copy_sub_header = self.sub_header.clone();

        self.calculate_edc();
        self.calculate_ecc();
    }

    pub fn calculate_edc(&mut self) {
        unsafe {
            let raw  = std::mem::transmute::<&Mode2Form1, &[u8;std::mem::size_of::<Mode2Form1>()]>(self);
            self.edc = EDC::calculate_for(&raw[0x10..=0x817]);
        }
    }

    pub fn calculate_ecc(&mut self) {
        let header = std::mem::take(&mut self.header);

        ECC::calculate_for(self);
        self.header = header;
    }
}

#[repr(packed(1))]
#[derive(Clone)]
pub struct Mode2Form2 {
    pub sync:            Sync,
    pub header:          Header,
    pub sub_header:      SubHeader,
    pub copy_sub_header: SubHeader,
    pub data:            [u8; Mode2Form2::DATA_SIZE],
    pub edc:             EDC,
}

impl Mode2Form2 {
    pub const DATA_SIZE:usize = 2324;

    pub fn new() -> Mode2Form2 {
        let sub_header      = SubHeader::default_form2();
        let copy_sub_header = sub_header.clone();

        Mode2Form2{sync: Sync::new(), header: Header::new(2), sub_header, copy_sub_header, data: [0; Self::DATA_SIZE], edc: EDC::default()}
    }

    pub fn set_end_of_file(&mut self) {
        self.sub_header.sub_mode.set_eor();
        self.sub_header.sub_mode.set_eof();
    }

    pub fn finalize(&mut self) {
        self.copy_sub_header = self.sub_header.clone();

        self.calculate_edc();
    }

    pub fn calculate_edc(&mut self) {
        unsafe {
            let raw  = std::mem::transmute::<&Mode2Form2, &[u8;std::mem::size_of::<Mode2Form2>()]>(self);
            self.edc = EDC::calculate_for(&raw[0x10..=0x92B]);
        }
    }
}

pub enum XAADPCMSound {
    Mono     = 0,
    Stereo   = 1,
    Reserved = 2,
}

impl XAADPCMSound {
    pub fn as_str(&self) -> &str {
        match self {
            Self::Mono     => "Mono",
            Self::Stereo   => "Stereo",
            Self::Reserved => "Reserved"
        }
    }
}

pub enum XAADPCMBitsPerSample {
    Normal   = 0,
    High     = 1,
    Reserved = 2
}

impl XAADPCMBitsPerSample {
    pub fn as_str(&self) -> &str {
        match self {
            Self::Normal   => "Normal (4bit)",
            Self::High     => "High (8bit)",
            Self::Reserved => "Reserved"
        }
    }
}

pub enum XAADPCMSampleRate {
    Freq37800Hz = 0,
    Freq18900Hz = 1,
    Reserved    = 2,
}

impl XAADPCMSampleRate {
    pub fn as_str(&self) -> &str {
        match self {
            Self::Freq37800Hz => "37800Hz",
            Self::Freq18900Hz => "18900Hz",
            Self::Reserved    => "Reserved",
        }
    }
}
