use std::{
    fmt::{self, Debug},
    iter::FusedIterator,
};

use crate::{DERIVES, codegen::DeriveId};

/// Number of bytes required for bit set which can represent all [`DeriveId`]s.
const NUM_BYTES: usize = DERIVES.len().div_ceil(8);

/// Bit set with a bit for each [`DeriveId`].
#[derive(Clone, Copy)]
pub struct Derives([u8; NUM_BYTES]);

impl Derives {
    /// Create empty [`Derives`] with no bits set.
    /// i.e. the type doesn't derive any traits generated by this crate.
    pub const fn none() -> Self {
        Self([0; NUM_BYTES])
    }

    /// Create [`Derives`] with all bits set.
    /// i.e. the type derives all traits generated by this crate.
    pub const fn all() -> Self {
        let mut out = Self::none();
        let mut index = 0;
        while index < DERIVES.len() {
            out = out.with(index);
            index += 1;
        }
        out
    }

    /// Returns `true` if the type derives the derive with provided [`DeriveId`].
    pub const fn has(self, id: DeriveId) -> bool {
        let (byte_index, mask) = Self::byte_index_and_mask(id);
        (self.0[byte_index] & mask) != 0
    }

    /// Set bit for provided [`DeriveId`], and return a new [`Derives`].
    /// i.e. the type derives this trait.
    pub const fn with(mut self, id: DeriveId) -> Self {
        let (byte_index, mask) = Self::byte_index_and_mask(id);
        self.0[byte_index] |= mask;
        self
    }

    /// Unset bit for provided [`DeriveId`], and return a new [`Derives`].
    /// i.e. the type does not derive this trait.
    pub const fn without(mut self, id: DeriveId) -> Self {
        let (byte_index, mask) = Self::byte_index_and_mask(id);
        self.0[byte_index] &= !mask;
        self
    }

    /// Set bit for provided [`DeriveId`] on this [`Derives`].
    /// i.e. the type derives this trait.
    pub fn add(&mut self, id: DeriveId) {
        *self = self.with(id);
    }

    /// Unset bit for provided [`DeriveId`] on this [`Derives`].
    /// i.e. the type does not derive this trait.
    pub fn remove(&mut self, id: DeriveId) {
        *self = self.without(id);
    }

    /// Get byte index and mask for a [`DeriveId`].
    /// (internal method)
    const fn byte_index_and_mask(id: DeriveId) -> (usize, u8) {
        (id / 8, 1u8 << (id & 7))
    }
}

impl IntoIterator for Derives {
    type Item = DeriveId;
    type IntoIter = DerivesIter;

    fn into_iter(self) -> DerivesIter {
        DerivesIter::new(self)
    }
}

impl IntoIterator for &Derives {
    type Item = DeriveId;
    type IntoIter = DerivesIter;

    fn into_iter(self) -> DerivesIter {
        DerivesIter::new(*self)
    }
}

/// Iterator over the [`DeriveId`]s which this [`Derives`] does derive,
/// in ascending order of [`DeriveId`].
///
/// i.e. yields the [`DeriveId`]s for traits which a type derives.
pub struct DerivesIter {
    derives: Derives,
    next_id: DeriveId,
}

impl DerivesIter {
    fn new(derives: Derives) -> Self {
        Self { derives, next_id: 0 }
    }
}

impl Iterator for DerivesIter {
    type Item = DeriveId;

    fn next(&mut self) -> Option<DeriveId> {
        while self.next_id < DERIVES.len() {
            let id = self.next_id;
            self.next_id += 1;

            if self.derives.has(id) {
                return Some(id);
            }
        }

        None
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, Some(DERIVES.len() - self.next_id))
    }
}

impl FusedIterator for DerivesIter {}

impl Debug for Derives {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_list().entries(self.into_iter().map(|id| DERIVES[id].trait_name())).finish()
    }
}
