// 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;

use assets::Polygon;

use super::Vertex as GliumVertex;
use super::super::particle::temperature::Temperature;

use crate::app::playing::model::changes::Hit;
use crate::world::{colour::Colour, player::PlayerTexture};
use crate::utils::GliumWriter;

struct Vertex {
	pos: Vec2,
	tex_coords: Vec2,
}

impl Vertex {
	fn new(vertex: Vec2, centre: Vec2, dir: Vec2) -> Vertex {
		Vertex {
			pos: (vertex - centre).rotate(dir),
			tex_coords: Vec2::new(vertex.x + 0.5, vertex.y * 2.0 + 1.0),
		}
	}

	fn to_glium(&self, frag: &Fragment, rot_dir: Vec2) -> GliumVertex {
		let t = frag.time / frag.max_time;

		GliumVertex {
			v_pos: (frag.pos + self.pos.rotate(rot_dir)).into(),
			v_tex_coords: self.tex_coords.into(),
			v_layer: frag.texture.get_layer(),
			v_colour: frag.colour.into(),
			v_alpha: t * (2.0 - t),
			v_temp: frag.temp.get_colour_rgb().into(),
		}
	}
}

pub struct Fragment {
	vertices: Vec<Vertex>,
	size: f32,
	pos: Vec2,
	vel: Vec2,
	rot: f32,
	rot_vel: f32,
	time: f32,
	max_time: f32,
	colour: Colour,
	texture: PlayerTexture,
	temp: Temperature,
}

impl Fragment {
	pub fn new(poly: &Polygon, hit: &Hit) -> Fragment {
		let player_dir = Vec2::from(hit.player_dir);
		let rot_pos = poly.pos.rotate(player_dir);
		let max_time = rand::random::<f32>() * 0.25 + 0.25;
		let rotations = (rand::random::<f32>() - 0.5) * rand::random::<f32>() * 2.5; // Number of rotations the fragment will complete (negative for opposite direction)
		let rot_vel = rotations * TAU / max_time;

		Fragment {
			vertices: poly.vertices.iter().map(|&vertex| Vertex::new(vertex, poly.pos, player_dir)).collect(),
			size: poly.size,
			pos: hit.player_pos + rot_pos,
			vel: hit.player_vel + rot_pos * (rand::random::<f32>() * 2.0 + 4.0),
			rot: 0.0, // Important to have the rotation start at zero so things smoothly transition from a player to a bunch of fragments
			rot_vel,
			time: max_time,
			max_time,
			colour: hit.player_colour,
			texture: hit.player_texture,
			temp: Temperature::new(rand::random::<f32>() * 1.5 + 2.5),
		}
	}

	pub fn update(&mut self, dt: f32) -> bool {
		self.pos += self.vel * dt;
		self.rot += self.rot_vel * dt;
		self.time -= dt;
		self.temp.cool(0.5 / self.size, dt);
		self.time >= 0.0
	}

	pub fn vertex_and_index_count(&self) -> (usize, usize) {
		(self.vertices.len(), (self.vertices.len() - 2) /* Number of triangles */ * 3 /* Number of indices */)
	}

	pub fn push_vertices(&self, vertices: &mut GliumWriter<GliumVertex>, indices: &mut GliumWriter<u32>) {
		let i = vertices.get_index() as u32;
		let rot_dir = Vec2::from_angle(self.rot);
		for vertex in &self.vertices { vertices.push(vertex.to_glium(self, rot_dir)); }

		for j in i + 1..i + self.vertices.len() as u32 - 1 {
			indices.push(i); indices.push(j); indices.push(j + 1);
		}
	}
}
