// Copyright 2025-2026 Gabriel Bjørnager Jensen.
//
// SPDX: MIT OR Apache-2.0

//! Conversion facilities for [`NVec`].

use core::mem::{ManuallyDrop, MaybeUninit, offset_of};
use core::slice;
use nvec::n_vec::{NVec, TryReserveError};

#[cfg(feature = "alloc")]
use alloc::boxed::Box;

#[cfg(feature = "alloc")]
use alloc::vec::Vec;

impl<T, const N: usize> NVec<T, N> {
	/// Constructs a new N-vector by repeating an
	/// element.
	///
	/// # Errors
	///
	/// This constructor will return an [`Err`] instance
	/// if the provided length is greater than the
	/// vector's capacity.
	pub fn try_from_elem(elem: T, len: usize) -> Result<Self, TryReserveError>
	where
		T: Clone,
	{
		if len > N {
			return Err(TryReserveError);
		}

		let mut new = Self::new();

		// Just return if no repeats are to be made.
		if len == 0 {
			return Ok(new);
		}

		// The index of the move. The result cannot under-
		// flow.
		let last_index = len - 1;

		// Get the slots of the new elements. Remember to
		// panic if capacity is insufficient.
		//
		// SAFETY: We have tested that this length is
		// within the capacity of the vector.
		let slots = unsafe { new.spare_capacity_mut().get_unchecked_mut(..len) };

		// Initialise the slots but not the last one; the
		// element is to finally be moved into it, avoiding
		// one clone.
		for slot in &mut slots[..last_index] {
			slot.write(elem.clone());
		}

		// Move the element into the final slot.
		slots[last_index].write(elem);

		// SAFETY: We have cloned and moved the provided
		// element into these slots.
		unsafe { new.set_len(len) };

		Ok(new)
	}

	/// Constructs an N-vector from a slice.
	///
	/// The slice elements are cloned into the N-vector.
	///
	/// # Errors
	///
	/// This constructor will return an [`Err`] instance
	/// if the provided slice is longer than the
	/// N-vector's capacity.
	pub fn try_from_slice(s: &[T]) -> Result<Self, TryReserveError>
	where
		T: Clone,
	{
		let mut new = Self::try_with_capacity(s.len())?;

		for elem in s.iter().cloned() {
			// SAFETY: We have tested that capacity is suffi-
			// cient for all slice elements.
			unsafe { new.push_unchecked_mut(elem) };
		}

		Ok(new)
	}

	/// Constructs an N-vector from array data.
	#[inline]
	#[must_use]
	pub const fn from_array(a: [T; N]) -> Self {
		union Transmute<T, const N: usize> {
			src: ManuallyDrop<[T; N]>,

			dst: ManuallyDrop<[MaybeUninit<T>; N]>,
		}

		let transmute = Transmute { src: ManuallyDrop::new(a) };

		// SAFETY: I don't feel like it.
		let data = unsafe { ManuallyDrop::into_inner(transmute.dst) };

		// SAFETY: I don't feel like it.
		unsafe { Self::from_raw_parts(data, N) }
	}

	/// Constructs a new N-vector from function results.
	///
	/// # Errors
	///
	/// If the provided length is greater than the
	/// capacity of the vector, this constructor will
	/// return an [`Err`] instance.
	pub fn try_from_fn<F: FnMut(usize) -> T>(len: usize, mut f: F) -> Result<Self, TryReserveError> {
		let mut new = Self::try_with_capacity(len)?;

		// SAFETY: We have reserved this capacity.
		let slots = unsafe { new.spare_capacity_mut().get_unchecked_mut(..len) };

		for (index, slot) in slots.iter_mut().enumerate() {
			let value = f(index);

			slot.write(value);
		}

		// SAFETY: All of these elements have been initial-
		// ised from return values of the provided func-
		// tion.
		unsafe { new.set_len(len) };

		Ok(new)
	}

	/// Constructs an N-vector from raw parts.
	///
	/// # Safety
	///
	/// The following prerequisites must be upheld when
	/// calling this constructor:
	///
	/// * `len` may not be greater than `N`.
	/// * The first `len`-count elements of `data` must
	///   represent valid instances of `T`.
	///
	/// Failure to upheld these invariants will yield
	/// undefined behaviour.
	#[inline(always)]
	#[must_use]
	pub const unsafe fn from_raw_parts(data: [MaybeUninit<T>; N], len: usize) -> Self {
		Self { len, data }
	}

	/// Gets a slice over the vector elements.
	#[inline]
	#[must_use]
	pub const fn as_slice(&self) -> &[T] {
		let len  = self.len();
		let data = self.as_ptr();

		// SAFETY:
		//
		// * The pointer, deriving from a reference, is
		//   guaranteed to be non-null, valid for both
		//   reads and writes, and aligned.
		//
		// * `Self::len` guarantees that all elements up to
		//   (exclusive) the returned index are valid in-
		//   stances of `T`.
		//
		// * The lifetime of this returned reference is in-
		//   herited from `self`.
		//
		// * The total size of the slice cannot be greater
		//   than `isize::MAX` as the containing buffer
		//   object is a Rust object itself.
		unsafe { slice::from_raw_parts(data, len) }
	}

	/// Gets a mutable slice over the vector elements.
	#[inline]
	#[must_use]
	pub const fn as_mut_slice(&mut self) -> &mut [T] {
		let len  = self.len();
		let data = self.as_mut_ptr();

		// SAFETY:
		//
		// * The pointer, deriving from a mutable refer-
		//   ence, is guaranteed to be non-null, valid for
		//   both reads and writes, and aligned.
		//
		// * `Self::len` guarantees that all elements up to
		//   (exclusive) the returned index are valid in-
		//   stances of `T`.
		//
		// * This returned slice reference is guaranteed to
		//   be exclusive, thanks to the provided `Self`
		//   mutable reference. The lifetime of the refer-
		//   ence is inherited from `self`.
		//
		// * The total size of the slice cannot be greater
		//   than `isize::MAX` as the containing buffer
		//   object is a Rust object itself.
		unsafe { slice::from_raw_parts_mut(data, len) }
	}

