/// Helper macro to define primitive enums with integer reprs, as well as their conversion methods
#[macro_export]
macro_rules! primitive_enum {
    (
        $(#[$struct_meta:meta])*
        $vis:vis enum $name:ident: $repr:ty {
            $( $(#[$variant_meta:meta])* $variant:ident = $discriminant:literal ),+ $(,)?
        }
        $([ $enum_extra:tt ]),* $(,)?
    ) => {
        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
        // #[repr($repr)]
        $(#[$struct_meta])*
        $vis enum $name {
            $(
                $(#[$variant_meta])?
                $variant = $discriminant
            ),+
        }

        impl TryFrom<$repr> for $name {
            type Error = $crate::error::Error;

            fn try_from(v: $repr) -> Result<$name, Self::Error> {
                match v {
                    $(
                        $discriminant => Ok($name::$variant)
                    ,)+
                    _ => Err($crate::error::Error::InvalidArgument)
                }
            }
        }

        impl From<$name> for $repr {
            fn from(v: $name) -> $repr {
                v as $repr
            }
        }

        $(
            $crate::primitive_enum_extra!($enum_extra,
                impl abi_serde::Serialize for $name {
                    fn serialize<S: abi_serde::ser::Serializer>(&self, serializer: &mut S) -> Result<(), <S as abi_serde::ser::Serializer>::Error> {
                        let repr = *self as $repr;
                        repr.serialize(serializer)
                    }
                }

                impl<'de> abi_serde::Deserialize<'de> for $name {
                    fn deserialize<D: abi_serde::des::Deserializer<'de>>(deserializer: &mut D) -> Result<Self, <D as abi_serde::des::Deserializer<'de>>::Error> {
                        let repr = <$repr>::deserialize(deserializer)?;
                        Self::try_from(repr).map_err(|_| <<D as abi_serde::des::Deserializer<'de>>::Error as abi_serde::des::DeserializeError>::INVALID_ENUM_VARIANT)
                    }
                }
            );
        )*
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! primitive_enum_extra {
    (with_serde, $($inner:item)+) => {
        $($inner)+
    };
    ($any_other:ident, $($inner:item)+) => {};
}

/// Common implementations for bitflag definitions
#[macro_export]
macro_rules! bitflags_impl_common {
    ($name:ident, $repr:ty) => {
        impl $name {
            /// Constructs a value from its bit representation
            pub const fn new(bits: $repr) -> Self {
                Self(bits)
            }

            /// Constructs a value with all bits set to zero
            pub const fn empty() -> Self {
                Self(0)
            }

            /// Returns the bit representation of the value
            #[inline]
            pub const fn bits(self) -> $repr {
                self.0
            }

            /// Returns `true` if the value contains all bits in `bits`
            #[inline]
            pub const fn contains(self, bits: $name) -> bool {
                self.0 & bits.bits() == bits.bits()
            }

            /// Returns `true` if the value contains any of the bits in `bits`
            #[inline]
            pub const fn contains_any(self, bits: $name) -> bool {
                self.0 & bits.bits() != 0
            }
        }

        impl From<$repr> for $name {
            fn from(value: $repr) -> $name {
                $name::new(value)
            }
        }

        impl From<$name> for $repr {
            fn from(value: $name) -> $repr {
                value.bits()
            }
        }

        impl core::ops::BitOr for $name {
            type Output = $name;

            fn bitor(self, rhs: $name) -> Self::Output {
                Self::new(self.bits() | rhs.bits())
            }
        }

        impl core::ops::BitAnd for $name {
            type Output = $name;

            fn bitand(self, rhs: $name) -> Self::Output {
                Self::new(self.bits() & rhs.bits())
            }
        }

        impl core::ops::BitOrAssign for $name {
            fn bitor_assign(&mut self, rhs: $name) {
                self.0 |= rhs.bits();
            }
        }

        impl core::ops::BitAndAssign for $name {
            fn bitand_assign(&mut self, rhs: $name) {
                self.0 &= rhs.bits();
            }
        }

        impl core::ops::Not for $name {
            type Output = $name;

            fn not(self) -> Self::Output {
                Self::new(!self.bits())
            }
        }
    };
}

/// Implements Default trait for a bitflag definition
#[macro_export]
macro_rules! bitflags_impl_default {
    ($name:ident, $repr:ty) => {};

    ($name:ident, $repr:ty, ($($flag:ident)|+)) => {
        impl Default for $name {
            fn default() -> Self {
                $name($($name::$flag.bits())|+)
            }
        }

        impl $name {
            /// const-version of [Default] trait impl
            pub const fn const_default() -> Self {
                Self($($name::$flag.bits())|+)
            }
        }
    };

    ($name:ident, $repr:ty, ()) => {
        impl Default for $name {
            fn default() -> Self {
                $name(0)
            }
        }

        impl $name {
            /// const-version of [Default] trait impl
            pub const fn const_default() -> Self {
                Self(0)
            }
        }
    };
}

/// Helper macro to construct bit flag wrappers
#[macro_export]
macro_rules! bitflags {
    (
        $(#[default = $struct_default:tt])?
        $(#[$struct_meta:meta])*
        $vis:vis struct $name:ident: $repr:ty {
            $(
                $(#[doc = $field_doc:expr])?
                const $field_name:ident: bit $field_offset:expr;
            )+
        }
    ) => {
        #[repr(transparent)]
        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
        $(#[$struct_meta])*
        $vis struct $name($repr);

        impl $name {
            $(
                $(#[doc = $field_doc])?
                pub const $field_name: $name = $name(1 << $field_offset);
            )+

            pub fn fields() -> impl Iterator<Item = Self> {
                [$(Self::$field_name,)+].into_iter()
            }

            pub fn set_fields(&self) -> impl Iterator<Item = Self> + '_ {
                Self::fields().filter(|&f| self.contains(f))
            }
        }

        $crate::bitflags_impl_common!($name, $repr);

        impl core::fmt::Debug for $name {
            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
                let mut s = f.debug_struct(stringify!($name));
                $(
                    s.field(stringify!($field_name), &self.contains($name::$field_name));
                )+
                s.finish_non_exhaustive()
            }
        }

        $crate::bitflags_impl_default!($name, $repr$(, $struct_default)?);
    };
}

/// Generates a #[repr(transparent)] wrapper for a value with Debug, Copy, Eq, Ord, Hash derived +
/// Into<T> implemented
#[macro_export]
macro_rules! transparent_wrapper {
    ($(#[$struct_meta:meta])* $vis:vis struct $name:ident ( $ty:ty )) => {
        #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
        $(#[$struct_meta])* $vis struct $name ($ty);

        impl $name {
            /// Constructs the value from its raw representation.
            ///
            /// # Safety
            ///
            /// Unsafe: the actual subset of values represented by this type may not include all
            /// the raw values.
            pub unsafe fn from_raw(value: $ty) -> $name {
                $name(value)
            }
        }

        impl From<$name> for $ty {
            fn from(value: $name) -> $ty {
                value.0
            }
        }
    };
}

#[allow(missing_docs)]
#[macro_export]
macro_rules! define_bitfield_getter {
    ($offset:literal => $getter:ident: $ty:ty) => {
        pub fn $getter(&self) -> bool {
            unsafe { core::ptr::read_volatile(&self.0) & (1 << $offset) != 0 }
        }
    };
    (($start:literal..$end:literal) => $getter:ident: $ty:ty) => {
        pub fn $getter(&self) -> $ty {
            (unsafe { core::ptr::read_volatile(&self.0) } >> $start) & ((1 << ($end - $start)) - 1)
        }
    };
}

#[allow(missing_docs)]
#[macro_export]
macro_rules! define_bitfield_setter {
    ($offset:literal => $setter:ident: $ty:ty) => {
        pub fn $setter(&mut self, value: bool) -> &mut Self {
            // TODO volatile
            if value {
                self.0 |= 1 << $offset;
            } else {
                self.0 &= !(1 << $offset);
            }
            self
        }
    };
    (($start:literal..$end:literal) => $setter:ident: $ty:ty) => {
        pub fn $setter(&mut self, value: $ty) -> &mut Self {
            const MASK: $ty = ((1 << ($end - $start)) - 1) << $start;
            self.0 &= !MASK;
            self.0 |= (value << $start) & MASK;
            self
        }
    };
}

#[allow(missing_docs)]
#[macro_export]
macro_rules! define_bitfield_argument {
    ($offset:literal => $ty:ty) => {
        bool
    };
    (($start:literal..$end:literal) => $ty:ty) => {
        $ty
    };
}

#[allow(missing_docs)]
#[macro_export]
macro_rules! define_bitfield_constructor_field {
    ($self:ident $offset:literal => $getter:ident) => {
        if $getter {
            $self.0 |= 1 << $offset;
        }
    };
    ($self:ident ($start:literal..$end:literal) => $getter:ident) => {
        $self.0 |= ($getter & ((1 << ($end - $start)) - 1)) << $start;
    };
}

/// Helper macro to declare more complex bitfields than [bitflags!] does.
/// TODO: deprecate and remove [bitflags!].
#[macro_export]
macro_rules! define_bitfields {
    ($vis:vis $name:ident : $ty:ty {
        $($range:tt => $getter:ident $(+ $setter:ident)?),* $(,)*
    }) => {
        #[derive(Clone, Copy, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable)]
        #[repr(transparent)]
        $vis struct $name($ty);

        impl $name {
            pub const fn new(
                $( $getter: yggdrasil_abi::define_bitfield_argument!($range => $ty), )*
            ) -> Self {
                let mut this = Self(0);
                $(
                    yggdrasil_abi::define_bitfield_constructor_field!(this $range => $getter);
                )*
                this
            }

            pub const fn into_raw(self) -> $ty {
                self.0
            }

            $(
                yggdrasil_abi::define_bitfield_getter!($range => $getter:$ty);

                $(
                    yggdrasil_abi::define_bitfield_setter!($range => $setter:$ty);
                )?
            )*
        }

        impl core::fmt::Debug for $name {
            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
                f.debug_struct(stringify!($name))
                $(
                    .field(stringify!($getter), &self.$getter())
                )*
                .finish()
            }
        }
    };
}
