use core::{
    fmt,
    ops::{BitAnd, BitOr, BitXor, Not},
};

use crate::{
    iter,
    parser::{ParseError, ParseHex, WriteHex},
};

/**
A defined flags value that may be named or unnamed.
*/
pub struct Flag<B> {
    name: &'static str,
    value: B,
}

impl<B> Flag<B> {
    /**
    Define a flag.

    If `name` is non-empty then the flag is named, otherwise it's unnamed.
    */
    pub const fn new(name: &'static str, value: B) -> Self {
        Flag { name, value }
    }

    /**
    Get the name of this flag.

    If the flag is unnamed then the returned string will be empty.
    */
    pub const fn name(&self) -> &'static str {
        self.name
    }

    /**
    Get the flags value of this flag.
    */
    pub const fn value(&self) -> &B {
        &self.value
    }

    /**
    Whether the flag is named.

    If [`Flag::name`] returns a non-empty string then this method will return `true`.
    */
    pub const fn is_named(&self) -> bool {
        !self.name.is_empty()
    }

    /**
    Whether the flag is unnamed.

    If [`Flag::name`] returns a non-empty string then this method will return `false`.
    */
    pub const fn is_unnamed(&self) -> bool {
        self.name.is_empty()
    }
}

/**
A set of defined flags using a bits type as storage.

## Implementing `Flags`

This trait is implemented by the [`bitflags`](macro.bitflags.html) macro:

```
use bitflags::bitflags;

bitflags! {
    struct MyFlags: u8 {
        const A = 1;
        const B = 1 << 1;
    }
}
```

It can also be implemented manually:

```
use bitflags::{Flag, Flags};

struct MyFlags(u8);

impl Flags for MyFlags {
    const FLAGS: &'static [Flag<Self>] = &[
        Flag::new("A", MyFlags(1)),
        Flag::new("B", MyFlags(1 << 1)),
    ];

    type Bits = u8;

    fn from_bits_retain(bits: Self::Bits) -> Self {
        MyFlags(bits)
    }

    fn bits(&self) -> Self::Bits {
        self.0
    }
}
```

## Using `Flags`

The `Flags` trait can be used generically to work with any flags types. In this example,
we can count the number of defined named flags:

```
# use bitflags::{bitflags, Flags};
fn defined_flags<F: Flags>() -> usize {
    F::FLAGS.iter().filter(|f| f.is_named()).count()
}

bitflags! {
    struct MyFlags: u8 {
        const A = 1;
        const B = 1 << 1;
        const C = 1 << 2;

        const _ = !0;
    }
}

assert_eq!(3, defined_flags::<MyFlags>());
```
*/
pub trait Flags: Sized + 'static {
    /// The set of defined flags.
    const FLAGS: &'static [Flag<Self>];

    /// The underlying bits type.
    type Bits: Bits;

    /// Get a flags value with all bits unset.
    fn empty() -> Self {
        Self::from_bits_retain(Self::Bits::EMPTY)
    }

    /// Get a flags value with all known bits set.
    fn all() -> Self {
        let mut truncated = Self::Bits::EMPTY;

        for flag in Self::FLAGS.iter() {
            truncated = truncated | flag.value().bits();
        }

        Self::from_bits_retain(truncated)
    }

    /// Get the underlying bits value.
    ///
    /// The returned value is exactly the bits set in this flags value.
    fn bits(&self) -> Self::Bits;

    /// Convert from a bits value.
    ///
    /// This method will return `None` if any unknown bits are set.
    fn from_bits(bits: Self::Bits) -> Option<Self> {
        let truncated = Self::from_bits_truncate(bits);

        if truncated.bits() == bits {
            Some(truncated)
        } else {
            None
        }
    }

    /// Convert from a bits value, unsetting any unknown bits.
    fn from_bits_truncate(bits: Self::Bits) -> Self {
        Self::from_bits_retain(bits & Self::all().bits())
    }

    /// Convert from a bits value exactly.
    fn from_bits_retain(bits: Self::Bits) -> Self;

    /// Get a flags value with the bits of a flag with the given name set.
    ///
    /// This method will return `None` if `name` is empty or doesn't
    /// correspond to any named flag.
    fn from_name(name: &str) -> Option<Self> {
        // Don't parse empty names as empty flags
        if name.is_empty() {
            return None;
        }

        for flag in Self::FLAGS {
            if flag.name() == name {
                return Some(Self::from_bits_retain(flag.value().bits()));
            }
        }

        None
    }

    /// Yield a set of contained flags values.
    ///
    /// Each yielded flags value will correspond to a defined named flag. Any unknown bits
    /// will be yielded together as a final flags value.
    fn iter(&self) -> iter::Iter<Self> {
        iter::Iter::new(self)
    }

    /// Yield a set of contained named flags values.
    ///
    /// This method is like [`Flags::iter`], except only yields bits in contained named flags.
    /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
    fn iter_names(&self) -> iter::IterNames<Self> {
        iter::IterNames::new(self)
    }

    /// Whether all bits in this flags value are unset.
    fn is_empty(&self) -> bool {
        self.bits() == Self::Bits::EMPTY
    }

    /// Whether all known bits in this flags value are set.
    fn is_all(&self) -> bool {
        // NOTE: We check against `Self::all` here, not `Self::Bits::ALL`
        // because the set of all flags may not use all bits
        Self::all().bits() | self.bits() == self.bits()
    }

    /// Whether any set bits in a source flags value are also set in a target flags value.
    fn intersects(&self, other: Self) -> bool
    where
        Self: Sized,
    {
        self.bits() & other.bits() != Self::Bits::EMPTY
    }

    /// Whether all set bits in a source flags value are also set in a target flags value.
    fn contains(&self, other: Self) -> bool
    where
        Self: Sized,
    {
        self.bits() & other.bits() == other.bits()
    }

    /// The bitwise or (`|`) of the bits in two flags values.
    fn insert(&mut self, other: Self)
    where
        Self: Sized,
    {
        *self = Self::from_bits_retain(self.bits()).union(other);
    }

    /// The intersection of a source flags value with the complement of a target flags value (`&!`).
    ///
    /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
    /// `remove` won't truncate `other`, but the `!` operator will.
    fn remove(&mut self, other: Self)
    where
        Self: Sized,
    {
        *self = Self::from_bits_retain(self.bits()).difference(other);
    }

    /// The bitwise exclusive-or (`^`) of the bits in two flags values.
    fn toggle(&mut self, other: Self)
    where
        Self: Sized,
    {
        *self = Self::from_bits_retain(self.bits()).symmetric_difference(other);
    }

    /// Call [`Flags::insert`] when `value` is `true` or [`Flags::remove`] when `value` is `false`.
    fn set(&mut self, other: Self, value: bool)
    where
        Self: Sized,
    {
        if value {
            self.insert(other);
        } else {
            self.remove(other);
        }
    }

    /// The bitwise and (`&`) of the bits in two flags values.
    #[must_use]
    fn intersection(self, other: Self) -> Self {
        Self::from_bits_retain(self.bits() & other.bits())
    }

    /// The bitwise or (`|`) of the bits in two flags values.
    #[must_use]
    fn union(self, other: Self) -> Self {
        Self::from_bits_retain(self.bits() | other.bits())
    }

    /// The intersection of a source flags value with the complement of a target flags value (`&!`).
    ///
    /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
    /// `difference` won't truncate `other`, but the `!` operator will.
    #[must_use]
    fn difference(self, other: Self) -> Self {
        Self::from_bits_retain(self.bits() & !other.bits())
    }

    /// The bitwise exclusive-or (`^`) of the bits in two flags values.
    #[must_use]
    fn symmetric_difference(self, other: Self) -> Self {
        Self::from_bits_retain(self.bits() ^ other.bits())
    }

    /// The bitwise negation (`!`) of the bits in a flags value, truncating the result.
    #[must_use]
    fn complement(self) -> Self {
        Self::from_bits_truncate(!self.bits())
    }
}

