// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::f32::consts::TAU;

use glam::{Vec2, Vec3, Vec4};

use super::Particle;

use crate::world::checkpoint::RADIUS;

pub struct Checkpoint {
	pos: Vec2,
	colour: Vec3,
	size: f32,
	r: f32,
	r_vel: f32,
	theta: f32,
	theta_vel: f32,
	time: f32,
}

const FADE_IN_TIME: f32 = 0.25;
const MAX_TIME: f32 = 2.0;
const R_START: f32 = 0.6875;
const R_RANGE: f32 = 0.25;
const THETA_RANGE: f32 = R_RANGE / (R_START + R_RANGE / 2.0);

impl Checkpoint {
	pub fn new(pos: Vec2, theta: f32, hue: f32) -> Checkpoint {
		let index = (rand::random::<u32>() % 3) as f32;
		Checkpoint {
			pos,
			colour: get_colour((hue + index * 2.0) % 6.0).lerp(Vec3::ONE, rand::random::<f32>() * 0.375 + 0.25),
			size: (rand::random::<f32>() * 2.0 + 1.0) / 60.0,
			r: (rand::random::<f32>() * R_RANGE + R_START) * RADIUS,
			r_vel: (rand::random::<f32>() - 0.5) * (rand::random::<f32>() - 0.5) * (rand::random::<f32>() - 0.5) * 4.0,
			theta: theta + (rand::random::<f32>() - 0.5) * THETA_RANGE + index * TAU / 3.0,
			theta_vel: rand::random::<f32>() - 2.0,
			time: 0.0,
		}
	}
}

impl Particle for Checkpoint {
	fn get_pos(&self) -> Vec2 {
		let (sin, cos) = self.theta.sin_cos();
		self.pos + self.r * Vec2::new(cos, sin)
	}

	fn get_size(&self) -> f32 { self.size }

	fn get_colour(&self) -> Vec4 {
		let alpha = (self.time / FADE_IN_TIME).min(1.0) * (1.0 - self.time / MAX_TIME);
		self.colour.extend(alpha)
	}

	fn update(&mut self, dt: f32) -> bool {
		self.r += self.r_vel * dt;
		self.theta += self.theta_vel * dt;
		self.time += dt;
		self.time <= MAX_TIME
	}
}

fn get_colour(hue: f32) -> Vec3 {
	match hue {
		0.0..1.0 => Vec3::new(1.0, hue, 0.0),
		1.0..2.0 => Vec3::new(2.0 - hue, 1.0, 0.0),
		2.0..3.0 => Vec3::new(0.0, 1.0, hue - 2.0),
		3.0..4.0 => Vec3::new(0.0, 4.0 - hue, 1.0),
		4.0..5.0 => Vec3::new(hue - 4.0, 0.0, 1.0),
		_ => Vec3::new(1.0, 0.0, 6.0 - hue),
	}
}
