use derive_more::Display;
use serde::Deserialize;

/// Different coarse definitions of how a ball is hit -- likely up to scorers interpretation.
#[derive(Debug, Deserialize, PartialEq, Eq, Copy, Clone, Display, Hash)]
#[serde(try_from = "__HitTrajectoryStruct")]
pub enum HitTrajectory {
	#[display("Bunt - Ground Ball")]
	BuntGrounder,

	#[display("Bunt - Popup")]
	BuntPopup,

	#[display("Bunt - Line Drive")]
	BuntLineDrive,

	#[display("Line Drive")]
	LineDrive,

	#[display("Ground Ball")]
	GroundBall,

	#[display("Fly Ball")]
	FlyBall,

	#[display("Popup")]
	Popup,
}

impl HitTrajectory {
	/// Uses launch angle data to derive a hit trajectory.
	#[must_use]
	pub const fn from_launch_angle(launch_angle: f64) -> Self {
		match launch_angle {
			..10.0 => Self::GroundBall,
			10.0..25.0 => Self::LineDrive,
			25.0..50.0 => Self::FlyBall,
			_ => Self::Popup,
		}
	}
}

#[derive(Deserialize)]
#[doc(hidden)]
#[serde(untagged)]
enum __HitTrajectoryStruct {
	Wrapped { code: String },
	Inline(String),
}

impl TryFrom<__HitTrajectoryStruct> for HitTrajectory {
	type Error = &'static str;

	fn try_from((__HitTrajectoryStruct::Wrapped { code } | __HitTrajectoryStruct::Inline(code)): __HitTrajectoryStruct) -> Result<Self, Self::Error> {
		Ok(match &*code {
			"bunt_grounder" => Self::BuntGrounder,
			"bunt_popup" => Self::BuntPopup,
			"bunt_line_drive" => Self::BuntLineDrive,
			"line_drive" => Self::LineDrive,
			"ground_ball" => Self::GroundBall,
			"fly_ball" => Self::FlyBall,
			"popup" => Self::Popup,
			_ => return Err("unknown hit trajectory"),
		})
	}
}

meta_kind_impl!("hitTrajectories" => HitTrajectory);
static_request_entry_cache_impl!(HitTrajectory);
test_impl!(HitTrajectory);
