// 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 [`Mode`] enumeration.

use crate::mode::{ModeAudio, ModeRaw, ModeVideo};

/// A synth mode.
#[derive(Debug)]
#[derive_const(Clone)]
pub enum Mode {
	/// The `raw` mode.
	///
	/// Output from this mode is in the form of image
	/// sequence, e.g. [PNG] or [WebP].
	///
	/// [PNG]: <https://en.wikipedia.org/wiki/PNG>
	/// [WebP]: <https://en.wikipedia.org/wwiki/WebP>
	///
	/// Note that raw output uses the `.zlk` file exten-
	/// sion.
	Raw(ModeRaw),

	/// The `audio` mode.
	///
	/// Output from this mode is in the form of image
	/// sequence, e.g. [WAVE].
	///
	/// [WAVE]: <https://en.wikipedia.org/wiki/WAV>
	Audio(ModeAudio),

	/// The `video` mode.
	///
	/// Output from this mode is in the form of image
	/// sequence, e.g. [PNG] or [WebP].
	///
	/// [PNG]: <https://en.wikipedia.org/wiki/PNG>
	/// [WebP]: <https://en.wikipedia.org/wwiki/WebP>
	Video(ModeVideo),
}

impl Mode {
	/// Computes the size of a (raw) render with the
	/// specific mode.
	///
	/// The returned value is in count of 32-bit words.
	///
	/// # Panics
	///
	/// This method will panic if the result is greater
	/// than [`usize::MAX`].
	#[inline]
	#[must_use]
	pub const fn render_size(&self) -> usize {
		match *self {
			Self::Audio(ref audio) => {
				let depth = audio.sample_depth.as_usize();

				let sample_count: usize = audio.sample_count.try_into().ok().unwrap();

				depth.strict_mul(sample_count)
			},

			Self::Raw(ref raw) => {
				raw.length.try_into().ok().unwrap()
			},

			Self::Video(ref video) => {
				let frame_count: usize = video.frame_count.try_into().ok().unwrap();

				video.pixel_layout.channel_count()
					.strict_mul(video.width.as_usize())
					.strict_mul(video.height.as_usize())
					.strict_mul(frame_count)
			},
		}
	}

	/// Tests if the mode is [`Raw`].
	///
	/// [`Raw`]: Self::Raw
	#[inline(always)]
	#[must_use]
	pub const fn is_raw(&self) -> bool {
		matches!(*self, Self::Raw(..))
	}

	/// Tests if the mode is [`Audio`].
	///
	/// [`Audio`]: Self::Audio
	#[inline(always)]
	#[must_use]
	pub const fn is_audio(&self) -> bool {
		matches!(*self, Self::Audio(..))
	}

	/// Tests if the mode is [`Video`].
	///
	/// [`Video`]: Self::Video
	#[inline(always)]
	#[must_use]
	pub const fn is_video(&self) -> bool {
		matches!(*self, Self::Video(..))
	}

	/// Retrieves an identifier for the mode.
	#[inline]
	#[must_use]
	pub const fn as_str(&self) -> &'static str {
		match *self {
			Self::Audio(..) => "audio",
			Self::Raw(..)   => "raw",
			Self::Video(..) => "video",
		}
	}

	/// Retrieves an identifier for the output format.
	#[inline]
	#[must_use]
	pub const fn format_as_str(&self) -> &'static str {
		match *self {
			Self::Audio(ref audio) => {
				audio.format.as_str()
			}

			Self::Raw(..) => {
				"raw"
			}

			Self::Video(ref video) => {
				video.format.as_str()
			}
		}
	}
}

const impl From<ModeAudio> for Mode {
	#[inline]
	fn from(value: ModeAudio) -> Self {
		Self::Audio(value)
	}
}

const impl From<ModeRaw> for Mode {
	#[inline]
	fn from(value: ModeRaw) -> Self {
		Self::Raw(value)
	}
}

const impl From<ModeVideo> for Mode {
	#[inline]
	fn from(value: ModeVideo) -> Self {
		Self::Video(value)
	}
}
