use super::super::types::{error_correction::*, sector::*};

#[derive(Clone)]
pub enum Sector {
    Audio(Audio),
    Empty(Mode0),
    CDData(Mode1),
    CDXAData(Mode2Form1),
    CDXAAudio(Mode2Form2),
}

impl Sector {
    pub fn get_header(&self) -> Option<&Header> {
        match self {
            Sector::Audio(_)       => None,
            Sector::Empty(sec)     => Some(&sec.header),
            Sector::CDData(sec)    => Some(&sec.header),
            Sector::CDXAData(sec)  => Some(&sec.header),
            Sector::CDXAAudio(sec) => Some(&sec.header),
        }
    }

    pub fn get_header_mut(&mut self) -> Option<&mut Header> {
        match self {
            Sector::Audio(_)       => None,
            Sector::Empty(sec)     => Some(&mut sec.header),
            Sector::CDData(sec)    => Some(&mut sec.header),
            Sector::CDXAData(sec)  => Some(&mut sec.header),
            Sector::CDXAAudio(sec) => Some(&mut sec.header),
        }
    }

    pub fn get_sub_header(&self) -> Option<&SubHeader> {
        match self {
            Sector::CDXAData(sec)  => Some(&sec.sub_header),
            Sector::CDXAAudio(sec) => Some(&sec.sub_header),
            _                      => None
        }
    }

    pub fn get_error_correction(&self) -> Option<(&EDC, Option<&ECC>)> {
        match self {
            Sector::CDData(sector)    => Some((&sector.edc, Some(&sector.ecc))),
            Sector::CDXAData(sector)  => Some((&sector.edc, Some(&sector.ecc))),
            Sector::CDXAAudio(sector) => Some((&sector.edc, None)),
            _ => None
        }
    }

    pub fn get_data_offset_with_size(&self) -> (usize, usize) {
        match self {
            Sector::Audio(_)     => (0x0,  0x930),
            Sector::Empty(_)     => (0x10, 0x920),
            Sector::CDData(_)    => (0x10, 0x800),
            Sector::CDXAData(_)  => (0x18, 0x800),
            Sector::CDXAAudio(_) => (0x18, 0x914)
        }
    }

    pub fn get_friendly_name(&self) -> &str {
        match self {
            Sector::Audio(_)     => "Audio",
            Sector::Empty(_)     => "Mode0 (Empty)",
            Sector::CDData(_)    => "Mode1 (Original CDROM)",
            Sector::CDXAData(_)  => "Mode2/Form1 (CD-XA Data)",
            Sector::CDXAAudio(_) => "Mode2/Form2 (CD-XA Audio)"
        }
    }

    pub fn get_data(&self) -> &[u8] {
        match self {
            Sector::Audio(sector)     => sector.get_raw_samples(),
            Sector::Empty(sector)     => sector.zero.as_slice(),
            Sector::CDData(sector)    => sector.data.as_slice(),
            Sector::CDXAData(sector)  => sector.data.as_slice(),
            Sector::CDXAAudio(sector) => sector.data.as_slice()
        }
    }

    pub fn get_edc(&self) -> Option<&EDC> {
        match self {
            Sector::Audio(_)          => None,
            Sector::Empty(_)          => None,
            Sector::CDData(sector)    => Some(&sector.edc),
            Sector::CDXAData(sector)  => Some(&sector.edc),
            Sector::CDXAAudio(sector) => Some(&sector.edc),
        }
    }

    pub fn get_ecc(&self) -> Option<&ECC> {
        match self {
            Sector::Audio(_)         => None,
            Sector::Empty(_)         => None,
            Sector::CDData(sector)   => Some(&sector.ecc),
            Sector::CDXAData(sector) => Some(&sector.ecc),
            Sector::CDXAAudio(_)     => None
        }
    }

    pub fn calculate_edc(&mut self) {
        match self {
            Sector::Audio(_)       => (),
            Sector::Empty(_)       => (),
            Sector::CDData(sector) => {
                sector.calculate_edc();
            },
            Sector::CDXAData(sector) => {
                sector.calculate_edc();
            },
            Sector::CDXAAudio(sector) => {
                unsafe {
                    let raw = std::mem::transmute::<&Mode2Form2, &[u8;std::mem::size_of::<Mode2Form2>()]>(sector);
                    sector.edc = EDC::calculate_for(&raw[0x10..=0x92B]);
                }
            }
        }
    }

    pub fn calculate_ecc(&mut self) {
        match self {
            Sector::Audio(_)          => (),
            Sector::Empty(_)          => (),
            Sector::CDData(sector)    => sector.calculate_ecc(),
            Sector::CDXAData(sector)  => sector.calculate_ecc(),
            Sector::CDXAAudio(_)      => ()
        }
    }

    pub fn finalize(&mut self) {
        match self {
            Sector::Audio(_)          => (),
            Sector::Empty(_)          => (),
            Sector::CDData(sector)    => sector.finalize(),
            Sector::CDXAData(sector)  => sector.finalize(),
            Sector::CDXAAudio(sector) => sector.finalize(),
        }
    }

    pub fn as_raw(&self) -> &[u8] {
        match self {
            Sector::Audio(sector)     => unsafe {std::mem::transmute::<&Audio, &[u8; SECTOR_SIZE]>(sector)},
            Sector::Empty(sector)     => unsafe {std::mem::transmute::<&Mode0, &[u8; SECTOR_SIZE]>(sector)},
            Sector::CDData(sector)    => unsafe {std::mem::transmute::<&Mode1, &[u8; SECTOR_SIZE]>(sector)},
            Sector::CDXAData(sector)  => unsafe {std::mem::transmute::<&Mode2Form1, &[u8; SECTOR_SIZE]>(sector)},
            Sector::CDXAAudio(sector) => unsafe {std::mem::transmute::<&Mode2Form2, &[u8; SECTOR_SIZE]>(sector)},
        }
    }
}
