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

//! The [`IntoIter`] iterator type.

use core::iter::FusedIterator;
use core::mem::MaybeUninit;
use core::ptr::drop_in_place;
use core::slice;
use nvec::n_vec::NVec;

/// An owning iterator over a [N-vector].
///
/// [N-vector]: NVec
#[must_use]
pub struct IntoIter<T, const N: usize> {
	/// The current position in the vector.
	position: usize,

	/// The count of remaining elements.
	remaining: usize,

	/// The raw vector data.
	data: [MaybeUninit<T>; N],
}

impl<T, const N: usize> IntoIter<T, N> {
	/// Constructs a new, owning
	#[inline]
	pub(super) fn new(v: NVec<T, N>) -> Self {
		let (data, remaining) = v.into_raw_parts();

		Self { position: 0, remaining, data }
	}

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

		let data = {
			let base = self.data.as_ptr().cast::<T>();

			// SAFETY: We always guarantee that `position` is
			// within bounds.
			unsafe { base.add(self.position) }
		};

		// SAFETY:
		//
		// * The pointer, deriving from a reference, is
		//   guaranteed to be non-null, valid for both
		//   reads and writes, and aligned.
		//
		// * `Self::remaining` guarantees that all elements
		//   up to (exclusive) it are valid instances 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 remaining
	/// elements.
	#[inline]
	#[must_use]
	pub fn as_mut_slice(&mut self) -> &mut [T] {
		let len = self.remaining;

		let data = {
			let base = self.data.as_mut_ptr().cast::<T>();

			// SAFETY: We always guarantee that `position` is
			// within bounds.
			unsafe { base.add(self.position) }
		};

		// SAFETY:
		//
		// * The pointer, deriving from a mutable refer-
		//   ence, is guaranteed to be non-null, valid for
		//   both reads and writes, and aligned.
		//
		// * `Self::remaining` guarantees that all elements
		//   up to (exclusive) it are valid instances 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) }
	}
}

impl<T, const N: usize> Default for IntoIter<T, N> {
	#[inline]
	fn default() -> Self {
		Self::new(Default::default())
	}
}

impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, N> {
	fn next_back(&mut self) -> Option<Self::Item> {
		let slot: *const T = self.as_slice().last()?;

		// 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`.
		let value = unsafe { slot.read() };

		self.remaining -= 1;

		Some(value)
	}
}

impl<T, const N: usize> Drop for IntoIter<T, N> {
	fn drop(&mut self) {
		let to_drop = self.as_mut_slice();

		// SAFETY: The following are guaranteed due to our
		// passing a mutable reference:
		//
		// * The pointer is valid for both reads and
		//   writes.
		//
		// * The pointer is aligned.
		//
		// * The pointer is non-null.
		//
		// * The destination is a valid instance (slice
		//   hereof) of `T`.
		//
		// * The pointer is exclusive for the duration of
		//   the `drop_in_place` call.
		//
		// The mutable slice does not exist after this
		// call, and we guarantee that the elements are not
		// used afterwards in no other context.
		unsafe { drop_in_place(to_drop) };
	}
}

impl<T, const N: usize> FusedIterator for IntoIter<T, N> {}

impl<T, const N: usize> ExactSizeIterator for IntoIter<T, N> {}

impl<T, const N: usize> Iterator for IntoIter<T, N> {
	type Item = T;

	fn next(&mut self) -> Option<Self::Item> {
		let slot: *const T = self.as_slice().first()?;

		// 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`.
		let value = unsafe { slot.read() };

		self.position  += 1;
		self.remaining -= 1;

		Some(value)
	}

	#[inline]
	fn size_hint(&self) -> (usize, Option<usize>) {
		let size = self.remaining;
		(size, Some(size))
	}
}
