// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use serde::Deserialize;
use glam::Vec2;

use crate::world::{effects::Effect, team::TeamId, colour::ColourAlpha};
use crate::utils::maths::RectCorners;

#[derive(Default, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecialAreaConfig {
	#[serde(default)] pub areas: Vec<SpecialAreaRect>,
	#[serde(default)] pub portals: Box<[Portal]>,
	#[serde(default)] pub portal_default_colours: PortalDefaultColourConfig,
	#[serde(default)] pub effect_zones: Box<[EffectZone]>,
	#[serde(default)] pub effect_zone_default_colours: EffectZoneDefaultColourConfig,
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpecialAreaRect {
	pub pos: Vec2,
	pub size: Vec2,
	pub colour: ColourAlpha,
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Portal {
	pub rect: RectCorners,
	#[serde(default)] pub out: Box<[PortalLink]>,
	pub colour: Option<ColourAlpha>,
	pub team_only: Option<TeamId>,
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PortalLink {
	pub index: usize,
	#[serde(default)] pub rotation: Rotation,
	#[serde(default)] pub flip_x: bool, // Flipping is done after rotation
	#[serde(default)] pub flip_y: bool,
	#[serde(default = "vec2_one")] pub vel_scl: Vec2,
	#[serde(default)] pub vel_off: Vec2,
}

#[derive(Clone, Copy, Default, PartialEq, Eq, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum Rotation { // Anti-clockwise
	#[default] D0,
	D90,
	D180,
	D270,
}

impl Rotation {
	pub fn rotate(self, v: Vec2) -> Vec2 {
		match self {
			Rotation::D0 => v,
			Rotation::D90 => Vec2::new(-v.y, v.x),
			Rotation::D180 => -v,
			Rotation::D270 => Vec2::new(v.y, -v.x),
		}
	}
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PortalDefaultColourConfig {
	#[serde(default = "PortalDefaultColourConfig::default_source")] pub source: ColourAlpha,
	#[serde(default = "PortalDefaultColourConfig::default_sink")] pub sink: ColourAlpha,
	#[serde(default = "PortalDefaultColourConfig::default_bidirectional")] pub bidirectional: ColourAlpha,
}

impl PortalDefaultColourConfig {
	fn default_source() -> ColourAlpha { [255, 127, 0, 95].into() }
	fn default_sink() -> ColourAlpha { [0, 127, 255, 95].into() }
	fn default_bidirectional() -> ColourAlpha { [127, 0, 255, 95].into() }
}

impl Default for PortalDefaultColourConfig {
	fn default() -> PortalDefaultColourConfig {
		PortalDefaultColourConfig {
			source: Self::default_source(),
			sink: Self::default_sink(),
			bidirectional: Self::default_bidirectional(),
		}
	}
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EffectZone {
	pub rect: RectCorners,
	pub effect: Effect,
	pub time: f32,

	pub power: Option<f32>, // Falls back to the effect default
	pub colour: Option<ColourAlpha>,
}

#[derive(Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EffectZoneDefaultColourConfig {
	#[serde(default = "EffectZoneDefaultColourConfig::default_speed")] pub speed: ColourAlpha,
	#[serde(default = "EffectZoneDefaultColourConfig::default_reload")] pub reload: ColourAlpha,
	#[serde(default = "EffectZoneDefaultColourConfig::default_regen")] pub regen: ColourAlpha,
	#[serde(default = "EffectZoneDefaultColourConfig::default_damage")] pub damage: ColourAlpha,
	#[serde(default = "EffectZoneDefaultColourConfig::default_forcefield")] pub forcefield: ColourAlpha,
}

impl EffectZoneDefaultColourConfig {
	fn default_speed() -> ColourAlpha { [47, 127, 31, 127].into() }
	fn default_reload() -> ColourAlpha { [127, 95, 23, 127].into() }
	fn default_regen() -> ColourAlpha { [127, 63, 111, 127].into() }
	fn default_damage() -> ColourAlpha { [127, 47, 35, 127].into() }
	fn default_forcefield() -> ColourAlpha { [31, 95, 127, 127].into() }
}

impl Default for EffectZoneDefaultColourConfig {
	fn default() -> EffectZoneDefaultColourConfig {
		EffectZoneDefaultColourConfig {
			speed: Self::default_speed(),
			reload: Self::default_reload(),
			regen: Self::default_regen(),
			damage: Self::default_damage(),
			forcefield: Self::default_forcefield(),
		}
	}
}

fn vec2_one() -> Vec2 { Vec2::ONE }
