Documentation
use crate::FrameId;
use clap::ArgGroup;
use clap::Args;
use clap::Parser;
use clap::Subcommand;
use clap::ValueEnum;
use clap_complete::Shell;
use std::path::PathBuf;
use tokio::fs::read_to_string;
#[derive(Clone, Debug, PartialEq, Subcommand)]
pub enum Command {
	Complete {
		#[clap(flatten)]
		options: CompleteOptions,
		#[clap(value_enum)]
		shell: Shell,
	},
	Get {
		#[clap(flatten)]
		frame: Frame,
		#[clap(required = true)]
		input: PathBuf,
		#[clap(flatten)]
		options: GetOptions,
	},
	Print {
		#[clap(flatten)]
		frames: Frames,
		#[clap(required = true)]
		inputs: Vec<PathBuf>,
		#[clap(flatten)]
		options: PrintOptions,
	},
	Remove {
		#[clap(flatten)]
		frames: Frames,
		#[clap(required = true)]
		inputs: Vec<PathBuf>,
	},
	Set {
		#[clap(flatten)]
		frame: Frame,
		#[clap(flatten)]
		frame_content: FrameContent,
		#[clap(required = true)]
		inputs: Vec<PathBuf>,
	},
}
#[derive(Clone, Debug, PartialEq, ValueEnum)]
enum FrameAlias {
	Album,
	Artist,
	Date,
	Disc,
	Genre,
	Picture,
	Subtitle,
	Title,
	Track,
}
#[derive(Clone, Debug, PartialEq, ValueEnum)]
enum FrameCompilation {
	All,
	Basic,
	NotBasic,
}
#[derive(Clone, Debug, Parser, PartialEq)]
pub struct Arguments {
	#[clap(subcommand)]
	pub command: Command,
}
#[derive(Args, Clone, Debug, PartialEq)]
pub struct CompleteOptions {
	#[clap(long, require_equals = true, short, value_name = "VALUE")]
	pub output: Option<PathBuf>,
}
#[derive(Args, Clone, Debug, PartialEq)]
#[clap(group = ArgGroup::new("FrameAG").required(true))]
pub struct Frame {
	#[clap(
		group = "FrameAG",
		long,
		require_equals = true,
		short,
		value_enum,
		value_name = "VALUE"
	)]
	alias: Option<FrameAlias>,
	#[clap(
		group = "FrameAG",
		long,
		require_equals = true,
		short,
		value_enum,
		value_name = "VALUE"
	)]
	id: Option<FrameId>,
}
#[derive(Args, Clone, Debug, PartialEq)]
#[clap(group = ArgGroup::new("FrameContentAG").required(true))]
pub struct FrameContent {
	#[clap(
		group = "FrameContentAG",
		long,
		require_equals = true,
		short,
		value_enum,
		value_name = "VALUE"
	)]
	path: Option<PathBuf>,
	#[clap(
		group = "FrameContentAG",
		long,
		require_equals = true,
		short,
		value_enum,
		value_name = "VALUE"
	)]
	text: Option<String>,
}
#[derive(Args, Clone, Debug, PartialEq)]
#[clap(group = ArgGroup::new("FramesAG").multiple(true).required(true))]
pub struct Frames {
	#[clap(
		group = "FramesAG",
		long,
		require_equals = true,
		short,
		value_delimiter = ',',
		value_enum,
		value_name = "VALUES"
	)]
	aliases: Option<Vec<FrameAlias>>,
	#[clap(
		group = "FramesAG",
		long,
		require_equals = true,
		short,
		value_enum,
		value_name = "VALUE"
	)]
	compilation: Option<FrameCompilation>,
	#[clap(
		group = "FramesAG",
		long,
		require_equals = true,
		short,
		value_delimiter = ',',
		value_enum,
		value_name = "VALUES"
	)]
	ids: Option<Vec<FrameId>>,
}
#[derive(Args, Clone, Debug, PartialEq)]
pub struct GetOptions {
	#[clap(long, require_equals = true, short, value_name = "VALUE")]
	pub output: Option<PathBuf>,
}
#[derive(Args, Clone, Debug, PartialEq)]
pub struct PrintOptions {
	#[clap(long, short)]
	pub existing: bool,
}
impl Frame {
	pub fn into_frame_id(self) -> FrameId {
		if let Some(i) = self.id {
			return i;
		}
		self.alias.unwrap().as_frame_id()
	}
}
impl FrameAlias {
	fn as_frame_id(&self) -> FrameId {
		match self {
			Self::Album => FrameId::Talb,
			Self::Artist => FrameId::Tpe1,
			Self::Date => FrameId::Tdrc,
			Self::Disc => FrameId::Tpos,
			Self::Genre => FrameId::Tcon,
			Self::Picture => FrameId::Apic,
			Self::Subtitle => FrameId::Tit3,
			Self::Title => FrameId::Tit2,
			Self::Track => FrameId::Trck,
		}
	}
}
impl FrameCompilation {
	fn as_frame_id_slice(&self) -> &[FrameId] {
		match self {
			Self::All => &[
				FrameId::Aenc,
				FrameId::Apic,
				FrameId::Aspi,
				FrameId::Comm,
				FrameId::Comr,
				FrameId::Encr,
				FrameId::Equ2,
				FrameId::Etco,
				FrameId::Geob,
				FrameId::Grid,
				FrameId::Link,
				FrameId::Mcdi,
				FrameId::Mllt,
				FrameId::Owne,
				FrameId::Priv,
				FrameId::Pcnt,
				FrameId::Popm,
				FrameId::Poss,
				FrameId::Rbuf,
				FrameId::Rva2,
				FrameId::Rvrb,
				FrameId::Seek,
				FrameId::Sign,
				FrameId::Sylt,
				FrameId::Sytc,
				FrameId::Talb,
				FrameId::Tbpm,
				FrameId::Tcom,
				FrameId::Tcon,
				FrameId::Tcop,
				FrameId::Tden,
				FrameId::Tdly,
				FrameId::Tdor,
				FrameId::Tdrc,
				FrameId::Tdrl,
				FrameId::Tdtg,
				FrameId::Tenc,
				FrameId::Text,
				FrameId::Tflt,
				FrameId::Tipl,
				FrameId::Tit1,
				FrameId::Tit2,
				FrameId::Tit3,
				FrameId::Tkey,
				FrameId::Tlan,
				FrameId::Tlen,
				FrameId::Tmcl,
				FrameId::Tmed,
				FrameId::Tmoo,
				FrameId::Toal,
				FrameId::Tofn,
				FrameId::Toly,
				FrameId::Tope,
				FrameId::Town,
				FrameId::Tpe1,
				FrameId::Tpe2,
				FrameId::Tpe3,
				FrameId::Tpe4,
				FrameId::Tpos,
				FrameId::Tpro,
				FrameId::Tpub,
				FrameId::Trck,
				FrameId::Trsn,
				FrameId::Trso,
				FrameId::Tsoa,
				FrameId::Tsop,
				FrameId::Tsot,
				FrameId::Tsrc,
				FrameId::Tsse,
				FrameId::Tsst,
				FrameId::Txxx,
				FrameId::Ufid,
				FrameId::User,
				FrameId::Uslt,
				FrameId::Wcom,
				FrameId::Wcop,
				FrameId::Woaf,
				FrameId::Woar,
				FrameId::Woas,
				FrameId::Wors,
				FrameId::Wpay,
				FrameId::Wpub,
				FrameId::Wxxx,
			],
			Self::Basic => &[
				FrameId::Apic,
				FrameId::Talb,
				FrameId::Tcon,
				FrameId::Tdrc,
				FrameId::Tit2,
				FrameId::Tit3,
				FrameId::Tpe1,
				FrameId::Tpos,
				FrameId::Trck,
			],
			Self::NotBasic => &[
				FrameId::Aenc,
				FrameId::Aspi,
				FrameId::Comm,
				FrameId::Comr,
				FrameId::Encr,
				FrameId::Equ2,
				FrameId::Etco,
				FrameId::Geob,
				FrameId::Grid,
				FrameId::Link,
				FrameId::Mcdi,
				FrameId::Mllt,
				FrameId::Owne,
				FrameId::Priv,
				FrameId::Pcnt,
				FrameId::Popm,
				FrameId::Poss,
				FrameId::Rbuf,
				FrameId::Rva2,
				FrameId::Rvrb,
				FrameId::Seek,
				FrameId::Sign,
				FrameId::Sylt,
				FrameId::Sytc,
				FrameId::Tbpm,
				FrameId::Tcom,
				FrameId::Tcop,
				FrameId::Tden,
				FrameId::Tdly,
				FrameId::Tdor,
				FrameId::Tdrl,
				FrameId::Tdtg,
				FrameId::Tenc,
				FrameId::Text,
				FrameId::Tflt,
				FrameId::Tipl,
				FrameId::Tit1,
				FrameId::Tkey,
				FrameId::Tlan,
				FrameId::Tlen,
				FrameId::Tmcl,
				FrameId::Tmed,
				FrameId::Tmoo,
				FrameId::Toal,
				FrameId::Tofn,
				FrameId::Toly,
				FrameId::Tope,
				FrameId::Town,
				FrameId::Tpe2,
				FrameId::Tpe3,
				FrameId::Tpe4,
				FrameId::Tpro,
				FrameId::Tpub,
				FrameId::Trsn,
				FrameId::Trso,
				FrameId::Tsoa,
				FrameId::Tsop,
				FrameId::Tsot,
				FrameId::Tsrc,
				FrameId::Tsse,
				FrameId::Tsst,
				FrameId::Txxx,
				FrameId::Ufid,
				FrameId::User,
				FrameId::Uslt,
				FrameId::Wcom,
				FrameId::Wcop,
				FrameId::Woaf,
				FrameId::Woar,
				FrameId::Woas,
				FrameId::Wors,
				FrameId::Wpay,
				FrameId::Wpub,
				FrameId::Wxxx,
			],
		}
	}
}
impl FrameContent {
	pub async fn try_into_string(self) -> Result<String, String> {
		if let Some(path) = self.path {
			return Ok(read_to_string(path).await.map_err(|e| e.to_string())?);
		}
		Ok(self.text.unwrap())
	}
}
impl Frames {
	pub fn into_frame_id_vec(self) -> Vec<FrameId> {
		let mut ids = Vec::new();
		if let Some(i) = self.ids {
			ids.extend(i);
		}
		if let Some(aliases) = self.aliases {
			ids.extend(
				aliases
					.iter()
					.map(|a| a.as_frame_id())
					.filter(|i| !ids.contains(i))
					.collect::<Vec<_>>(),
			);
		}
		if let Some(compilation) = self.compilation {
			ids.extend(
				compilation
					.as_frame_id_slice()
					.iter()
					.map(|i| i.to_owned())
					.filter(|i| !ids.contains(i))
					.collect::<Vec<_>>(),
			);
		}
		ids
	}
}