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

use super::{player::PlayerId, colour::ColourAlpha, changes::Changes};

use crate::utils::maths::{self, RectCorners};

#[cfg(feature = "client")] pub const WIDTH: f32 = 1.5;
pub const HEIGHT: f32 = 5.0 / 3.0;
#[cfg(feature = "client")] pub const X0: f32 = -5.0 / 72.0;
#[cfg(feature = "client")] pub const Y0: f32 = -HEIGHT / 2.0;

const POLE_WIDTH: f32 = 1.0 / 12.0;
const POLE_HEIGHT: f32 = HEIGHT;
pub const POLE_SIZE: Vec2 = Vec2::new(POLE_WIDTH, POLE_HEIGHT);

/**
 * This is added to the player position to get the flag position when the flag
 * follows a player (so the bottom of the pole is centred at the player).
 */
const FLAG_FOLLOWING_OFFSET: Vec2 = Vec2::new(0.0, POLE_HEIGHT / 2.0);

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Flag {
	state: FlagState,
	colour: ColourAlpha,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FlagState {
	Fixed(Vec2),
	Following(PlayerId, CaptureAnimation),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CaptureAnimation {
	init_dpos: Vec2,
	time: f32,
}

impl CaptureAnimation {
	pub fn new(init_dpos: Vec2) -> CaptureAnimation {
		CaptureAnimation { init_dpos: init_dpos - FLAG_FOLLOWING_OFFSET, time: 0.0 }
	}

	pub fn get_dpos(&self, time_shift: f32) -> Vec2 {
		let t = maths::smoothstep((1.0 - (self.time + time_shift) / ANIMATION_TIME).clamp(0.0, 1.0));
		self.init_dpos * t + FLAG_FOLLOWING_OFFSET
	}
}

const ANIMATION_TIME: f32 = 0.25;

impl Flag {
	pub fn new(pos: Vec2, colour: ColourAlpha) -> Flag {
		Flag { state: FlagState::Fixed(pos), colour }
	}

	pub fn get_pos(&self, get_player_pos: impl Fn(PlayerId) -> Option<Vec2>, time_shift: f32) -> Vec2 {
		match &self.state {
			FlagState::Fixed(pos) => *pos,
			FlagState::Following(id, animation) => get_player_pos(*id).unwrap_or(Vec2::NAN) + animation.get_dpos(time_shift),
		}
	}

	#[cfg(feature = "client")]
	pub fn get_colour(&self) -> ColourAlpha {
		self.colour
	}

	pub fn get_fixed_pos(&self) -> Option<Vec2> {
		if let FlagState::Fixed(pos) = self.state { Some(pos) }
		else { None }
	}

	pub fn get_following(&self) -> Option<PlayerId> {
		if let FlagState::Following(id, _) = self.state { Some(id) }
		else { None }
	}

	pub fn get_state(&self) -> &FlagState {
		&self.state
	}

	pub fn set_state(&mut self, state: FlagState, changes: &mut impl Changes, get_player_pos: impl Fn(PlayerId) -> Option<Vec2>) {
		match (&state, &self.state) {
			(FlagState::Following(..), FlagState::Fixed(old_pos)) => changes.flag_captured(*old_pos),
			(FlagState::Fixed(new_pos), FlagState::Following(id, animation)) => changes.flag_dropped(get_player_pos(*id).map_or(*new_pos, |pos| pos + animation.get_dpos(0.0))),
			_ => (),
		}

		self.state = state;
	}

	pub fn update(&mut self, dt: f32) {
		if let FlagState::Following(_, animation) = &mut self.state {
			animation.time += dt;
		}
	}

	pub fn rect_from_pos(pos: Vec2) -> RectCorners {
		RectCorners::new_unchecked(pos - POLE_SIZE / 2.0, pos + POLE_SIZE / 2.0)
	}
}
