Documentation
use crate::sync::AtomicOp;
use core::marker::PhantomData;
use core::sync::atomic::Ordering;

use super::{Atomic, Discriminant};

/// A type for managing mutually exclusive states (like an atomic enum)
pub struct Mutual<E: Discriminant>
where
    E::Repr: AtomicOp + Into<usize> + TryFrom<usize>,
{
    repr: <E::Repr as Atomic>::Atomic,
    _phantom: PhantomData<E>,
}

impl<E: Discriminant> Default for Mutual<E>
where
    <E as Discriminant>::Repr: AtomicOp + TryFrom<usize>,
    <<E as Discriminant>::Repr as Atomic>::Atomic: Default,
{
    fn default() -> Self {
        Self {
            repr: Default::default(),
            _phantom: Default::default(),
        }
    }
}

impl<E: Discriminant> Mutual<E>
where
    E::Repr: AtomicOp + Into<usize> + TryFrom<usize> + Copy + PartialEq,
{
    pub const fn new() -> Self {
        Self {
            repr: E::Repr::new(),
            _phantom: PhantomData,
        }
    }

    /// Set the state to the given variant
    pub fn set(&self, variant: E) {
        E::Repr::store(&self.repr, variant.discriminant(), Ordering::Release);
    }

    /// Get the current state if it matches the variant
    pub fn is(&self, variant: &E) -> bool {
        let current = E::Repr::load(&self.repr, Ordering::Acquire);
        current == variant.discriminant()
    }

    /// Try to transition from one state to another atomically
    pub fn compare_exchange(
        &self,
        expected: E,
        new: E,
        success: Ordering,
        failure: Ordering,
    ) -> Result<E::Repr, E::Repr> {
        E::Repr::compare_exchange(
            &self.repr,
            expected.discriminant(),
            new.discriminant(),
            success,
            failure,
        )
    }

    /// Get the raw discriminant value
    pub fn load(&self, order: Ordering) -> E::Repr {
        E::Repr::load(&self.repr, order)
    }
}