Documentation
use core::default;
use core::pin::pin;
use core::sync::atomic::Ordering::*;
use core::sync::atomic::{AtomicU8, AtomicU16, AtomicU32, AtomicU64, Ordering};
use core::{
    cell::UnsafeCell,
    marker::PhantomData,
    mem,
    ops::{Deref, DerefMut},
    pin::Pin,
    ptr,
    sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize},
    task::{Poll, Waker},
};

use alloc::boxed::Box;
use derive_more::{Deref, DerefMut};

use q::Queue;
pub mod channel;
pub mod duplex;
pub mod lazy;
pub mod lock;
pub mod map;
pub mod mutual;
pub mod once;
pub mod q;
pub mod ring;
pub mod slab;
use crate::sync;

pub struct Link {
    next: AtomicPtr<Link>,
    prev: AtomicPtr<Link>,
}

impl Default for Link {
    fn default() -> Self {
        Self {
            next: Default::default(),
            prev: Default::default(),
        }
    }
}

#[derive(Default)]
pub struct WakerCell(UnsafeCell<Option<Waker>>);

impl WakerCell {
    pub fn set(&self, waker: Waker) {
        unsafe { *self.0.get() = Some(waker) }
    }

    pub fn take(&self) -> Option<Waker> {
        unsafe { (*self.0.get()).take() }
    }
}

impl Deref for WakerCell {
    type Target = Waker;

    fn deref(&self) -> &Self::Target {
        unsafe { self.0.get().as_ref().unwrap().as_ref().unwrap() }
    }
}

impl DerefMut for WakerCell {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { self.0.get().as_mut().unwrap().as_mut().unwrap() }
    }
}

pub trait Discriminant {
    type Repr: Into<usize>;
    fn discriminant(&self) -> Self::Repr;
}

#[const_trait]
pub trait Atomic: Sized {
    type Atomic;

    fn new() -> Self::Atomic;
}

