use super::{helper::{force_convert_ascii_to_str}, lsb_msb::{EndianFamilyTypes, ReadWriteEndian, LittleEndianFamily, BigEndianFamily}};

pub type PathTableL = PathTableBase<LittleEndianFamily>;
pub type PathTableB = PathTableBase<BigEndianFamily>;

#[repr(packed(1))]
pub struct PathTableBase<EndianFamily: EndianFamilyTypes> {
    pub name_length:               [u8; 1],
    pub extended_attribute_length: [u8; 1],
    pub directory_logical_block:   EndianFamily::U32,
    pub parent_table_id:           EndianFamily::U16,
    //name:    DString<name_length>
    //padding: only if name_length is odd 
}

impl<EndianFamily: EndianFamilyTypes> PathTableBase<EndianFamily> {
    pub unsafe fn new(&mut self, name: &str) {
        *self = PathTableBase{
            name_length:               [name.len() as u8],
            extended_attribute_length: [0],
            directory_logical_block:   EndianFamily::U32::default(),
            parent_table_id:           EndianFamily::U16::default(),
        };
        
        self.set_name(name);
    }

    pub fn get_name(&self) -> &str {
        unsafe {
            let raw_str:*const u8 = (&self.name_length as *const u8).add(std::mem::size_of::<Self>());
            force_convert_ascii_to_str(std::slice::from_raw_parts(raw_str, self.name_length[0] as usize))
        }
    }

    pub unsafe fn set_name(&mut self, name: &str) {
        let mut raw_str:*mut u8 = (&mut self.name_length as *mut u8).add(std::mem::size_of::<Self>());
        for char in name.bytes() {
            *raw_str = char;
            raw_str = raw_str.add(1);
        }
    }

    pub fn get_size(&self) -> usize {
        let name_length = self.name_length[0] as usize;
        std::mem::size_of::<PathTableBase<EndianFamily>>() + name_length + Self::padding_value(name_length)
    }

    pub fn calculate_size_for(name: &str) -> usize {
        let name_length = name.len();

        std::mem::size_of::<PathTableBase<EndianFamily>>() + name_length + Self::padding_value(name_length)
    }

    fn padding_value(name_length: usize) -> usize {
        if (name_length & 0x1) != 0 {
            1usize
        }

        else {
            0usize
        }        
    }
}

impl<EndianFamily: EndianFamilyTypes> std::fmt::Display for PathTableBase<EndianFamily> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let (dir_block, parent_block) = unsafe {
            (std::ptr::addr_of!(self.directory_logical_block).read_unaligned().read(), std::ptr::addr_of!(self.parent_table_id).read_unaligned().read())
        };

        write!(f, "\"{}\" @{} ^{}", self.get_name(), dir_block, parent_block)
    }
}