use bevy::ecs::system::Resource;
use bevy::prelude::*;
use bevy_kira_audio::AudioSource;
use serde::{Deserialize, Serialize};

use crate::music_box::MusicBox;

pub trait SuppliesAudio: Resource {
	fn get_audio_track<T: ToString>(&self, name: T) -> Option<Handle<AudioSource>>;
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct AudioSettings {
	master_volume: f32,
	music_volume: f32,
	sfx_volume: f32,
	ui_volume: f32,
}

impl Default for AudioSettings {
	fn default() -> Self {
		Self {
			master_volume: 1.0,
			music_volume: 0.0,
			sfx_volume: 0.0,
			ui_volume: 0.0,
		}
	}
}

#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub enum CrossFadeTrack {
	#[default]
	A,
	B,
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct CrossFadeState {
	pub active: CrossFadeTrack,
	pub next: CrossFadeTrack,
	pub progress: f32,
}

impl Default for CrossFadeState {
	fn default() -> Self {
		Self {
			active: CrossFadeTrack::A,
			next: CrossFadeTrack::B,
			progress: 0.0,
		}
	}
}

#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct AudioCrossFade {
	pub music: CrossFadeState,
	pub ambiance: CrossFadeState,
}

pub enum TrackType {
	Single(Handle<AudioSource>),
	WithIntro(Handle<AudioSource>, Handle<AudioSource>),
	Missing,
}

impl<'w, 's, T: SuppliesAudio> MusicBox<'w, 's, T> {
	pub fn resolve_track_name<Name: ToString>(&'w self, name: Name) -> TrackType {
		let name = name.to_string();

		if let (Some(intro), Some(looped)) = (
			self.handles.get_audio_track(format!("{}_intro", &name)),
			self.handles.get_audio_track(format!("{}_loop", &name)),
		) {
			TrackType::WithIntro(intro, looped)
		} else if let Some(track) = self.handles.get_audio_track(name.clone()) {
			TrackType::Single(track)
		} else if let Some(track) = self.handles.get_audio_track(format!("{}_looped", name)) {
			TrackType::Single(track)
		} else {
			TrackType::Missing
		}
	}
}


impl SuppliesAudio for AssetServer {
	fn get_audio_track<T: ToString>(&self, name: T) -> Option<Handle<AudioSource>> {
		Some(self.load(&name.to_string()))
	}
}