use super::bcd::BCDValue;
use super::super::Error;

#[derive(Clone)]
pub struct Time {
    minute: u8,
    second: u8,
    sector: u8
}

impl Time {
    pub const MAX_SECTORS:usize = 75;
    pub const MAX_SECONDS:usize = 60;
    pub const MAX_MINUTES:usize = 74;

    pub const fn cue_start() -> Time {
        Time{minute: 0, second: 0, sector: 0}
    }

    pub const fn cd_start() -> Time {
        Time{minute: 0, second: 2, sector: 0}
    }

    pub const fn cd_pregap() -> Time {
        Time{minute: 0, second: 2, sector: 0}
    }

    pub const fn from_mss(minute: u8, second: u8, sector: u8) -> Time {
        Time{minute, second, sector}
    }

    pub fn add_sectors(&mut self, sector: usize) -> Result<(), Error> {
        let sector = self.sector as usize + sector;
        let second = self.second as usize + (sector/Self::MAX_SECTORS);
        let minute = self.minute as usize + (second/Self::MAX_SECONDS);

        if minute > Self::MAX_MINUTES {
            Err(Error::TimeToLarge)
        }

        else {
            *self = Time{minute: minute as u8, second: (second%Self::MAX_SECONDS) as u8, sector: (sector%Self::MAX_SECTORS) as u8};
            Ok(())
        }
    }

    pub fn parse(string: &str) -> Option<Time> {
        let mut slices  = string.split(':');
        let     minutes = slices.next()?;
        let     seconds = slices.next()?;
        let     sectors = slices.next()?;

        let mut time = Time{minute: minutes.parse().ok()?, second: seconds.parse().ok()?, sector: sectors.parse().ok()?};
        time.add_sectors(0).ok()?;

        Some(time)
    }

    pub fn add_sector(&mut self) -> Result<(), Error> {
        self.add_sectors(1)
    }

    pub fn to_bcd(&self) -> (BCDValue, BCDValue, BCDValue) {
        (BCDValue::from(self.minute), BCDValue::from(self.second), BCDValue::from(self.sector))
    }

    pub const fn to_lba(&self) -> usize {
        self.sector as usize + (self.second as usize*Self::MAX_SECTORS) + (self.minute as usize*Self::MAX_SECONDS*Self::MAX_SECTORS)
    }

    pub fn from_lba(lba: usize) -> Time {
        let sector = (lba%Time::MAX_SECTORS) as u8;
        let second = ((lba/Time::MAX_SECTORS)%Time::MAX_SECONDS) as u8;
        let minute = (((lba/Time::MAX_SECTORS)/Time::MAX_SECONDS)%Time::MAX_MINUTES) as u8;

        Time{sector, second, minute}
    }

    pub const fn as_sectors(&self) -> usize {
        self.sector as usize + self.second as usize*Self::MAX_SECTORS + self.minute as usize*Self::MAX_SECONDS*Self::MAX_SECTORS
    }

    pub const fn get(&self) -> (i32, i32, i32) {
        (self.minute as i32, self.second as i32, self.sector as i32)
    }

    pub fn dump(&self) -> String {
        format!("min: {}, sec: {}, sector: {}", self.minute, self.second, self.sector)
    }
}

impl std::convert::From<(BCDValue, BCDValue, BCDValue)> for Time {
    fn from(time: (BCDValue, BCDValue, BCDValue)) -> Self {
        Time{minute: u8::from(time.0), second: u8::from(time.1), sector: u8::from(time.2)}
    }
}

impl std::fmt::Display for Time {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:0>2}:{:0>2}:{:0>2}", self.minute, self.second, self.sector)
    }
}