#![cfg(not(tarpaulin_include))]
use core::{
	cmp,
	convert::TryFrom,
	fmt::{
		self,
		Debug,
		Display,
		Formatter,
		Pointer,
	},
	hash::{
		Hash,
		Hasher,
	},
	ops::Deref,
	ptr::NonNull,
	slice,
};
use tap::Pipe;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Const;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Mut;
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Frozen<Inner>
where Inner: Mutability
{
	inner: Inner,
}
pub trait Mutability: 'static + Copy + Sized + self::seal::Sealed {
	const CONTAINS_MUTABILITY: bool = false;
	const PEANO_NUMBER: usize = 0;
	const SELF: Self;
	const RENDER: &'static str;
	fn freeze(self) -> Frozen<Self> {
		Frozen { inner: self }
	}
	fn thaw(Frozen { inner }: Frozen<Self>) -> Self {
		inner
	}
}
impl Mutability for Const {
	const RENDER: &'static str = "*const";
	const SELF: Self = Self;
}
impl self::seal::Sealed for Const {
}
impl<Inner> Mutability for Frozen<Inner>
where Inner: Mutability + Sized
{
	const CONTAINS_MUTABILITY: bool = Inner::CONTAINS_MUTABILITY;
	const PEANO_NUMBER: usize = 1 + Inner::PEANO_NUMBER;
	const RENDER: &'static str = Inner::RENDER;
	const SELF: Self = Self { inner: Inner::SELF };
}
impl<Inner> self::seal::Sealed for Frozen<Inner> where Inner: Mutability + Sized
{
}
impl Mutability for Mut {
	const CONTAINS_MUTABILITY: bool = true;
	const RENDER: &'static str = "*mut";
	const SELF: Self = Self;
}
impl self::seal::Sealed for Mut {
}
pub struct Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
	inner: NonNull<T>,
	comu: M,
}
impl<M, T> Address<M, T>
where M: Mutability
{
	pub const DANGLING: Self = Self {
		inner: NonNull::dangling(),
		comu: M::SELF,
	};
}
impl<M, T> Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
	#[inline(always)]
	pub fn new(addr: NonNull<T>) -> Self {
		Self {
			inner: addr,
			comu: M::SELF,
		}
	}
	#[inline(always)]
	pub fn immut(self) -> Address<Const, T> {
		Address {
			inner: self.inner,
			comu: Const,
		}
	}
	#[inline(always)]
	pub unsafe fn assert_mut(self) -> Address<Mut, T> {
		Address {
			inner: self.inner,
			comu: Mut,
		}
	}
	#[inline(always)]
	pub fn freeze(self) -> Address<Frozen<M>, T> {
		let Self { inner, comu } = self;
		Address {
			inner,
			comu: comu.freeze(),
		}
	}
	#[inline(always)]
	pub fn into_inner(self) -> NonNull<T> {
		self.inner
	}
	#[inline(always)]
	pub fn to_const(self) -> *const T {
		self.inner.as_ptr() as *const T
	}
}
impl<T> Address<Mut, T> {
	#[inline(always)]
	#[allow(clippy::wrong_self_convention)]
	pub fn to_mut(self) -> *mut T {
		self.inner.as_ptr()
	}
}
impl<M, T> Address<Frozen<M>, T>
where
	M: Mutability,
	T: ?Sized,
{
	#[inline(always)]
	pub fn thaw(self) -> Address<M, T> {
		let Self { inner, comu } = self;
		Address {
			inner,
			comu: Mutability::thaw(comu),
		}
	}
}
macro_rules! fwd {
	($(
		$(@$unsafe:ident)?
		$name:ident
		$(<
			$($lt:lifetime),*
			$($typaram:ident$(: $($bound:ident),+ $(,)?)?),*
			$(,)*
		>)?
		$(, $arg:ident: $ty:ty)*
		$(=> $ret:ty)?
	);+ $(;)?) => { $(
		#[doc = concat!("Applies `<*T>::", stringify!($name), "`.")]
		#[doc = concat!("[orig]: https://doc.rust-lang.org/std/primitive.pointer.html#method.", stringify!($name))]
		pub $($unsafe)? fn $name$(<
			$($lt,)* $($typaram$(: $($bound),+)?,)*
		>)?(self$(, $arg: $ty)*) $(-> $ret)? {
			self.with_ptr(|ptr| ptr.$name($($arg),*))
		}
	)+ };
}
macro_rules! map {
	($(
		$(@$unsafe:ident)?
		$name:ident
		$(<
			$($lt:lifetime),*
			$($typaram:ident$(: $($bound:ident),+ $(,)?)?),*
			$(,)?
		>)?
		$(, $arg:ident: $ty:ty $(as $map:expr)?)*
		$(=> $ret:ty)?
	);+ $(;)?) => { $(
		#[doc = concat!("Applies `<*T>::", stringify!($name), "`.")]
		#[doc = concat!("[orig]: https://doc.rust-lang.org/std/primitive.pointer.html#method.", stringify!($name))]
		pub $($unsafe)? fn $name$(<
			$($lt,)* $($typaram$(: $($bound),+)?,)*
		>)?(self$(, $arg: $ty)*) $(-> $ret)? {
			self.inner.as_ptr().$name($($arg$(.pipe($map))?),*)
		}
	)+ };
}
#[allow(clippy::missing_safety_doc)]
impl<M, T> Address<M, T>
where M: Mutability
{
	fwd! {
		cast<U> => Address<M, U>;
		@unsafe offset, count: isize => Self;
		@unsafe add, count: usize => Self;
		@unsafe sub, count: usize => Self;
		wrapping_offset, count: isize => Self;
		wrapping_add, count: usize => Self;
		wrapping_sub, count: usize => Self;
	}
	map! {
		@unsafe offset_from, origin: Self as |orig| orig.to_const() as *mut T => isize;
		@unsafe read => T;
		@unsafe read_volatile => T;
		@unsafe read_unaligned => T;
		@unsafe copy_to, dest: Address<Mut, T> as Address::to_mut, count: usize;
		@unsafe copy_to_nonoverlapping, dest: Address<Mut, T> as Address::to_mut, count: usize;
		align_offset, align: usize => usize;
	}
}
impl<M, T> Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
	map! {
		@unsafe as_ref<'a> => Option<&'a T>;
	}
	#[track_caller]
	fn with_ptr<U>(self, func: impl FnOnce(*mut T) -> *mut U) -> Address<M, U> {
		self.inner
			.as_ptr()
			.pipe(func)
			.pipe(NonNull::new)
			.unwrap()
			.pipe(Address::new)
	}
}
impl<T> Address<Mut, T> {
	map! {
		@unsafe copy_from<M2: Mutability>, src: Address<M2, T> as Address::to_const, count: usize;
		@unsafe copy_from_nonoverlapping<M2: Mutability>, src: Address<M2, T> as Address::to_const, count: usize;
		@unsafe write, value: T;
		@unsafe write_volatile, value: T;
		@unsafe write_unaligned, value: T;
		@unsafe replace, src: T => T;
		@unsafe swap, with: Self as Self::to_mut;
	}
}
impl<T> Address<Mut, T>
where T: ?Sized
{
	map! {
		@unsafe as_mut<'a> => Option<&'a mut T>;
		@unsafe drop_in_place;
	}
}
impl<M, T> Clone for Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
	#[inline(always)]
	fn clone(&self) -> Self {
		*self
	}
}
impl<T> TryFrom<*const T> for Address<Const, T>
where T: ?Sized
{
	type Error = NullPtrError;
	#[inline(always)]
	fn try_from(elem: *const T) -> Result<Self, Self::Error> {
		NonNull::new(elem as *mut T)
			.ok_or(NullPtrError)
			.map(Self::new)
	}
}
impl<T> From<&T> for Address<Const, T>
where T: ?Sized
{
	#[inline(always)]
	fn from(elem: &T) -> Self {
		Self::new(elem.into())
	}
}
impl<T> TryFrom<*mut T> for Address<Mut, T>
where T: ?Sized
{
	type Error = NullPtrError;
	#[inline(always)]
	fn try_from(elem: *mut T) -> Result<Self, Self::Error> {
		NonNull::new(elem).ok_or(NullPtrError).map(Self::new)
	}
}
impl<T> From<&mut T> for Address<Mut, T>
where T: ?Sized
{
	#[inline(always)]
	fn from(elem: &mut T) -> Self {
		Self::new(elem.into())
	}
}
impl<M, T> Eq for Address<M, T> where M: Mutability
{
}
impl<M1, M2, T1, T2> PartialEq<Address<M2, T2>> for Address<M1, T1>
where
	M1: Mutability,
	M2: Mutability,
{
	#[inline]
	fn eq(&self, other: &Address<M2, T2>) -> bool {
		self.inner.as_ptr() as usize == other.inner.as_ptr() as usize
	}
}
impl<M, T> Ord for Address<M, T>
where M: Mutability
{
	#[inline]
	fn cmp(&self, other: &Self) -> cmp::Ordering {
		self.partial_cmp(other)
			.expect("Addresses have a total ordering")
	}
}
impl<M1, M2, T1, T2> PartialOrd<Address<M2, T2>> for Address<M1, T1>
where
	M1: Mutability,
	M2: Mutability,
{
	#[inline]
	fn partial_cmp(&self, other: &Address<M2, T2>) -> Option<cmp::Ordering> {
		(self.inner.as_ptr() as usize)
			.partial_cmp(&(other.inner.as_ptr() as usize))
	}
}
impl<M, T> Debug for Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
	#[inline(always)]
	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
		Debug::fmt(&self.to_const(), fmt)
	}
}
impl<M, T> Pointer for Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
	#[inline(always)]
	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
		Pointer::fmt(&self.to_const(), fmt)
	}
}
impl<M, T> Hash for Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
	#[inline(always)]
	fn hash<H>(&self, state: &mut H)
	where H: Hasher {
		self.inner.hash(state)
	}
}
impl<M, T> Copy for Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
}
impl<M, T> self::seal::Sealed for Address<M, T>
where
	M: Mutability,
	T: ?Sized,
{
}
pub trait Referential<'a>: self::seal::Sealed {
	type Ref: 'a + Deref;
	unsafe fn to_ref(self) -> Self::Ref;
	fn from_ref(this: Self::Ref) -> Self;
}
impl<'a, T> Referential<'a> for Address<Const, T>
where T: 'a + ?Sized
{
	type Ref = &'a T;
	unsafe fn to_ref(self) -> Self::Ref {
		self.inner.as_ref()
	}
	fn from_ref(this: Self::Ref) -> Self {
		this.into()
	}
}
impl<'a, T> Referential<'a> for Address<Mut, T>
where T: 'a + ?Sized
{
	type Ref = &'a mut T;
	unsafe fn to_ref(mut self) -> Self::Ref {
		self.inner.as_mut()
	}
	fn from_ref(this: Self::Ref) -> Self {
		this.into()
	}
}
impl<'a, M, T> Referential<'a> for Address<Frozen<M>, T>
where
	M: Mutability,
	T: 'a + ?Sized,
{
	type Ref = &'a T;
	unsafe fn to_ref(self) -> Self::Ref {
		self.inner.as_ref()
	}
	fn from_ref(this: Self::Ref) -> Self {
		Self::new(NonNull::from(this))
	}
}
pub type Reference<'a, M, T> = <Address<M, T> as Referential<'a>>::Ref;
pub trait SliceReferential<'a>: Referential<'a> + self::seal::Sealed {
	type ElementAddr;
	unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref;
}
impl<'a, T> SliceReferential<'a> for Address<Const, [T]>
where T: 'a
{
	type ElementAddr = Address<Const, T>;
	unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref {
		slice::from_raw_parts(ptr.to_const(), len)
	}
}
impl<'a, M, T> SliceReferential<'a> for Address<Frozen<M>, [T]>
where
	M: Mutability,
	T: 'a,
{
	type ElementAddr = Address<Frozen<M>, T>;
	unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref {
		slice::from_raw_parts(ptr.to_const(), len)
	}
}
impl<'a, T> SliceReferential<'a> for Address<Mut, [T]>
where T: 'a
{
	type ElementAddr = Address<Mut, T>;
	unsafe fn from_raw_parts(ptr: Self::ElementAddr, len: usize) -> Self::Ref {
		slice::from_raw_parts_mut(ptr.to_mut(), len)
	}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NullPtrError;
impl Display for NullPtrError {
	#[inline]
	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
		write!(fmt, "wyz::Address cannot contain a null pointer")
	}
}
#[cfg(feature = "std")]
impl std::error::Error for NullPtrError {
}
#[doc(hidden)]
mod seal {
	#[doc(hidden)]
	pub trait Sealed {}
}