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

use glium::{VertexBuffer, Program, Display, texture::Texture2dArray, implement_vertex};
use glutin::surface::WindowSurface;
use png::ColorType;
use strum::EnumCount;

use super::filesystem::{Filesystem, FsBase};

use crate::world::player::PlayerTexture;
use crate::utils::texture::Image;

pub struct PlayerGfx {
	pub v_vbo: VertexBuffer<Vertex>,
	pub textures: Texture2dArray,
	pub program: Program,
}

impl PlayerGfx {
	fn new(display: &Display<WindowSurface>, fs: &Filesystem) -> PlayerGfx {
		static VERTICES: [Vertex; 12] = [
			// Top side
			Vertex { v_pos: [-0.5, 0.5], v_tex_coords: [0.0, 0.0] },
			Vertex { v_pos: [0.25, 0.375], v_tex_coords: [0.75, 0.25] },
			Vertex { v_pos: [0.5, 0.0], v_tex_coords: [1.0, 1.0] },

			Vertex { v_pos: [-0.5, 0.5], v_tex_coords: [0.0, 0.0] },
			Vertex { v_pos: [-0.25, 0.0], v_tex_coords: [0.25, 1.0] },
			Vertex { v_pos: [0.5, 0.0], v_tex_coords: [1.0, 1.0] },

			// Bottom side
			Vertex { v_pos: [-0.5, -0.5], v_tex_coords: [0.0, 0.0] },
			Vertex { v_pos: [0.25, -0.375], v_tex_coords: [0.75, 0.25] },
			Vertex { v_pos: [0.5, 0.0], v_tex_coords: [1.0, 1.0] },

			Vertex { v_pos: [-0.5, -0.5], v_tex_coords: [0.0, 0.0] },
			Vertex { v_pos: [-0.25, 0.0], v_tex_coords: [0.25, 1.0] },
			Vertex { v_pos: [0.5, 0.0], v_tex_coords: [1.0, 1.0] },
		];

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

		let image = Image::try_new(&fs.get(FsBase::Static, Path::new("textures/players.png")), ColorType::GrayscaleAlpha).unwrap();

		PlayerGfx {
			v_vbo: VertexBuffer::immutable(display, &VERTICES).unwrap(),
			textures: image.into_texture_array(display, PlayerTexture::COUNT as u32),
			program: Program::from_source(display, &vsh, &fsh, None).unwrap(),
		}
	}
}

#[derive(Default)]
pub struct PlayerGfxCache(Option<Rc<PlayerGfx>>);

impl PlayerGfxCache {
	pub fn get(&mut self, display: &Display<WindowSurface>, fs: &Filesystem) -> Rc<PlayerGfx> {
		Rc::clone(self.0.get_or_insert_with(|| Rc::new(PlayerGfx::new(display, fs))))
	}
}

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

#[derive(Clone, Copy)]
pub struct Instance { pub i_pos: [f32; 2], pub i_dir: [f32; 2], pub i_colour: [f32; 3], pub i_layer: u32, pub i_brightness: f32, pub i_alpha: f32 }
implement_vertex!(Instance, i_pos, i_dir, i_colour, i_layer, i_brightness, i_alpha);
