// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use std::{fs, path::Path};

use glium::{Surface, VertexBuffer, Display, Program, Frame, DrawParameters, index::{NoIndices, PrimitiveType}, uniforms::EmptyUniforms, implement_vertex};
use glutin::surface::WindowSurface;

use crate::app::filesystem::{Filesystem, FsBase};
use crate::utils::glium_resize;

#[derive(Clone, Copy)]
struct Vertex {
	v_pos: [f32; 2],
	v_alpha: f32,
	v_use_t: f32,
}
implement_vertex!(Vertex, v_pos, v_alpha, v_use_t);

#[derive(Clone, Copy)]
pub struct Instance {
	pub(super) i_pos: [f32; 2],
	pub(super) i_size: [f32; 2],
	pub(super) i_t: f32,
	pub(super) i_colour: [f32; 3],
}
implement_vertex!(Instance, i_pos, i_size, i_t, i_colour);

pub struct Renderer {
	v_vbo: VertexBuffer<Vertex>,
	i_vbo: VertexBuffer<Instance>,
	instance_buf: Vec<Instance>,
	program: Program,
}

const ALPHA_ACTIVE: f32 = 0.875; // The part representing the health the player has
const ALPHA_LOST: f32 = 0.25; // The part representing the health the player lost

impl Renderer {
	pub fn new(display: &Display<WindowSurface>, fs: &Filesystem) -> Renderer {
		static VERTICES: [Vertex; 12] = [
			// Active part
			Vertex { v_pos: [0.0, 0.0], v_use_t: 0.0, v_alpha: ALPHA_ACTIVE },
			Vertex { v_pos: [0.0, 0.0], v_use_t: 1.0, v_alpha: ALPHA_ACTIVE },
			Vertex { v_pos: [0.0, 1.0], v_use_t: 1.0, v_alpha: ALPHA_ACTIVE },

			Vertex { v_pos: [0.0, 0.0], v_use_t: 0.0, v_alpha: ALPHA_ACTIVE },
			Vertex { v_pos: [0.0, 1.0], v_use_t: 0.0, v_alpha: ALPHA_ACTIVE },
			Vertex { v_pos: [0.0, 1.0], v_use_t: 1.0, v_alpha: ALPHA_ACTIVE },

			// Lost part
			Vertex { v_pos: [0.0, 0.0], v_use_t: 1.0, v_alpha: ALPHA_LOST },
			Vertex { v_pos: [1.0, 0.0], v_use_t: 0.0, v_alpha: ALPHA_LOST },
			Vertex { v_pos: [1.0, 1.0], v_use_t: 0.0, v_alpha: ALPHA_LOST },

			Vertex { v_pos: [0.0, 0.0], v_use_t: 1.0, v_alpha: ALPHA_LOST },
			Vertex { v_pos: [0.0, 1.0], v_use_t: 1.0, v_alpha: ALPHA_LOST },
			Vertex { v_pos: [1.0, 1.0], v_use_t: 0.0, v_alpha: ALPHA_LOST },
		];

		let vsh = fs::read_to_string(fs.get(FsBase::Static, Path::new("shaders/health_bar.vsh"))).unwrap();
		let fsh = fs::read_to_string(fs.get(FsBase::Static, Path::new("shaders/health_bar.fsh"))).unwrap();

		Renderer {
			v_vbo: VertexBuffer::immutable(display, &VERTICES).unwrap(),
			i_vbo: VertexBuffer::empty_persistent(display, 1).unwrap(),
			instance_buf: Vec::with_capacity(1),
			program: Program::from_source(display, &vsh, &fsh, None).unwrap(),
		}
	}

	pub fn push(&mut self, inst: Instance) {
		self.instance_buf.push(inst);
	}

	pub fn render(&mut self, display: &Display<WindowSurface>, frame: &mut Frame, params: &DrawParameters) {
		let count = self.instance_buf.len();
		glium_resize::vbo_persistent(&mut self.i_vbo, display, count);

		{
			let mut buf = self.i_vbo.map_write();
			for (i, inst) in self.instance_buf.drain(..).enumerate() {
				buf.set(i, inst);
			}
		}

		frame.draw((&self.v_vbo, self.i_vbo.slice(..count).unwrap().per_instance().unwrap()), NoIndices(PrimitiveType::TrianglesList), &self.program, &EmptyUniforms, params).unwrap();
	}
}
