use std::{fmt, ops};
use super::Factor;
pub trait ByteUnits {
    fn bytes(self) -> ByteLength;
    fn kibibytes(self) -> ByteLength;
    fn kilobytes(self) -> ByteLength;
    fn mebibytes(self) -> ByteLength;
    fn megabytes(self) -> ByteLength;
    fn gibibytes(self) -> ByteLength;
    fn gigabytes(self) -> ByteLength;
    fn tebibytes(self) -> ByteLength;
    fn terabytes(self) -> ByteLength;
}
impl ByteUnits for usize {
    fn bytes(self) -> ByteLength {
        ByteLength(self)
    }
    fn kibibytes(self) -> ByteLength {
        ByteLength::from_kibi(self)
    }
    fn kilobytes(self) -> ByteLength {
        ByteLength::from_kilo(self)
    }
    fn mebibytes(self) -> ByteLength {
        ByteLength::from_mebi(self)
    }
    fn megabytes(self) -> ByteLength {
        ByteLength::from_mega(self)
    }
    fn gibibytes(self) -> ByteLength {
        ByteLength::from_gibi(self)
    }
    fn gigabytes(self) -> ByteLength {
        ByteLength::from_giga(self)
    }
    fn tebibytes(self) -> ByteLength {
        ByteLength::from_tebi(self)
    }
    fn terabytes(self) -> ByteLength {
        ByteLength::from_tera(self)
    }
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct ByteLength(pub usize);
impl From<usize> for ByteLength {
    fn from(value: usize) -> Self {
        Self(value)
    }
}
impl ops::Add for ByteLength {
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {
        Self(self.0 + rhs.0)
    }
}
impl ops::AddAssign for ByteLength {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0;
    }
}
impl ops::Sub for ByteLength {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self::Output {
        Self(self.0 - rhs.0)
    }
}
impl ops::SubAssign for ByteLength {
    fn sub_assign(&mut self, rhs: Self) {
        self.0 -= rhs.0;
    }
}
impl ByteLength {
    pub fn bytes(&self) -> usize {
        self.0
    }
    fn scaled(self, scale: f64) -> f64 {
        self.0 as f64 / scale
    }
    pub fn kibis(self) -> f64 {
        self.scaled(1024.0)
    }
    pub fn kilos(self) -> f64 {
        self.scaled(1000.0)
    }
    pub fn mebis(self) -> f64 {
        self.scaled(1024.0f64.powi(2))
    }
    pub fn megas(self) -> f64 {
        self.scaled(1000.0f64.powi(2))
    }
    pub fn gibis(self) -> f64 {
        self.scaled(1024.0f64.powi(3))
    }
    pub fn gigas(self) -> f64 {
        self.scaled(1000.0f64.powi(3))
    }
    pub fn tebis(self) -> f64 {
        self.scaled(1024.0f64.powi(4))
    }
    pub fn teras(self) -> f64 {
        self.scaled(1000.0f64.powi(4))
    }
    pub const MAX: ByteLength = ByteLength(usize::MAX);
    pub const fn saturating_add(self, rhs: ByteLength) -> ByteLength {
        ByteLength(self.0.saturating_add(rhs.0))
    }
    pub const fn saturating_sub(self, rhs: ByteLength) -> ByteLength {
        ByteLength(self.0.saturating_sub(rhs.0))
    }
    pub const fn saturating_mul(self, rhs: ByteLength) -> ByteLength {
        ByteLength(self.0.saturating_mul(rhs.0))
    }
    pub const fn wrapping_add(self, rhs: ByteLength) -> ByteLength {
        ByteLength(self.0.wrapping_add(rhs.0))
    }
    pub const fn wrapping_sub(self, rhs: ByteLength) -> ByteLength {
        ByteLength(self.0.wrapping_sub(rhs.0))
    }
    pub const fn wrapping_mul(self, rhs: ByteLength) -> ByteLength {
        ByteLength(self.0.wrapping_mul(rhs.0))
    }
    pub const fn wrapping_div(self, rhs: ByteLength) -> ByteLength {
        ByteLength(self.0.wrapping_div(rhs.0))
    }
    pub fn checked_add(self, rhs: ByteLength) -> Option<ByteLength> {
        self.0.checked_add(rhs.0).map(ByteLength)
    }
    pub fn checked_sub(self, rhs: ByteLength) -> Option<ByteLength> {
        self.0.checked_sub(rhs.0).map(ByteLength)
    }
    pub fn checked_mul(self, rhs: ByteLength) -> Option<ByteLength> {
        self.0.checked_mul(rhs.0).map(ByteLength)
    }
    pub fn checked_div(self, rhs: ByteLength) -> Option<ByteLength> {
        self.0.checked_div(rhs.0).map(ByteLength)
    }
}
impl ByteLength {
    pub const fn from_byte(bytes: usize) -> Self {
        ByteLength(bytes)
    }
    const fn new(value: usize, scale: usize) -> Self {
        ByteLength(value.saturating_mul(scale))
    }
    pub const fn from_kibi(kibi_bytes: usize) -> Self {
        Self::new(kibi_bytes, 1024)
    }
    pub const fn from_kilo(kibi_bytes: usize) -> Self {
        Self::new(kibi_bytes, 1000)
    }
    pub const fn from_mebi(mebi_bytes: usize) -> Self {
        Self::new(mebi_bytes, 1024usize.pow(2))
    }
    pub const fn from_mega(mebi_bytes: usize) -> Self {
        Self::new(mebi_bytes, 1000usize.pow(2))
    }
    pub const fn from_gibi(gibi_bytes: usize) -> Self {
        Self::new(gibi_bytes, 1024usize.pow(3))
    }
    pub const fn from_giga(giga_bytes: usize) -> Self {
        Self::new(giga_bytes, 1000usize.pow(3))
    }
    pub const fn from_tebi(gibi_bytes: usize) -> Self {
        Self::new(gibi_bytes, 1024usize.pow(4))
    }
    pub const fn from_tera(tera_bytes: usize) -> Self {
        Self::new(tera_bytes, 1000usize.pow(4))
    }
}
impl ByteLength {
    pub fn max(self, other: Self) -> Self {
        Self(self.0.max(other.0))
    }
    pub fn min(self, other: Self) -> Self {
        Self(self.0.min(other.0))
    }
}
impl fmt::Debug for ByteLength {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.debug_tuple("ByteLength").field(&self.0).finish()
        } else {
            write!(f, "ByteLength({self})")
        }
    }
}
impl fmt::Display for ByteLength {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            if self.0 >= 1024usize.pow(4) {
                write!(f, "{:.2} tebibytes", self.tebis())
            } else if self.0 >= 1024usize.pow(3) {
                write!(f, "{:.2} gibibytes", self.gibis())
            } else if self.0 >= 1024usize.pow(2) {
                write!(f, "{:.2} mebibytes", self.mebis())
            } else if self.0 >= 1024 {
                write!(f, "{:.2} kibibytes", self.kibis())
            } else {
                write!(f, "{} bytes", self.bytes())
            }
        } else if self.0 >= 1000usize.pow(4) {
            write!(f, "{:.2} terabytes", self.teras())
        } else if self.0 >= 1000usize.pow(3) {
            write!(f, "{:.2} gigabytes", self.gigas())
        } else if self.0 >= 1000usize.pow(2) {
            write!(f, "{:.2} megabytes", self.megas())
        } else if self.0 >= 1000 {
            write!(f, "{:.2} kilobytes", self.kilos())
        } else {
            write!(f, "{} bytes", self.bytes())
        }
    }
}
impl<S: Into<Factor>> ops::Mul<S> for ByteLength {
    type Output = Self;
    fn mul(mut self, rhs: S) -> Self {
        self.0 = (self.0 as f64 * rhs.into().0 as f64) as usize;
        self
    }
}
impl<S: Into<Factor>> ops::MulAssign<S> for ByteLength {
    fn mul_assign(&mut self, rhs: S) {
        *self = *self * rhs;
    }
}
impl<S: Into<Factor>> ops::Div<S> for ByteLength {
    type Output = Self;
    fn div(mut self, rhs: S) -> Self {
        self.0 = (self.0 as f64 / rhs.into().0 as f64) as usize;
        self
    }
}
impl<S: Into<Factor>> ops::DivAssign<S> for ByteLength {
    fn div_assign(&mut self, rhs: S) {
        *self = *self / rhs;
    }
}