#[cfg(feature = "alloc")]
use alloc::string::String;
use core::{
    borrow::Borrow,
    fmt,
    hash::{Hash, Hasher},
    ops::{Deref, DerefMut},
    str,
};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StrCapacityError {
    pub len: usize,
    pub capacity: usize,
}

impl fmt::Display for StrCapacityError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "stx::Str capacity exceeded: len {} > capacity {}",
            self.len, self.capacity
        )
    }
}

#[derive(Clone, Copy)]
pub struct Str<const N: usize> {
    buf: [u8; N],
    len: usize,
}
impl<const N: usize> Str<N> {
    #[inline]
    pub const fn new() -> Self {
        Self {
            buf: [0; N],
            len: 0,
        }
    }

    #[inline]
    pub const fn capacity(&self) -> usize {
        N
    }

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

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

    #[inline]
    pub const fn clear(&mut self) {
        self.len = 0;
    }

    #[inline]
    pub const fn truncate(&mut self, new_len: usize) {
        if new_len < self.len {
            self.len = new_len;
        }
    }

    #[inline]
    pub fn as_str(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.buf[..self.len]) }
    }

    #[inline]
    pub fn as_mut_str(&mut self) -> &mut str {
        unsafe { str::from_utf8_unchecked_mut(&mut self.buf[..self.len]) }
    }

    #[inline]
    pub fn from_str(s: &str) -> Self {
        let mut buf = [0; N];
        let bytes = s.as_bytes();
        let len = bytes.len().min(N);
        buf[..len].copy_from_slice(&bytes[..len]);
        Self { buf, len }
    }

    #[inline]
    pub fn try_from_str(s: &str) -> Result<Self, StrCapacityError> {
        let bytes = s.as_bytes();
        let len = bytes.len();
        if len > N {
            return Err(StrCapacityError { len, capacity: N });
        }
        let mut buf = [0; N];
        buf[..len].copy_from_slice(bytes);
        Ok(Self { buf, len })
    }

    #[inline]
    pub fn push_str(&mut self, s: &str) -> Result<(), StrCapacityError> {
        let bytes = s.as_bytes();
        let len = bytes.len();
        if self.len + len > N {
            return Err(StrCapacityError {
                len: self.len + len,
                capacity: N,
            });
        }
        self.buf[self.len..self.len + len].copy_from_slice(bytes);
        self.len += len;
        Ok(())
    }

    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        &self.buf[..self.len]
    }

    #[inline]
    pub fn as_mut_bytes(&mut self) -> &mut [u8] {
        &mut self.buf[..self.len]
    }
}

/* Formatting */

impl<const N: usize> fmt::Display for Str<N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self.as_str(), f)
    }
}

impl<const N: usize> fmt::Debug for Str<N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self.as_str(), f)
    }
}

/* Equality and ordering */

impl<const N: usize> PartialEq for Str<N> {
    fn eq(&self, other: &Self) -> bool {
        self.as_str() == other.as_str()
    }
}

impl<const N: usize> Eq for Str<N> {}

impl<const N: usize> PartialEq<&str> for Str<N> {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}

impl<const N: usize> PartialOrd for Str<N> {
    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
        self.as_str().partial_cmp(other.as_str())
    }
}

impl<const N: usize> Ord for Str<N> {
    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
        self.as_str().cmp(other.as_str())
    }
}

/* Hashing */

impl<const N: usize> Hash for Str<N> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.as_str().hash(state);
    }
}

/* Borrowing */

impl<const N: usize> Borrow<str> for Str<N> {
    fn borrow(&self) -> &str {
        self.as_str()
    }
}

/* Dereferencing */

impl<const N: usize> Deref for Str<N> {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.as_str()
    }
}

impl<const N: usize> AsRef<str> for Str<N> {
    #[inline]
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl<const N: usize> DerefMut for Str<N> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.as_mut_str()
    }
}

/* Conversions */

impl<const N: usize> From<&str> for Str<N> {
    fn from(s: &str) -> Self {
        assert!(
            s.len() <= N,
            "stx::Str capacity exceeded: len {} > capacity {}",
            s.len(),
            N
        );

        let mut buf = [0u8; N];
        buf[..s.len()].copy_from_slice(s.as_bytes());

        Self { buf, len: s.len() }
    }
}

pub trait ToStkStr<const N: usize> {
    /// Converts the given value to a [`Str<N>`].
    ///
    /// # Examples
    ///
    /// ```
    /// use stx::{Str, ToStkStr};
    ///
    /// struct Num(u8);
    ///
    /// impl<const N: usize> ToStkStr<N> for Num {
    ///     fn to_stk_str(&self) -> Str<N> {
    ///         match self.0 {
    ///             5 => Str::from("5"),
    ///             _ => Str::from("?"),
    ///         }
    ///     }
    /// }
    ///
    /// let five = ToStkStr::<16>::to_stk_str(&Num(5));
    /// assert_eq!(five, Str::<16>::from("5"));
    /// ```
    fn to_stk_str(&self) -> Str<N>;
}

#[cfg(feature = "alloc")]
impl<const N: usize> From<Str<N>> for String {
    fn from(s: Str<N>) -> Self {
        String::from(s.as_str())
    }
}

/* Extend */

impl<const N: usize> Extend<char> for Str<N> {
    fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
        for c in iter {
            let mut buf = [0; 4];
            let s = c.encode_utf8(&mut buf);
            if self.len + s.len() > N {
                break;
            }
            self.buf[self.len..self.len + s.len()].copy_from_slice(s.as_bytes());
            self.len += s.len();
        }
    }
}

/* Serde */

#[cfg(feature = "serde")]
impl<const N: usize> serde::Serialize for Str<N> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(self.as_str())
    }
}

#[cfg(feature = "serde")]
impl<'de, const N: usize> serde::Deserialize<'de> for Str<N> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct StrVisitor<const N: usize>;

        impl<'de, const N: usize> serde::de::Visitor<'de> for StrVisitor<N> {
            type Value = Str<N>;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                write!(formatter, "a string with at most {} bytes", N)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                if value.len() > N {
                    return Err(E::invalid_length(value.len(), &self));
                }

                let mut buf = [0; N];
                buf[..value.len()].copy_from_slice(value.as_bytes());
                Ok(Str {
                    buf,
                    len: value.len(),
                })
            }

            fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E>
            where
                E: serde::de::Error,
            {
                self.visit_str(value)
            }
        }

        deserializer.deserialize_str(StrVisitor::<N>)
    }
}

#[cfg(test)]
mod tests;
