// 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, Texture2d, Program, Frame, DrawParameters, index::{NoIndices, PrimitiveType}, uniforms::SamplerWrapFunction, uniform, implement_vertex};
use glutin::surface::WindowSurface;
use glam::Vec2;
use png::ColorType;
use egui::{Window, Frame as EguiFrame, Margin, RichText, Color32, Context, Layout, Align};

use super::health_bar::HUD_HEIGHT;

use crate::app::filesystem::{Filesystem, FsBase};
use crate::app::gui::style::FontSize;
use crate::utils::texture;

#[derive(Clone, Copy)]
struct Vertex { pos: [f32; 2], tex_coords: [f32; 2] }
implement_vertex!(Vertex, pos, tex_coords);

pub struct AmmoStatus {
	vbo: VertexBuffer<Vertex>,
	texture: Texture2d,
	program: Program,
	text: String,
}

const TEXTURE_SCALE: f32 = 2.0;

impl AmmoStatus {
	pub fn new(display: &Display<WindowSurface>, fs: &Filesystem) -> AmmoStatus {
		let vsh = fs::read_to_string(fs.get(FsBase::Static, Path::new("shaders/ammo_status.vsh"))).unwrap();
		let fsh = fs::read_to_string(fs.get(FsBase::Static, Path::new("shaders/ammo_status.fsh"))).unwrap();

		AmmoStatus {
			vbo: VertexBuffer::empty_persistent(display, 4).unwrap(),
			texture: texture::load(display, &fs.get(FsBase::Static, Path::new("textures/ammo_status.png")), ColorType::Rgba),
			program: Program::from_source(display, &vsh, &fsh, None).unwrap(),
			text: String::new(),
		}
	}

	pub fn render(&self, frame: &mut Frame, params: &DrawParameters, lwinsize: Vec2) {
		let size = self.get_size();
		let pos = Vec2::new((lwinsize.x - size.x) / 2.0, lwinsize.y - HUD_HEIGHT * 3.0); // Bottom-left corner
		let p0 = Vec2::new(2.0 * pos.x / lwinsize.x - 1.0, 1.0 - 2.0 * pos.y / lwinsize.y);
		let p1 = p0 + 2.0 * size / lwinsize;

		self.vbo.write(&[
			Vertex { pos: [p0.x, p0.y], tex_coords: [0.0, 1.0] },
			Vertex { pos: [p1.x, p0.y], tex_coords: [1.0, 1.0] },
			Vertex { pos: [p0.x, p1.y], tex_coords: [0.0, 0.0] },
			Vertex { pos: [p1.x, p1.y], tex_coords: [1.0, 0.0] },
		]);

		let uniforms = uniform! { u_texture: self.texture.sampled().wrap_function(SamplerWrapFunction::Clamp) };
		frame.draw(&self.vbo, NoIndices(PrimitiveType::TriangleStrip), &self.program, &uniforms, params).unwrap();
	}

	pub fn set_text(&mut self, text: String) {
		self.text = text;
	}

	pub fn render_text(&self, ctx: &Context, lwinsize: Vec2) {
		let mut size = self.get_size();
		let mut pos = Vec2::new((lwinsize.x - size.x) / 2.0, lwinsize.y - HUD_HEIGHT * 3.0 - size.y);

		/*
		 * egui doesn't seem to allow windows to be off-screen, so if the top of
		 * the egui window isn't visible, shrink the size to avoid the position
		 * text being off. This is only needed in extreme edge cases.
		 */
		if pos.y < 0.0 {
			size.y += pos.y;
			pos.y = 0.0;
		}

		Window::new("ammo-status")
			.frame(EguiFrame::none().inner_margin(Margin::same(-4.0))) // Negative margin feels hacky but it works
			.fixed_pos(pos.to_array())
			.resizable(false)
			.title_bar(false)
			.show(ctx, |ui| {
				// Annoyingly need to do this as Window::fixed_size doesn't actually set the size
				ui.set_width(size.x); ui.set_height(size.y);
				ui.with_layout(Layout::bottom_up(Align::Max), |ui| {
					ui.label(RichText::new(&self.text).color(Color32::WHITE).background_color(Color32::from_rgba_unmultiplied(0, 0, 0, 127)).font_size(11.0));
				});
			});
	}

	fn get_size(&self) -> Vec2 {
		Vec2::new(self.texture.width() as f32, self.texture.height() as f32) / TEXTURE_SCALE
	}
}
