use crate::repr::EnumSetTypeRepr;

#[cfg(feature = "serde")]
use crate::EnumSet;

/// The trait used to define enum types that may be used with [`EnumSet`].
///
/// This trait must be impelmented using `#[derive(EnumSetType)]`, is not public API, and its
/// internal structure may change at any time with no warning.
///
/// For full documentation on the procedural derive and its options, see
/// [`#[derive(EnumSetType)]`](derive@crate::EnumSetType).
///
/// [`EnumSet`]: crate::set::EnumSet
pub unsafe trait EnumSetType: Copy + Eq + EnumSetTypePrivate {}

/// An [`EnumSetType`] for which [`EnumSet`]s have a guaranteed in-memory representation.
///
/// An implementation of this trait is generated by using
/// [`#[derive(EnumSetType)]`](derive@crate::EnumSetType) with the annotation
/// `#[enumset(repr = "…")]`, where `…` is `u8`, `u16`, `u32`, `u64` or `u128`.
///
/// For any type `T` that implements this trait, the in-memory representation of `EnumSet<T>`
/// is guaranteed to be `Repr`. This guarantee is useful for FFI. See [the `EnumSet` documentation
/// under “FFI, Safety and `repr`”][crate::set::EnumSet#ffi-safety-and-repr] for an example.
///
/// [`EnumSet`]: crate::set::EnumSet
pub unsafe trait EnumSetTypeWithRepr:
    EnumSetType + EnumSetTypePrivate<Repr = <Self as EnumSetTypeWithRepr>::Repr>
{
    /// The guaranteed representation.
    type Repr: EnumSetTypeRepr;
}

/// The actual members of EnumSetType. Put here to avoid polluting global namespaces.
pub unsafe trait EnumSetTypePrivate: EnumSetConstHelper + Sized {
    /// The underlying type used to store the bitset.
    type Repr: EnumSetTypeRepr;
    /// A mask of bits that are valid in the bitset.
    const ALL_BITS: Self::Repr;
    /// The largest bit used in the bitset.
    const BIT_WIDTH: u32;
    /// The number of variants in the bitset.
    const VARIANT_COUNT: u32;

    /// Converts an enum of this type into its bit position.
    fn enum_into_u32(self) -> u32;

    /// Converts a bit position into an enum value.
    unsafe fn enum_from_u32(val: u32) -> Self;

    /// Converts a bit position into an enum value, with an debug_assert.
    unsafe fn enum_from_u32_checked(val: u32) -> Self {
        debug_assert!(Self::ALL_BITS.has_bit(val), "Unknown bit retrieved from bitset.");
        Self::enum_from_u32(val)
    }

    /// Serializes the `EnumSet`.
    ///
    /// This and `deserialize` are part of the `EnumSetType` trait so the procedural derive
    /// can control how `EnumSet` is serialized.
    #[cfg(feature = "serde")]
    fn serialize<S: serde::Serializer>(set: EnumSet<Self>, ser: S) -> Result<S::Ok, S::Error>
    where Self: EnumSetType;
    /// Deserializes the `EnumSet`.
    #[cfg(feature = "serde")]
    fn deserialize<'de, D: serde::Deserializer<'de>>(de: D) -> Result<EnumSet<Self>, D::Error>
    where Self: EnumSetType;
}

/// Retrieves a helper type for constant time operations on `EnumSet`s.
pub unsafe trait EnumSetConstHelper {
    /// A helper type used to convert values to EnumSets at compile-time.
    type ConstInitHelper;
    /// The instance of the `ConstInitHelper`.
    const CONST_INIT_HELPER: Self::ConstInitHelper;

    /// A helper type used to implement compile-time operations on enums.
    type ConstOpHelper;
    /// The instance of the `ConstOpHelper`.
    const CONST_OP_HELPER: Self::ConstOpHelper;
}