/**
A bits type that can be used as storage for a flags type.
*/
pub trait Bits:
    Clone
    + Copy
    + PartialEq
    + BitAnd<Output = Self>
    + BitOr<Output = Self>
    + BitXor<Output = Self>
    + Not<Output = Self>
    + Sized
    + 'static
{
    /// A value with all bits unset.
    const EMPTY: Self;

    /// A value with all bits set.
    const ALL: Self;
}

// Not re-exported: prevent custom `Bits` impls being used in the `bitflags!` macro,
// or they may fail to compile based on crate features
pub trait Primitive {}

macro_rules! impl_bits {
    ($($u:ty, $i:ty,)*) => {
        $(
            impl Bits for $u {
                const EMPTY: $u = 0;
                const ALL: $u = <$u>::MAX;
            }

            impl Bits for $i {
                const EMPTY: $i = 0;
                const ALL: $i = <$u>::MAX as $i;
            }

            impl ParseHex for $u {
                fn parse_hex(input: &str) -> Result<Self, ParseError> {
                    <$u>::from_str_radix(input, 16).map_err(|_| ParseError::invalid_hex_flag(input))
                }
            }

            impl ParseHex for $i {
                fn parse_hex(input: &str) -> Result<Self, ParseError> {
                    <$i>::from_str_radix(input, 16).map_err(|_| ParseError::invalid_hex_flag(input))
                }
            }

            impl WriteHex for $u {
                fn write_hex<W: fmt::Write>(&self, mut writer: W) -> fmt::Result {
                    write!(writer, "{:x}", self)
                }
            }

            impl WriteHex for $i {
                fn write_hex<W: fmt::Write>(&self, mut writer: W) -> fmt::Result {
                    write!(writer, "{:x}", self)
                }
            }

            impl Primitive for $i {}
            impl Primitive for $u {}
        )*
    }
}

impl_bits! {
    u8, i8,
    u16, i16,
    u32, i32,
    u64, i64,
    u128, i128,
    usize, isize,
}

/// A trait for referencing the `bitflags`-owned internal type
/// without exposing it publicly.
pub trait PublicFlags {
    /// The type of the underlying storage.
    type Primitive: Primitive;

    /// The type of the internal field on the generated flags type.
    type Internal;
}

#[doc(hidden)]
#[deprecated(note = "use the `Flags` trait instead")]
pub trait BitFlags: ImplementedByBitFlagsMacro + Flags {
    /// An iterator over enabled flags in an instance of the type.
    type Iter: Iterator<Item = Self>;

    /// An iterator over the raw names and bits for enabled flags in an instance of the type.
    type IterNames: Iterator<Item = (&'static str, Self)>;
}

#[allow(deprecated)]
impl<B: Flags> BitFlags for B {
    type Iter = iter::Iter<Self>;
    type IterNames = iter::IterNames<Self>;
}

impl<B: Flags> ImplementedByBitFlagsMacro for B {}

/// A marker trait that signals that an implementation of `BitFlags` came from the `bitflags!` macro.
///
/// There's nothing stopping an end-user from implementing this trait, but we don't guarantee their
/// manual implementations won't break between non-breaking releases.
#[doc(hidden)]
pub trait ImplementedByBitFlagsMacro {}

pub(crate) mod __private {
    pub use super::{ImplementedByBitFlagsMacro, PublicFlags};
}