pub trait AtomicOp: const Atomic {
    fn fetch_or(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self;
    fn fetch_and(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self;
    fn load(atomic: &Self::Atomic, order: Ordering) -> Self;
    fn store(atomic: &Self::Atomic, val: Self, order: Ordering);
    fn compare_exchange(
        atomic: &Self::Atomic,
        current: Self,
        new: Self,
        success: Ordering,
        failure: Ordering,
    ) -> Result<Self, Self>;
}

impl const Atomic for u8 {
    type Atomic = AtomicU8;

    fn new() -> Self::Atomic {
        AtomicU8::new(0)
    }
}
impl AtomicOp for u8 {
    fn fetch_or(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self {
        atomic.fetch_or(val, order)
    }
    fn fetch_and(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self {
        atomic.fetch_and(val, order)
    }
    fn load(atomic: &Self::Atomic, order: Ordering) -> Self {
        atomic.load(order)
    }
    fn store(atomic: &Self::Atomic, val: Self, order: Ordering) {
        atomic.store(val, order)
    }
    fn compare_exchange(
        atomic: &Self::Atomic,
        current: Self,
        new: Self,
        success: Ordering,
        failure: Ordering,
    ) -> Result<Self, Self> {
        atomic.compare_exchange(current, new, success, failure)
    }
}

impl const Atomic for u16 {
    type Atomic = AtomicU16;

    fn new() -> Self::Atomic {
        AtomicU16::new(0)
    }
}
impl AtomicOp for u16 {
    fn fetch_or(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self {
        atomic.fetch_or(val, order)
    }
    fn fetch_and(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self {
        atomic.fetch_and(val, order)
    }
    fn load(atomic: &Self::Atomic, order: Ordering) -> Self {
        atomic.load(order)
    }
    fn store(atomic: &Self::Atomic, val: Self, order: Ordering) {
        atomic.store(val, order)
    }
    fn compare_exchange(
        atomic: &Self::Atomic,
        current: Self,
        new: Self,
        success: Ordering,
        failure: Ordering,
    ) -> Result<Self, Self> {
        atomic.compare_exchange(current, new, success, failure)
    }
}

impl const Atomic for u32 {
    type Atomic = AtomicU32;

    fn new() -> Self::Atomic {
        AtomicU32::new(0)
    }
}
impl AtomicOp for u32 {
    fn fetch_or(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self {
        atomic.fetch_or(val, order)
    }
    fn fetch_and(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self {
        atomic.fetch_and(val, order)
    }
    fn load(atomic: &Self::Atomic, order: Ordering) -> Self {
        atomic.load(order)
    }
    fn store(atomic: &Self::Atomic, val: Self, order: Ordering) {
        atomic.store(val, order)
    }
    fn compare_exchange(
        atomic: &Self::Atomic,
        current: Self,
        new: Self,
        success: Ordering,
        failure: Ordering,
    ) -> Result<Self, Self> {
        atomic.compare_exchange(current, new, success, failure)
    }
}

impl const Atomic for u64 {
    type Atomic = AtomicU64;

    fn new() -> Self::Atomic {
        AtomicU64::new(0)
    }
}
impl AtomicOp for u64 {
    fn fetch_or(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self {
        atomic.fetch_or(val, order)
    }
    fn fetch_and(atomic: &Self::Atomic, val: Self, order: Ordering) -> Self {
        atomic.fetch_and(val, order)
    }
    fn load(atomic: &Self::Atomic, order: Ordering) -> Self {
        atomic.load(order)
    }
    fn store(atomic: &Self::Atomic, val: Self, order: Ordering) {
        atomic.store(val, order)
    }
    fn compare_exchange(
        atomic: &Self::Atomic,
        current: Self,
        new: Self,
        success: Ordering,
        failure: Ordering,
    ) -> Result<Self, Self> {
        atomic.compare_exchange(current, new, success, failure)
    }
}

pub struct Flags<E: Discriminant>
where
    E::Repr: Atomic + Into<usize> + TryFrom<usize>,
{
    repr: <E::Repr as Atomic>::Atomic,
    _phantom: PhantomData<E>,
}

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

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

    pub fn set(&self, variant: &E) {
        let bit: usize = variant.discriminant().into();
        let mask = E::Repr::try_from(1usize << bit)
            .ok()
            .expect("invalid discriminant conversion");
        E::Repr::fetch_or(&self.repr, mask, Ordering::Release);
    }

    pub fn clear(&self, variant: &E) {
        let bit: usize = variant.discriminant().into();
        let mask = E::Repr::try_from(1usize << bit)
            .ok()
            .expect("invalid discriminant conversion");
        E::Repr::fetch_and(&self.repr, mask, Ordering::Release);
    }

    pub fn is_set(&self, variant: &E) -> bool {
        let bit: usize = variant.discriminant().into();
        let mask = E::Repr::try_from(1usize << bit)
            .ok()
            .expect("invalid discriminant conversion");
        let current = E::Repr::load(&self.repr, Ordering::Acquire);
        let current_usize: usize = current.into();
        let mask_usize: usize = mask.into();
        current_usize & mask_usize != 0
    }
    pub fn clear_all(&self) {
        E::Repr::fetch_and(
            &self.repr,
            E::Repr::try_from(0).ok().expect(""),
            Ordering::Release,
        );
    }
}

#[repr(u8)]
#[derive(Clone, Copy)]
pub enum Signal {
    On,
    Off,
}
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum Closure {
    Closed,
}
impl Discriminant for Closure {
    type Repr = u8;

    fn discriminant(&self) -> Self::Repr {
        *self as u8
    }
}
impl Discriminant for Signal {
    type Repr = u8;

    fn discriminant(&self) -> Self::Repr {
        *self as u8
    }
}
#[derive(Default)]
pub struct Waiter {
    link: Link,
    waker: WakerCell,
    signal: Flags<Signal>,
}

impl Waiter {
    pub fn from_waker(waker: Waker) -> Self {
        let mut waiter = Self::default();
        waiter.assign_waker(waker);
        waiter
    }
    pub fn wake_by_ref(&self) {
        self.waker.wake_by_ref();
    }
    pub fn assign_waker(&self, waker: Waker) {
        self.waker.set(waker);
    }
    pub fn signaled(&self) -> bool {
        self.signal.is_set(&Signal::On)
    }
    pub fn signal(&self) {
        self.signal.set(&Signal::On);
    }
    pub fn reset(&mut self) {
        self.link = Link::default();
        self.signal = Default::default();
    }
}

pub type PinnedWaiter = Pin<Box<Waiter>>;

pub type Waiters = Queue<Waiter>;

pub trait WaitersExt {
    fn notify_one(&self);
    fn notify_all(&self);
}

impl WaitersExt for Waiters {
    fn notify_one(&self) {
        if let Some(x) = unsafe { self.dequeue() } {
            x.wake_by_ref();
        }
    }

    fn notify_all(&self) {
        while let Some(x) = unsafe { self.dequeue() } {
            x.wake_by_ref();
        }
    }
}

pub struct AtomicWaker(AtomicPtr<Waker>);

pub struct Barrier {
    count: AtomicUsize,
    generation: AtomicUsize,
    waiters: Waiters,
}

pub struct Semaphore {
    permits: AtomicUsize,
    waiters: Waiters,
}

/// Common initialization states for sync primitives
#[repr(u8)]
#[derive(Clone, Copy, PartialEq)]
pub enum InitState {
    Uninitialized = 0,
    Initializing = 1,
    Initialized = 2,
}

impl Discriminant for InitState {
    type Repr = u8;

    fn discriminant(&self) -> Self::Repr {
        *self as u8
    }
}