// 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 [`FrameResolution`] type.

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

/// A one-dimensional video resolution.
#[repr(transparent)]
#[derive(Copy)]
#[derive_const(Clone)]
pub struct FrameResolution(pattern_type!(u16 is 1..=16384));

impl FrameResolution {
	/// Constructs a new resolution.
	///
	/// The provided value is measured in count of
	/// pixels.
	///
	/// If the provided value is zero or greater than
	/// `16384`, this constructor returns [`None`].
	#[inline]
	#[must_use]
	pub const fn new(value: u16) -> Option<Self> {
		if value >= 1 && value <= 16384 {
			let value = unsafe { Self::new_unchecked(value) };

			Some(value)
		} else {
			None
		}
	}

	/// Unsafely constructs a new resolution.
	///
	/// # Safety
	///
	/// The provided value must be non-zero and less
	/// than or equal to `16384`.
	#[inline(always)]
	#[must_use]
	#[track_caller]
	pub const unsafe fn new_unchecked(value: u16) -> Self {
		unsafe { transmute::<u16, Self>(value) }
	}

	/// Retrieves the resolution as [`u16`], meaasured in
	/// count of pixels.
	#[inline(always)]
	#[must_use]
	pub const fn as_u16(self) -> u16 {
		unsafe { transmute::<Self, u16>(self) }
	}

	/// Retrieves the resolution as [`usize`], meaasured
	/// in count of pixels.
	#[inline]
	#[must_use]
	pub const fn as_usize(self) -> usize {
		usize::from(self.as_u16())
	}
}

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

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

		Ok(())
	}
}

const impl Eq for FrameResolution {}

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

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

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