	/// Gets a constant pointer to the first element.
	#[inline]
	#[must_use]
	pub const fn as_ptr(&self) -> *const T {
		self.data.as_ptr().cast::<T>()
	}

	/// Gets a mutable pointer to the first element.
	#[inline]
	#[must_use]
	pub const fn as_mut_ptr(&mut self) -> *mut T {
		self.data.as_mut_ptr().cast::<T>()
	}

	/// Converts the N-vector into a standard vector.
	///
	/// The elements of the N-vector are moved into the
	/// new allocation without being dropped.
	#[cfg(feature = "alloc")]
	#[must_use]
	pub fn into_vec(self) -> Vec<T> {
		let len = self.len();

		// Remember to inhibit our destructor.
		let this = ManuallyDrop::new(self);

		let mut v = Vec::<T>::with_capacity(len);

		// SAFETY:
		//
		// * The source pointer, deriving from a reference,
		//   is guaranteed to be valid for reads. The length
		//   component is itself guaranteed to always be
		//   within bounds of this source buffer.
		//
		// * The destination pointer, coming from the vec-
		//   tor, is guaranteed to be valid for writes. The caller guarantees that capacity is
		//   enough to contain the source string.
		//
		// * Both pointers, the former deriving from a ref-
		//   erence and the latter coming from the vector,
		//   are guaranteed to be aligned.
		//
		// * The vector guarantees that the returned point-
		//   er is exclusive.
		//
		// Furthermore, we've made sure that destructors
		// are not run for the original vector elements.
		unsafe { v.as_mut_ptr().copy_from_nonoverlapping(this.as_ptr(), len) };

		// SAFETY: We have copied the active elements from
		// our buffer into the new vector.
		unsafe { v.set_len(len) };

		v
	}

	/// Converts the N-vector into a boxed slice.
	///
	/// The elements of the N-vector are moved into the
	/// new allocation without being dropped.
	#[cfg(feature = "alloc")]
	#[must_use]
	pub fn into_boxed_slice(self) -> Box<[T]> {
		self.into_vec().into()
	}

	/// Deconstructs the vector into its raw parts.
	#[inline]
	#[must_use]
	pub const fn into_raw_parts(self) -> ([MaybeUninit<T>; N], usize) {
		// NOTE: Due to `Self: Drop`, we can't move fields
		// out of the structure. We therefore have to
		// firstly inhibit the destructor and then manually
		// move the fields out into the returned tuple.

		// NOTE: The length is `Copy` and can be "moved"
		// out now; no need for the manual move.
		let len = self.len;

		// Inhibit our destructor.

		let this = MaybeUninit::new(self);

		// Manually move `data` out of object.
		let data = {
			// Pointer to start of object.
			let mut p = this.as_ptr().cast::<[MaybeUninit<T>; N]>();

			// Offset pointer to `data`.
			p = unsafe { p.byte_add(offset_of!(Self, data)) };

			// SAFETY: The following are guaranteed due to our
			// passing a reference:
			//
			// * The pointer is valid for reads.
			//
			// * The pointer is aligned.
			//
			// * The pointer is non-null.
			//
			// Further note that we guarantee the (`!Copy`) ob-
			// ject not being used again, partially by wrapping
			// the object in `MaybeUninit`.
			unsafe { p.read() }
		};

		// Return.

		(data, len)
	}
}

impl<T, const N: usize> AsMut<Self> for NVec<T, N> {
	#[inline(always)]
	fn as_mut(&mut self) -> &mut Self {
		self
	}
}

// SORT: slice
impl<T, const N: usize> AsMut<[T]> for NVec<T, N> {
	#[inline]
	fn as_mut(&mut self) -> &mut [T] {
		self
	}
}

impl<T, const N: usize> AsRef<Self> for NVec<T, N> {
	#[inline(always)]
	fn as_ref(&self) -> &Self {
		self
	}
}

// SORT: slice
impl<T, const N: usize> AsRef<[T]> for NVec<T, N> {
	#[inline]
	fn as_ref(&self) -> &[T] {
		self
	}
}

impl<T, const N: usize> From<[T; N]> for NVec<T, N> {
	#[inline]
	fn from(value: [T; N]) -> Self {
		Self::from_array(value)
	}
}

// SORT: slice
impl<T: Clone, const N: usize> TryFrom<&[T]> for NVec<T, N> {
	type Error = TryReserveError;

	#[inline]
	fn try_from(value: &[T]) -> Result<Self, Self::Error> {
		Self::try_from_slice(value)
	}
}

// SORT: slice
impl<T: Clone, const N: usize> TryFrom<&mut [T]> for NVec<T, N> {
	type Error = TryReserveError;

	#[inline]
	fn try_from(value: &mut [T]) -> Result<Self, Self::Error> {
		Self::try_from_slice(value)
	}
}

#[cfg(feature = "alloc")]
impl<T, const N: usize> From<NVec<T, N>> for Box<[T]> {
	#[inline]
	fn from(value: NVec<T, N>) -> Self {
		value.into_boxed_slice()
	}
}

#[cfg(feature = "alloc")]
impl<T, const N: usize> From<NVec<T, N>> for Vec<T> {
	#[inline]
	fn from(value: NVec<T, N>) -> Self {
		value.into_vec()
	}
}
