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

use super::Particle;

use crate::app::playing::model::changes::Hit as ChangesHit;
use crate::world::player::MIN_DIST_TO_EDGE;
use crate::utils::maths::{Circle, glam_fix::Fix};

pub struct Hit {
	pos: Vec2,
	vel: Vec2,
	time: f32,
	size: f32,
	colour: Vec3,
}

const TIME: f32 = 0.375;

/*
 * Used to avoid calculating the particle position and reflected bullet velocity
 * multiple times when generating particles from a single hit.
 */
pub struct HitArgs {
	refl_vel: Vec2,
	pos: Vec2,
}

impl HitArgs {
	pub fn new(hit: &ChangesHit) -> HitArgs {
		// First calculates the reflected velocity
		// Velocity of the bullet from the player's frame of reference
		let vel = hit.bullet.get_vel() - hit.player_vel;

		// Not normalised, this is fine
		let normal = hit.bullet.get_pos() - hit.player_pos;
		let normal_len2 = normal.length_squared();
		if normal_len2 == 0.0 { // If the normal length is zero for whatever reason, just negate the velocity and have the particle's position be the player's position
			return HitArgs { refl_vel: -vel.normalise_or_zero(), pos: hit.player_pos };
		}

		let proj = (vel.dot(normal) / normal_len2) * normal;
		let refl_vel = (vel - 2.0 * proj).normalise_or_zero();

		// Then calculates the particle's position
		let dist = normal_len2.sqrt();
		let pos = hit.player_pos + (normal / dist) * MIN_DIST_TO_EDGE; // Makes the particle spawn at the minimum distance to the player's edge to avoid it appearing to spawn outside the player's image (but in its hitbox)
		HitArgs { refl_vel, pos }
	}
}

impl Hit {
	pub fn new(hit: &ChangesHit, args: &HitArgs) -> Hit {
		let angle = (rand::random::<f32>() - 0.5) * rand::random::<f32>() * 2.0;
		let colour = Vec3::from(hit.player_colour);
		let grey = colour.element_sum() / 3.0;
		let saturation = rand::random::<f32>() * 0.5 + 0.5;

		Hit {
			pos: args.pos,
			vel: args.refl_vel.rotate(Vec2::from_angle(angle)) * (rand::random::<f32>() * 4.0 + 2.0) + hit.player_vel, // Readds the player's position as it was subtracted when calculating refl_vel
			time: TIME,
			size: rand::random::<f32>() * 0.0625 + 0.0625,
			colour: Vec3::lerp(Vec3::splat(grey), colour, saturation),
		}
	}
}

impl Particle for Hit {
	fn get_pos(&self) -> Vec2 { self.pos }
	fn get_size(&self) -> f32 { self.size }
	fn get_colour(&self) -> Vec4 { self.colour.extend(self.time / TIME * 0.5) }

	fn update(&mut self, dt: f32) -> bool {
		self.pos += self.vel * dt;
		self.time -= dt;
		self.time >= 0.0
	}
}
