use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

mod codec;
pub use codec::*;

mod format;
pub use format::*;

mod sample_format;
pub use sample_format::*;

mod stream;
pub use stream::*;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum ContainerError {
    #[error("IoError: {0}")]
    Io(#[from] std::io::Error),

    #[error("Codec Error: {0}")]
    Codec(#[from] CodecError),

    #[error("Symphonia Error: {0}")]
    Symphonia(#[from] symphonia::core::errors::Error),

    #[error("File '{0}' has no tracks/streams")]
    NoStream(PathBuf),

    #[error("File '{0}' has an unsupported container")]
    InvalidSource(PathBuf),

    #[error("'{0:?}' is an unsupported container")]
    UnsupportedContainer(ContainerFormat),

    #[error("Couldn't find sample rate")]
    NoSampleRate,

    #[error("Couldn't find channel count'")]
    NoChannelCount,
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct MediaContainer {
    pub(crate) path: PathBuf,
    pub(crate) format: ContainerFormat,
    pub(crate) stream: Stream,
}

impl MediaContainer {
    pub(crate) fn set_path(&mut self, path: PathBuf) {
        self.path = path;
    }
}

impl MediaContainer {
    #[must_use]
    pub fn path(&self) -> &Path {
        &self.path
    }

    #[must_use]
    pub fn format(&self) -> &ContainerFormat {
        &self.format
    }

    /// Returns the file extension this [`MediaContainer`] uses. Returns [`Option::None`] for invalid or unrecognized pairs
    #[must_use]
    pub fn extension(&self) -> &'static str {
        let format = self.format;
        let codec = self.stream.codec_params.codec;
        match format {
            ContainerFormat::Flac if codec.is_flac() => "flac",
            ContainerFormat::Mp3 if codec.is_mp3() => "mp3",
            ContainerFormat::Ogg if codec.is_vorbis() => "ogg",
            ContainerFormat::Ogg if codec.is_opus() => "opus",
            ContainerFormat::Ogg if codec.is_flac() => "oga",
            ContainerFormat::Wav if codec.is_pcm() => "wav",
            ContainerFormat::Aiff if codec.is_pcm() => "aiff",
            ContainerFormat::Ape if codec.is_ape() => "ape",
            _ => panic!("Invalid format/codec pairing: {format:?} + {codec:?}"),
        }
    }

    #[must_use]
    pub fn stream(&self) -> &Stream {
        &self.stream
    }
}
