//! Network value handling utilities
use core::fmt;

macro_rules! wrap_primitives {
    ($($ty:ty),+) => {
        $(impl NetValueImpl for $ty {
            #[inline]
            fn from_network_order(value: NetValue<Self>) -> Self {
                Self::from_be(value.0)
            }

            #[inline]
            fn to_network_order(self) -> NetValue<Self> {
                NetValue(self.to_be())
            }
        })+
    };
}

/// Interface for converting from/to network-format values
#[cfg(not(feature = "bytemuck"))]
pub trait NetValueImpl: Copy + Eq {
    /// Performs conversion from network to host format
    fn from_network_order(value: NetValue<Self>) -> Self;
    /// Performs conversion from host format to network
    fn to_network_order(self) -> NetValue<Self>;
}

/// Interface for converting from/to network-format values
#[cfg(feature = "bytemuck")]
pub trait NetValueImpl: Copy + Eq + bytemuck::Pod + bytemuck::Zeroable {
    /// Performs conversion from network to host format
    fn from_network_order(value: NetValue<Self>) -> Self;
    /// Performs conversion from host format to network
    fn to_network_order(self) -> NetValue<Self>;
}

/// Wrapper type to ensure network values are handled properly
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
#[repr(transparent)]
pub struct NetValue<T>(T)
where
    T: NetValueImpl;

/// Helper type for primitive values wrapped in some 1-struct
#[cfg(not(feature = "bytemuck"))]
pub trait WrappedValue: Copy + Eq {
    /// Wrapped type
    type Inner: NetValueImpl;

    /// Converts from self to wrapped type
    fn into_inner(self) -> Self::Inner;
    /// Converts from wrapped type to self
    fn from_inner(value: Self::Inner) -> Self;
}

/// Helper type for primitive values wrapped in some 1-struct
#[cfg(feature = "bytemuck")]
pub trait WrappedValue: Copy + Eq + bytemuck::Pod + bytemuck::Zeroable {
    /// Wrapped type
    type Inner: NetValueImpl;

    /// Converts from self to wrapped type
    fn into_inner(self) -> Self::Inner;
    /// Converts from wrapped type to self
    fn from_inner(value: Self::Inner) -> Self;
}

impl<T: NetValueImpl> NetValue<T> {
    /// Creates a [NetValue] from a value that's already in network order
    #[inline]
    pub fn from_network_order(value: T) -> Self {
        Self(value)
    }
}

impl<T: WrappedValue> NetValue<T> {
    /// "Extracts" a wrapped value from [NetValue]
    #[inline]
    pub fn into_inner(self) -> NetValue<T::Inner> {
        NetValue(self.0.into_inner())
    }

    /// Converts a wrapped value into its "shell" [NetValue] type
    #[inline]
    pub fn from_inner(value: NetValue<T::Inner>) -> NetValue<T> {
        NetValue(T::from_inner(value.0))
    }
}

impl<T: NetValueImpl + fmt::Debug> fmt::Debug for NetValue<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let value = T::from_network_order(*self);
        fmt::Debug::fmt(&value, f)
    }
}

impl<T: WrappedValue> NetValueImpl for T {
    #[inline]
    fn from_network_order(value: NetValue<T>) -> T {
        let inner = <T::Inner>::from_network_order(value.into_inner());
        T::from_inner(inner)
    }

    #[inline]
    fn to_network_order(self) -> NetValue<T> {
        NetValue::from_inner(self.into_inner().to_network_order())
    }
}

wrap_primitives!(u16, u32, u64);
