use crate::directionality::Directionality;
use bevy_asset::{Asset, Handle};
use bevy_ecs::bundle::Bundle;
use bevy_ecs::component::Component;
use bevy_reflect::{Reflect, TypePath};
use bevy_sprite::Sprite;
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
use std::time::Duration;

macro_rules! dr {
	($type: ident -> $output: ty) => {
		impl ::std::ops::Deref for $type {
			type Target = $output;
			fn deref(&self) -> &Self::Target {
				&self.0
			}
		}

		impl ::std::ops::DerefMut for $type {
			fn deref_mut(&mut self) -> &mut Self::Target {
				&mut self.0
			}
		}
	};
}

#[derive(Clone, PartialOrd, PartialEq, Debug, Default, Reflect)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AnimationFrames {
	pub frames: Vec<usize>,
	pub frame_secs: f32,
}

impl AnimationFrames {
	pub fn duration(&self) -> Duration {
		Duration::from_secs_f32(self.frames.len() as f32 * self.frame_secs)
	}
}

#[derive(Clone, Debug, TypePath, PartialEq, Default, Asset)]
#[cfg_attr(
	feature = "serde",
	derive(serde::Serialize, serde::Deserialize),
	serde(transparent)
)]
pub struct AnimationSet(pub HashMap<String, AnimationFrames>);
dr!(AnimationSet -> HashMap<String, AnimationFrames>);

#[derive(Clone, Debug, Component, PartialEq, Default)]
pub struct AnimationHandle(pub Handle<AnimationSet>);
dr!(AnimationHandle -> Handle<AnimationSet>);

#[derive(Copy, Clone, Debug, Component, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SyncToParent;

#[derive(Copy, Clone, Debug, Component, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AnimationPaused;

#[derive(Copy, Clone, Debug, Component, PartialEq, Eq, Default)]
#[cfg_attr(
	feature = "serde",
	derive(serde::Serialize, serde::Deserialize),
	serde(transparent)
)]
pub struct UserData(pub u128);

#[derive(Clone, Debug, Component, PartialEq, Eq, Default)]
#[cfg_attr(
	feature = "serde",
	derive(serde::Serialize, serde::Deserialize),
	serde(untagged, rename_all = "snake_case")
)]
pub enum AnimationMode {
	#[default]
	Loop,
	Once,
	IntroLoop {
		intro: String,
		r#loop: String,
	},
}

#[derive(Clone, Debug, Component, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AnimationStatus {
	pub active_name: String,
	pub active_step: usize,
	pub frame_time: f32,
}

impl Default for AnimationStatus {
	fn default() -> Self {
		Self {
			active_name: String::from("idle"),
			active_step: 0,
			frame_time: 0.0,
		}
	}
}

impl From<String> for AnimationStatus {
	fn from(value: String) -> Self {
		AnimationStatus {
			active_name: value,
			active_step: 0,
			frame_time: 0.0,
		}
	}
}

impl AnimationStatus {
	pub fn new(name: impl ToString) -> Self {
		AnimationStatus {
			active_name: name.to_string(),
			active_step: 0,
			frame_time: 0.0,
		}
	}
	pub fn set_animation(&mut self, name: impl ToString) {
		self.active_name = name.to_string();
	}
	pub fn start_animation(&mut self, name: impl ToString) {
		self.active_name = name.to_string();
		self.active_step = 0;
	}
	pub fn start_or_continue(&mut self, name: impl ToString) {
		let name = name.to_string();
		if self.active_name != name {
			self.active_name = name;
			self.active_step = 0;
		}
	}
}

#[derive(Component)]
#[require(
	AnimationHandle,
	AnimationStatus,
	AnimationMode,
	AnimationPlaybackRate,
	Sprite
)]
pub struct SpriteAnimation;
pub fn create_sprite_animation(
	handle: Handle<AnimationSet>,
	initial_anim: impl ToString,
) -> impl Bundle {
	(
		SpriteAnimation,
		AnimationHandle(handle),
		AnimationStatus::new(initial_anim),
	)
}

#[derive(Component)]
#[require(
	AnimationHandle,
	AnimationStatus,
	AnimationMode,
	AnimationPlaybackRate,
	Directionality,
	Sprite
)]
pub struct DirectionalAnimation;
pub fn create_directional_animation(
	handle: Handle<AnimationSet>,
	initial_anim: impl ToString,
) -> impl Bundle {
	(
		DirectionalAnimation,
		AnimationHandle(handle),
		AnimationStatus::new(initial_anim),
	)
}
#[derive(Component)]
#[require(Sprite, SyncToParent)]
pub struct ChildAnimation;
pub fn create_child_animation() -> impl Bundle {
	(ChildAnimation,)
}

#[derive(Clone, Debug, Component, PartialEq, PartialOrd, Default)]
#[cfg_attr(
	feature = "serde",
	derive(serde::Serialize, serde::Deserialize),
	serde(transparent)
)]
#[require(SimpleAnimationStatus)]
pub struct SimpleAnimation(pub AnimationFrames);
dr!(SimpleAnimation -> AnimationFrames);

impl From<AnimationFrames> for SimpleAnimation {
	fn from(value: AnimationFrames) -> Self {
		SimpleAnimation(value)
	}
}

#[derive(Copy, Clone, Debug, Component, PartialEq, PartialOrd, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SimpleAnimationStatus {
	pub active_step: usize,
	pub frame_time: f32,
}

impl SimpleAnimation {
	pub fn new(frames: Vec<usize>, frame_secs: f32) -> Self {
		AnimationFrames { frames, frame_secs }.into()
	}
}

#[derive(Clone, Debug, Component, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OverrideData(pub u128);
dr!(OverrideData -> u128);

#[derive(Clone, Debug, Component, PartialEq, Default)]
#[cfg_attr(
	feature = "serde",
	derive(serde::Serialize, serde::Deserialize),
	serde(transparent)
)]
#[require(OverrideData)]
pub struct AnimationOverride(pub AnimationStatus);
dr!(AnimationOverride -> AnimationStatus);

impl AnimationOverride {
	pub fn new(name: impl ToString) -> Self {
		AnimationOverride(name.to_string().into())
	}
}
impl From<AnimationStatus> for AnimationOverride {
	fn from(other: AnimationStatus) -> Self {
		Self(other)
	}
}
impl From<String> for AnimationOverride {
	fn from(other: String) -> Self {
		AnimationStatus::from(other).into()
	}
}

#[derive(Bundle, Default)]
pub struct AnimationOverrideBundle {
	pub anim: AnimationOverride,
	pub data: OverrideData,
}

impl AnimationOverrideBundle {
	pub fn new(anim: impl Into<AnimationOverride>) -> Self {
		Self {
			anim: anim.into(),
			data: OverrideData(0),
		}
	}
}

#[derive(Clone, Debug, Component, PartialEq)]
pub struct AnimationPlaybackRate(f32);
impl Default for AnimationPlaybackRate {
	fn default() -> Self {
		AnimationPlaybackRate(1.0)
	}
}
impl From<f32> for AnimationPlaybackRate {
	fn from(value: f32) -> Self {
		AnimationPlaybackRate(value)
	}
}
impl Deref for AnimationPlaybackRate {
	type Target = f32;

	fn deref(&self) -> &Self::Target {
		&self.0
	}
}
impl DerefMut for AnimationPlaybackRate {
	fn deref_mut(&mut self) -> &mut Self::Target {
		&mut self.0
	}
}
