//! Sector data.

use core::{
    fmt::{self, Debug, Display, LowerHex},
    iter::Step,
    marker::PhantomData,
    ops::{Add, Sub},
};
/// Size of a sector in bytes
pub trait SectorSize: Clone + Copy + PartialEq + PartialOrd + 'static {
    /// DOCME: What is this?
    const LOG_SIZE: u32;
    /// DOCME: What is this?
    const SIZE: usize = 1 << Self::LOG_SIZE;
    /// DOCME: What is this?
    const OFFSET_MASK: u32 = (Self::SIZE - 1) as u32;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
/// DOCME: What is this?    
pub struct Size512;
impl SectorSize for Size512 {
    const LOG_SIZE: u32 = 9;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
/// DOCME: What is this?
pub struct Size1024;
impl SectorSize for Size1024 {
    const LOG_SIZE: u32 = 10;
}
/// DOCME: What is this?
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Size2048;
impl SectorSize for Size2048 {
    const LOG_SIZE: u32 = 11;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
/// DOCME: What is this?
pub struct Size4096;
impl SectorSize for Size4096 {
    const LOG_SIZE: u32 = 12;
}

/// Address in a physical sector
#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Address<S: SectorSize> {
    sector: u32,
    offset: u32,
    _phantom: PhantomData<S>,
}

impl<S: SectorSize> Address<S> {
    ///
    pub unsafe fn new_unchecked(sector: u32, offset: u32) -> Address<S> {
        assert!((offset as usize) < S::SIZE, "offset out of sector bounds");
        let _phantom = PhantomData;
        Address {
            sector,
            offset,
            _phantom,
        }
    }
    ///
    pub fn new(sector: u32, offset: i32) -> Address<S> {
        let sector = (sector as i32 + (offset >> S::LOG_SIZE)) as u32;
        let offset = offset.abs() as u32 & S::OFFSET_MASK;
        unsafe { Address::new_unchecked(sector, offset) }
    }
    ///
    pub fn with_block_size(
        block: u32,
        offset: i32,
        log_block_size: u32,
    ) -> Address<S> {
        let block = (block as i32 + (offset >> log_block_size)) as u32;
        let offset = offset.abs() as u32 & ((1 << log_block_size) - 1);

        let log_diff = log_block_size as i32 - S::LOG_SIZE as i32;
        let top_offset = offset >> S::LOG_SIZE;
        let offset = offset & ((1 << S::LOG_SIZE) - 1);
        let sector = block << log_diff | top_offset;
        unsafe { Address::new_unchecked(sector, offset) }
    }
    ///
    pub fn into_index(&self) -> u64 {
        ((self.sector as u64) << S::LOG_SIZE) + self.offset as u64
    }
    /// Get the size of the sector
    pub const fn sector_size(&self) -> usize {
        S::SIZE
    }
    /// DOCME: What is this?
    pub const fn log_sector_size(&self) -> u32 {
        S::LOG_SIZE
    }
    /// Return the sector number
    pub fn sector(&self) -> u32 {
        self.sector
    }
    /// Return the offset in the sector
    pub fn offset(&self) -> u32 {
        self.offset
    }
}

impl<S: SectorSize> Step for Address<S> {
    fn steps_between(start: &Self, end: &Self) -> Option<usize> {
        if end.sector >= start.sector {
            Some(end.sector as usize - start.sector as usize)
        } else {
            None
        }
    }

    /*
    fn replace_one(&mut self) -> Self {
        mem::replace(self, Address::new(1, 0))
    }

    fn replace_zero(&mut self) -> Self {
        mem::replace(self, Address::new(0, 0))
    }

    fn add_one(&self) -> Self {
        Address::new(self.sector + 1, 0)
    }

    fn sub_one(&self) -> Self {
        Address::new(self.sector - 1, 0)
    }

    fn add_usize(&self, n: usize) -> Option<Self> {
        self.sector
        .checked_add(n as u32)
        .map(|sector| Address::new(sector, 0))
    }
    */

    fn forward_checked(_start: Self, count: usize) -> Option<Self> {
        todo!("forward_checked:  count: {}", count);
    }

    fn backward_checked(_start: Self, count: usize) -> Option<Self> {
        todo!("backward_checked count: {}", count);
    }
}

impl<S: SectorSize> Display for Address<S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.sector, self.offset)
    }
}

impl<S: SectorSize> LowerHex for Address<S> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:x}:{:x}", self.sector, self.offset)
    }
}

impl<S: SectorSize> From<u64> for Address<S> {
    fn from(idx: u64) -> Address<S> {
        let sector = idx >> S::LOG_SIZE;
        let offset = idx & S::OFFSET_MASK as u64;
        Address::new(sector as u32, offset as i32)
    }
}

impl<S: SectorSize> From<usize> for Address<S> {
    fn from(idx: usize) -> Address<S> {
        let sector = idx >> S::LOG_SIZE;
        let offset = idx & S::OFFSET_MASK as usize;
        Address::new(sector as u32, offset as i32)
    }
}

impl<S: SectorSize> Add for Address<S> {
    type Output = Address<S>;
    fn add(self, rhs: Address<S>) -> Address<S> {
        Address::new(
            self.sector + rhs.sector,
            (self.offset + rhs.offset) as i32,
        )
    }
}

impl<S: SectorSize> Sub for Address<S> {
    type Output = Address<S>;
    fn sub(self, rhs: Address<S>) -> Address<S> {
        Address::new(
            self.sector - rhs.sector,
            self.offset as i32 - rhs.offset as i32,
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn conv() {
        assert_eq!(Address::<Size512>::new(0, 1024).into_index(), 1024);
        assert_eq!(Address::<Size512>::from(1024_u64).into_index(), 1024);
        assert_eq!(
            Address::<Size512>::with_block_size(1, 256, 10).into_index(),
            1024 + 256
        );
        assert_eq!(
            Address::<Size512>::with_block_size(2, 0, 10).into_index(),
            2048
        );
        assert_eq!(
            Address::<Size512>::with_block_size(0, 1792, 10).into_index(),
            1792
        );
    }

    #[test]
    fn arithmetic() {
        assert_eq!(
            Address::<Size512>::new(0, 512),
            Address::<Size512>::new(1, 0),
        );

        assert_eq!(
            Address::<Size512>::new(2, -256),
            Address::<Size512>::new(1, 256),
        );

        let a = Address::<Size2048>::new(0, 1024);
        let b = Address::<Size2048>::new(0, 1024);
        assert_eq!(a + b, Address::<Size2048>::new(1, 0));
        assert_eq!((a + b).into_index(), 2048);

        let a = Address::<Size512>::new(0, 2048);
        let b = Address::<Size512>::new(0, 256);
        assert_eq!(a - b, Address::<Size512>::new(3, 256));
        assert_eq!((a - b).into_index(), 1792);
    }
}
