// Copyright 2026 Gabriel Bjørnager Jensen.
//
// This file is part of LOOKLOOK.
//
// LOOKLOOK is free software: you can redistribute
// it and/or modify it under the terms of the GNU
// Affero General Public License as published by
// the Free Software Foundation, either version 3
// of the License, or (at your option) any later
// version.
//
// LOOKLOOK is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Af-
// fero General Public License along with LOOKLOOK.
// If not, see <https://www.gnu.org/licenses/>.

//! The [`SampleRate`] type.

use std::cmp::Ordering;
use std::fmt::{self, Debug, Display, Formatter};
use std::hint::assert_unchecked;
use std::mem::transmute;

/// An audio sampling rate.
#[repr(transparent)]
#[derive(Copy)]
#[derive_const(Clone)]
pub struct SampleRate(u32);

impl SampleRate {
	/// Constructs a new sampling rate.
	///
	/// The provided value is measured in hertz.
	///
	/// If the provided value is zero or greater than
	/// `8589934592`, this constructor returns [`None`].
	#[inline]
	#[must_use]
	pub const fn new(value: u64) -> Option<Self> {
		if value >= 1 && value <= 2_u64.pow(u32::BITS) {
			let value = unsafe { Self::new_unchecked(value) };

			Some(value)
		} else {
			None
		}
	}

	/// Unsafely constructs a new sampling rate.
	///
	/// # Safety
	///
	/// The provided value must be non-zero and less
	/// than or equal to `8589934592`.
	#[inline(always)]
	#[must_use]
	#[track_caller]
	pub const unsafe fn new_unchecked(mut value: u64) -> Self {
		unsafe { assert_unchecked(value <= 2_u64.pow(u32::BITS)) };

		// SAFETY: The only case where this would underflow
		// is where `value == 0`, which the caller specifi-
		// cally guarantees is not the case.
		value = unsafe { value.unchecked_sub(1) };

		// NOTE: Optimiser should see `value <= u32::MAX`.
		let value = value.truncate();

		unsafe { transmute::<u32, Self>(value) }
	}

	/// Retrieves the sampling rate, meaasured in hertz.
	#[inline]
	#[must_use]
	pub const fn get(self) -> u64 {
		u64::from(self.0) + 1
	}
}

impl Debug for SampleRate {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		Debug::fmt(&self.get(), f)
	}
}

const impl Default for SampleRate {
	#[inline(always)]
	fn default() -> Self {
		const { Self::new(44100).unwrap() }
	}
}

impl Display for SampleRate {
	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
		Display::fmt(&self.get(), f)?;
		f.write_str("Hz")?;

		Ok(())
	}
}

const impl Eq for SampleRate {}

const impl Ord for SampleRate {
	#[inline]
	fn cmp(&self, other: &Self) -> Ordering {
		self.get().cmp(&other.get())
	}
}

const impl PartialEq for SampleRate {
	#[inline]
	fn eq(&self, other: &Self) -> bool {
		self.get().eq(&other.get())
	}
}

const impl PartialOrd for SampleRate {
	#[inline]
	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
		self.get().partial_cmp(&other.get())
	}
}
