#![doc = include_str!("../../doc/slice/traits.md")]
#[cfg(feature = "alloc")]
use alloc::borrow::ToOwned;
use core::{
	cmp,
	convert::TryFrom,
	fmt::{
		self,
		Binary,
		Debug,
		Display,
		Formatter,
		LowerHex,
		Octal,
		Pointer,
		UpperHex,
	},
	hash::{
		Hash,
		Hasher,
	},
	str,
};
use wyz::fmt::FmtForward;
use super::BitSlice;
#[cfg(feature = "alloc")]
use crate::vec::BitVec;
use crate::{
	domain::Domain,
	mem,
	order::{
		BitOrder,
		Lsb0,
		Msb0,
	},
	store::BitStore,
	view::BitView,
};
impl<T, O> AsRef<Self> for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn as_ref(&self) -> &Self {
		self
	}
}
impl<T, O> AsMut<Self> for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn as_mut(&mut self) -> &mut Self {
		self
	}
}
impl<T, O> Eq for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
}
impl<T, O> Ord for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn cmp(&self, rhs: &Self) -> cmp::Ordering {
		self.partial_cmp(rhs)
			.expect("BitSlice has a total ordering")
	}
}
impl<T1, T2, O1, O2> PartialEq<BitSlice<T2, O2>> for BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn eq(&self, rhs: &BitSlice<T2, O2>) -> bool {
		if let (Some(this), Some(that)) =
			(self.coerce::<T1, Lsb0>(), rhs.coerce::<T1, Lsb0>())
		{
			this.sp_eq(that)
		}
		else if let (Some(this), Some(that)) =
			(self.coerce::<T1, Msb0>(), rhs.coerce::<T1, Msb0>())
		{
			this.sp_eq(that)
		}
		else {
			self.len() == rhs.len()
				&& self
					.iter()
					.by_vals()
					.zip(rhs.iter().by_vals())
					.all(|(l, r)| l == r)
		}
	}
}
impl<T1, T2, O1, O2> PartialEq<BitSlice<T2, O2>> for &BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn eq(&self, rhs: &BitSlice<T2, O2>) -> bool {
		**self == rhs
	}
}
impl<T1, T2, O1, O2> PartialEq<BitSlice<T2, O2>> for &mut BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn eq(&self, rhs: &BitSlice<T2, O2>) -> bool {
		**self == rhs
	}
}
impl<T1, T2, O1, O2> PartialEq<&BitSlice<T2, O2>> for BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn eq(&self, rhs: &&BitSlice<T2, O2>) -> bool {
		*self == **rhs
	}
}
impl<T1, T2, O1, O2> PartialEq<&mut BitSlice<T2, O2>> for BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn eq(&self, rhs: &&mut BitSlice<T2, O2>) -> bool {
		*self == **rhs
	}
}
impl<T1, T2, O1, O2> PartialOrd<BitSlice<T2, O2>> for BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn partial_cmp(&self, rhs: &BitSlice<T2, O2>) -> Option<cmp::Ordering> {
		for (l, r) in self.iter().by_vals().zip(rhs.iter().by_vals()) {
			match (l, r) {
				(true, false) => return Some(cmp::Ordering::Greater),
				(false, true) => return Some(cmp::Ordering::Less),
				_ => continue,
			}
		}
		self.len().partial_cmp(&rhs.len())
	}
}
impl<T1, T2, O1, O2> PartialOrd<BitSlice<T2, O2>> for &BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn partial_cmp(&self, rhs: &BitSlice<T2, O2>) -> Option<cmp::Ordering> {
		(*self).partial_cmp(rhs)
	}
}
impl<T1, T2, O1, O2> PartialOrd<BitSlice<T2, O2>> for &mut BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn partial_cmp(&self, rhs: &BitSlice<T2, O2>) -> Option<cmp::Ordering> {
		(**self).partial_cmp(rhs)
	}
}
impl<T1, T2, O1, O2> PartialOrd<&BitSlice<T2, O2>> for BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn partial_cmp(&self, rhs: &&BitSlice<T2, O2>) -> Option<cmp::Ordering> {
		(*self).partial_cmp(&**rhs)
	}
}
impl<T1, T2, O1, O2> PartialOrd<&mut BitSlice<T2, O2>> for BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn partial_cmp(&self, rhs: &&mut BitSlice<T2, O2>) -> Option<cmp::Ordering> {
		(*self).partial_cmp(&**rhs)
	}
}
impl<T1, T2, O1, O2> PartialOrd<&mut BitSlice<T2, O2>> for &BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn partial_cmp(&self, rhs: &&mut BitSlice<T2, O2>) -> Option<cmp::Ordering> {
		(**self).partial_cmp(&**rhs)
	}
}
impl<T1, T2, O1, O2> PartialOrd<&BitSlice<T2, O2>> for &mut BitSlice<T1, O1>
where
	T1: BitStore,
	T2: BitStore,
	O1: BitOrder,
	O2: BitOrder,
{
	#[inline]
	fn partial_cmp(&self, rhs: &&BitSlice<T2, O2>) -> Option<cmp::Ordering> {
		(**self).partial_cmp(&**rhs)
	}
}
impl<'a, T, O> TryFrom<&'a [T]> for &'a BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	type Error = &'a [T];
	#[inline]
	fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
		BitSlice::try_from_slice(slice).map_err(|_| slice)
	}
}
impl<'a, T, O> TryFrom<&'a mut [T]> for &'a mut BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	type Error = &'a mut [T];
	#[inline]
	fn try_from(slice: &'a mut [T]) -> Result<Self, Self::Error> {
		let slice_ptr = slice as *mut [T];
		BitSlice::try_from_slice_mut(slice)
			.map_err(|_| unsafe { &mut *slice_ptr })
	}
}
impl<T, O> Default for &BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn default() -> Self {
		BitSlice::empty()
	}
}
impl<T, O> Default for &mut BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn default() -> Self {
		BitSlice::empty_mut()
	}
}
impl<T, O> Debug for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
		self.as_bitspan().render(fmt, "Slice", None)?;
		fmt.write_str(" ")?;
		Display::fmt(self, fmt)
	}
}
impl<T, O> Display for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
		fmt.debug_list()
			.entries(self.iter().by_vals().map(|b| if b { 1 } else { 0 }))
			.finish()
	}
}
impl<T, O> Pointer for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
		Pointer::fmt(&self.as_bitspan(), fmt)
	}
}
#[inline(always)]
fn bits_to_ascii<T, O>(bits: &BitSlice<T, O>, alpha: u8) -> u8
where
	T: BitStore,
	O: BitOrder,
{
	let mut val = 0u8;
	for bit in bits.iter().by_vals() {
		val <<= 1;
		val |= bit as u8;
	}
	match val {
		v @ 0 ..= 9 => b'0' + v,
		v @ 10 ..= 35 => alpha - 10 + v,
		_ => unreachable!(
			"bit-slices wider than five bits cannot be rendered to ASCII b36"
		),
	}
}
#[inline(always)]
fn encode_ascii<'a, T, O>(
	bits: &BitSlice<T, O>,
	into: &'a mut [u8],
	radix: usize,
	mut skip: usize,
	alpha: u8,
) -> &'a str
where
	T: BitStore,
	O: BitOrder,
{
	for (chunk, slot) in
		bits.rchunks(radix).rev().zip(into.iter_mut().skip(skip))
	{
		*slot = bits_to_ascii(chunk, alpha);
		skip += 1;
	}
	unsafe { str::from_utf8_unchecked(&into[.. skip]) }
}
macro_rules! fmt {
	($($trait:ident: $alpha:expr, $pfx:expr, $radix:expr;)+) => { $(
		#[doc = include_str!("../../doc/slice/format.md")]
		impl<T, O> $trait for BitSlice<T, O>
		where
			T: BitStore,
			O: BitOrder,
		{
			#[inline]
			#[allow(clippy::modulo_one)] fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
				const D: usize = mem::bits_of::<usize>() / $radix;
				const M: usize = mem::bits_of::<usize>() % $radix;
				const W: usize = D + (M != 0) as usize;
				let mut txt: [u8; W + 2] = [b'0'; W + 2];
				txt[1] = $pfx;
				let start = if fmt.alternate() { 0 } else { 2 };
				let mut seq = fmt.debug_list();
				match self.domain() {
					Domain::Enclave(elem) => {
						seq.entry(&encode_ascii(
							elem.into_bitslice(),
							&mut txt[start ..],
							$radix,
							2 - start,
							$alpha,
						).fmt_display());
					},
					Domain::Region { head, body, tail } => {
						if let Some(elem) = head {
							seq.entry(&encode_ascii(
								elem.into_bitslice(),
								&mut txt[start ..],
								$radix,
								2 - start,
								$alpha,
							).fmt_display());
						}
						for elem in body.iter().map(BitStore::load_value) {
							seq.entry(&encode_ascii(
								elem.view_bits::<O>(),
								&mut txt[start ..],
								$radix,
								2 - start,
								$alpha,
							).fmt_display());
						}
						if let Some(elem) = tail {
							seq.entry(&encode_ascii(
								elem.into_bitslice(),
								&mut txt[start ..],
								$radix,
								2 - start,
								$alpha,
							).fmt_display());
						}
					},
				}
				seq.finish()
			}
		}
	)+ };
}
fmt! {
	Binary: b'0', b'b', 1;
	Octal: b'0', b'o', 3;
	LowerHex: b'a', b'x', 4;
	UpperHex: b'A', b'x', 4;
}
#[cfg(not(tarpaulin_include))]
impl<T, O> Hash for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	#[inline]
	fn hash<H>(&self, hasher: &mut H)
	where H: Hasher {
		self.iter().by_vals().for_each(|bit| bit.hash(hasher));
	}
}
#[doc = include_str!("../../doc/slice/threadsafe.md")]
unsafe impl<T, O> Send for BitSlice<T, O>
where
	T: BitStore + Sync,
	O: BitOrder,
{
}
#[doc = include_str!("../../doc/slice/threadsafe.md")]
unsafe impl<T, O> Sync for BitSlice<T, O>
where
	T: BitStore + Sync,
	O: BitOrder,
{
}
impl<T, O> Unpin for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
}
#[cfg(feature = "alloc")]
#[cfg(not(tarpaulin_include))]
impl<T, O> ToOwned for BitSlice<T, O>
where
	T: BitStore,
	O: BitOrder,
{
	type Owned = BitVec<T, O>;
	#[inline]
	fn to_owned(&self) -> Self::Owned {
		BitVec::from_bitslice(self)
	}
}