// Copyright 2025 Gabriel Bjørnager Jensen.

mod test;

use crate::compat::{StructuralPartialEq, Thin};

use core::array;
use core::borrow::{Borrow, BorrowMut};
use core::mem::MaybeUninit;
use core::ops::{
	Bound,
	Index,
	IndexMut,
	Range,
	RangeFrom,
	RangeFull,
	RangeInclusive,
	RangeTo,
	RangeToInclusive,
};

/// Denotes an array.
///
/// This trait allows abstracting unsized arrays.
/// It provides the methods and functions provided by both [`[_; _]`](prim@array) and the [`array`](mod@array) module.
///
/// When both [`associated_type_defaults`](https://github.com/rust-lang/rust/issues/29661/) and [`generic_const_exprs`](https://github.com/rust-lang/rust/issues/76560/) stabilise, this trait can be expected to be removed.
///
/// # Safety
///
/// `Self` must always be <code>\[[Self::Scalar]; [Self::LEN]\]</code>.
///
/// Furthermore, all items must behave exactly as their standard counterparts.
///
/// # Examples
///
/// Random-access arrays:
///
/// ```rust
/// use multitype::Array;
///
/// fn get_4th_element<T: Array<Scalar: Copy>>(data: T) -> Option<T::Scalar> {
///     data.as_slice().get(4).copied()
/// }
///
/// assert_eq!(
///     get_4th_element([0, 1, 1, 2, 3, 5]),
///     Some(3),
/// );
///
/// assert_eq!(
///     get_4th_element([3, 1, 4, 1, 6]),
///     Some(6),
/// );
///
/// assert_eq!(
///     get_4th_element([1, 6, 1, 8]),
///     None,
/// );
/// ```
pub unsafe trait Array
where
	Self:
		crate::seal::Array
		+ AsMut<[Self::Scalar]>
		+ AsRef<[Self::Scalar]>
		+ Borrow<[Self::Scalar]>
		+ BorrowMut<[Self::Scalar]>
		+ IntoIterator<Item = Self::Scalar>
		+ Sized
		+ StructuralPartialEq
		+ Thin
		// Indexing operators
		+ Index<                    usize,                 Output = Self::Scalar>
		+ Index<   Range<           usize>,                Output = [Self::Scalar]>
		+ Index<   RangeFrom<       usize>,                Output = [Self::Scalar]>
		+ Index<   RangeFull,                              Output = [Self::Scalar]>
		+ Index<   RangeInclusive<  usize>,                Output = [Self::Scalar]>
		+ Index<   RangeTo<         usize>,                Output = [Self::Scalar]>
		+ Index<   RangeToInclusive<usize>,                Output = [Self::Scalar]>
		+ Index<   (Bound<          usize>, Bound<usize>), Output = [Self::Scalar]>
		+ IndexMut<                 usize,                 Output = Self::Scalar>
		+ IndexMut<Range<           usize>,                Output = [Self::Scalar]>
		+ IndexMut<RangeFrom<       usize>,                Output = [Self::Scalar]>
		+ IndexMut<RangeFull,                              Output = [Self::Scalar]>
		+ IndexMut<RangeInclusive<  usize>,                Output = [Self::Scalar]>
		+ IndexMut<RangeTo<         usize>,                Output = [Self::Scalar]>
		+ IndexMut<RangeToInclusive<usize>,                Output = [Self::Scalar]>
		+ IndexMut<(Bound<          usize>, Bound<usize>), Output = [Self::Scalar]>,
{
	/// The scalar (element) type of the slice.
	type Scalar: Sized;

	/// The length of the array, in count of elements.
	const LEN: usize;

	/// See <code>[mod@array]::[repeat](array::repeat)</code>.
	#[must_use]
	fn repeat(value: Self::Scalar) -> Self
	where
		Self::Scalar: Clone;

	/// See <code>[mod@array]::[from_fn](array::from_fn)</code>.
	#[must_use]
	fn from_fn<F: FnMut(usize) -> Self::Scalar>(f: F) -> Self;

	/// See <code>&lt;[\[_; _\]](prim@array)&gt;::[each_mut](array::each_mut)</code>.
	///
	/// The value of [`LEN`](Self::LEN) of the return type must remain equal to that of `Self`.
	#[must_use]
	fn each_ref(&self) -> impl Array<Scalar = &Self::Scalar>;

	/// See <code>&lt;[\[_; _\]](prim@array)&gt;::[each_ref](array::each_ref)</code>.
	///
	/// The value of [`LEN`](Self::LEN) of the return type must remain equal to that of `Self`.
	#[must_use]
	fn each_mut(&mut self) -> impl Array<Scalar = &mut Self::Scalar>;

	/// See <code>&lt;[\[_; _\]](prim@array)&gt;::[map](array::map)</code>.
	///
	/// The value of [`LEN`](Self::LEN) of the return type must remain equal to that of `Self`.
	#[must_use]
	fn map<F: FnMut(Self::Scalar) -> U, U>(self, op: F) -> impl Array<Scalar = U>;

	/// See <code>&lt;[\[_; _\]](prim@array)&gt;::[as_slice](array::as_slice)</code>.
	#[must_use]
	fn as_slice(&self) -> &[Self::Scalar];

	/// See <code>&lt;[\[_; _\]](prim@array)&gt;::[as_mut_slice](array::as_mut_slice)</code>.
	#[must_use]
	fn as_mut_slice(&mut self) -> &mut [Self::Scalar];
}

impl<T, const N: usize> crate::seal::Array for [T; N] {}

unsafe impl<T, const N: usize> Array for [T; N] {
	type Scalar = T;

	const LEN: usize = N;

	// BACKPORT(1.91)
	#[inline]
	fn repeat(value: Self::Scalar) -> Self
	where
		Self::Scalar: Clone,
	{
		let mut this = [const { MaybeUninit::<Self::Scalar>::uninit() }; N];

		for slot in &mut this {
			slot.write(Clone::clone(&value));
		}

		unsafe { (&raw const this as *const Self).read() }
	}

	#[inline(always)]
	#[track_caller]
	fn from_fn<F: FnMut(usize) -> T>(f: F) -> Self {
		array::from_fn(f)
	}

	#[expect(refining_impl_trait_reachable)]
	#[inline(always)]
	#[track_caller]
	fn each_ref(&self) -> [&T; N] {
		self.each_ref()
	}

	#[expect(refining_impl_trait_reachable)]
	#[inline(always)]
	#[track_caller]
	fn each_mut(&mut self) -> [&mut T; N] {
		self.each_mut()
	}

	#[expect(refining_impl_trait_reachable)]
	#[inline(always)]
	#[track_caller]
	fn map<F: FnMut(Self::Scalar) -> U, U>(self, op: F) -> [U; N] {
		self.map(op)
	}

	#[inline(always)]
	#[track_caller]
	fn as_slice(&self) -> &[Self::Scalar] {
		self.as_slice()
	}

	#[inline(always)]
	#[track_caller]
	fn as_mut_slice(&mut self) -> &mut [Self::Scalar] {
		self.as_mut_slice()
	}
}
