use core::fmt;

use libk_util::{get_le_u16, get_le_u32};

use crate::FsLayout;

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Bpb<'a> {
    bytes: &'a [u8],
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Fat32Ebpb<'a> {
    bytes: &'a [u8],
}

#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Fat32FsInfo {
    bytes: [u8; 512],
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct ClusterNumber(pub u32);

#[derive(PartialEq, Eq)]
#[repr(transparent)]
pub struct FatStr([u8]);

impl<'a> Bpb<'a> {
    pub fn from_bytes(bytes: &'a [u8]) -> Self {
        assert_eq!(bytes.len(), 512);
        Self { bytes }
    }

    pub fn bytes_per_sector(&self) -> usize {
        get_le_u16(&self.bytes[0x0B..]) as usize
    }

    pub fn sectors_per_cluster(&self) -> usize {
        self.bytes[0x0D] as usize
    }

    pub fn reserved_sectors(&self) -> usize {
        get_le_u16(&self.bytes[0x0E..]) as usize
    }

    pub fn fat_count(&self) -> usize {
        self.bytes[0x10] as usize
    }

    pub fn total_sectors_16(&self) -> u16 {
        get_le_u16(&self.bytes[0x13..])
    }

    pub fn fat_size_16(&self) -> u16 {
        get_le_u16(&self.bytes[0x16..])
    }

    pub fn total_sectors(&self) -> u64 {
        let small = self.total_sectors_16() as u64;
        if small == 0 {
            get_le_u32(&self.bytes[0x20..]) as u64
        } else {
            small
        }
    }
}

impl<'a> Fat32Ebpb<'a> {
    pub fn from_bytes(bytes: &'a [u8]) -> Self {
        assert_eq!(bytes.len(), 512);
        Self { bytes }
    }

    pub fn sectors_per_fat(&self) -> usize {
        get_le_u32(&self.bytes[0x24..]) as usize
    }

    pub fn root_directory_cluster(&self) -> ClusterNumber {
        ClusterNumber(get_le_u32(&self.bytes[0x2C..]))
    }

    pub fn fsinfo_sector(&self) -> u64 {
        get_le_u16(&self.bytes[0x30..]) as u64
    }

    pub fn volume_label(&self) -> &FatStr {
        FatStr::new(&self.bytes[0x47..0x52])
    }
}

impl FatStr {
    pub fn new(bytes: &[u8]) -> &Self {
        let len = bytes
            .iter()
            .rposition(|&c| c != b' ' && c != 0)
            .map(|len| len + 1)
            .unwrap_or(0);
        unsafe { core::mem::transmute(&bytes[..len]) }
    }

    #[inline]
    pub fn from_str(str: &str) -> &Self {
        unsafe { core::mem::transmute(str.trim_end_matches(' ').as_bytes()) }
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.0.len()
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        unsafe { core::mem::transmute(self) }
    }

    #[inline]
    pub fn eq_ignore_case(&self, other: &Self) -> bool {
        self.0.eq_ignore_ascii_case(&other.0)
    }
}

impl fmt::Display for FatStr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for &byte in self.0.iter() {
            write!(f, "{}", byte.to_ascii_uppercase() as char)?;
        }
        Ok(())
    }
}

impl fmt::Debug for FatStr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "\"")?;
        for &byte in self.0.iter() {
            write!(f, "{}", byte.to_ascii_uppercase() as char)?;
        }
        write!(f, "\"")
    }
}

impl Fat32FsInfo {
    const SIGNATURE0: u32 = 0x41615252;
    const SIGNATURE1: u32 = 0x61417272;

    pub fn from_bytes(bytes: [u8; 512]) -> Self {
        Self { bytes }
    }

    pub fn is_valid_fat32(&self) -> bool {
        let signature0 = &self.bytes[0..4];
        let signature1 = &self.bytes[484..488];

        signature0 == Self::SIGNATURE0.to_le_bytes() && signature1 == Self::SIGNATURE1.to_le_bytes()
    }
}

impl ClusterNumber {
    pub fn first_sector(&self, layout: &FsLayout) -> u64 {
        layout.first_data_sector + (self.0 as u64 - 2) * layout.sectors_per_cluster as u64
    }
}
