// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
mod ammo_status;
mod scores;
mod timer;
mod level;
pub mod health_bar;
pub mod text_io;
pub mod world_label;

use glium::{Display, Frame, DrawParameters};
use glutin::surface::WindowSurface;
use winit::event::{KeyEvent, ElementState};
use glam::{UVec2, Vec2};
use egui::Context;

use ammo_status::AmmoStatus;
use text_io::TextIo;
use world_label::WorldLabel;

use super::{Camera, Model, HealthBarRenderer, TimeArgs, world::SmoothWorld};

use crate::app::{App, filesystem::Filesystem, config::action::ActionEvent};

pub struct Hud {
	ammo_status: AmmoStatus,
	health_bars: HealthBarRenderer,
	text_io: TextIo,
	winsize: Vec2,
}

impl Hud {
	pub fn new(display: &Display<WindowSurface>, fs: &Filesystem, winsize: UVec2) -> Hud {
		Hud {
			ammo_status: AmmoStatus::new(display, fs),
			health_bars: HealthBarRenderer::new(display, fs),
			text_io: TextIo::new(),
			winsize: winsize.as_vec2(),
		}
	}

	pub fn reset(&mut self, winsize: UVec2) {
		self.resize_event(winsize);
		self.text_io = TextIo::new();
	}

	pub fn get_text_io(&mut self) -> &mut TextIo { &mut self.text_io }
	pub fn text_input_open(&self) -> bool { self.text_io.open() }

	pub fn action(&mut self, action: ActionEvent) {
		self.text_io.action(action);
	}

	pub fn key_event(&mut self, event: &KeyEvent) {
		if !self.text_io.open() { return; }

		if let KeyEvent { text: Some(text), state: ElementState::Pressed, .. } = &event {
			self.text_io.push_current(text);
		}
	}

	pub fn resize_event(&mut self, winsize: UVec2) { self.winsize = winsize.as_vec2(); }

	pub fn push_health_bars_and_labels(&mut self, smooth_world: &SmoothWorld, camera: &Camera, time_diff: f32, scale_factor: f32, labels: &mut Vec<WorldLabel>) {
		let lwinsize = self.get_lwinsize(scale_factor);
		smooth_world.push_health_bars_and_labels(&mut self.health_bars, lwinsize, camera, time_diff, labels);
	}

	pub fn render_glium(&mut self, app: &App, frame: &mut Frame, params: &mut DrawParameters, smooth_world: &SmoothWorld, scale_factor: f32) {
		let lwinsize = self.get_lwinsize(scale_factor);

		self.health_bars.render(&app.display, frame, params);

		if smooth_world.followed_player_id().is_some() {
			self.ammo_status.render(frame, params, lwinsize);
		}
	}

	pub fn render_egui(&mut self, ctx: &Context, time: &TimeArgs, model: &mut Model, smooth_world: &SmoothWorld, scale_factor: f32) {
		let lwinsize = self.get_lwinsize(scale_factor);

		if let Some(level) = model.get_client().get_level() {
			level::render(ctx, level);
		} else {
			scores::render(ctx, model.get_client().get_scores(), smooth_world.followed_player_id(), smooth_world.followed_team(), model.get_world());
		}

		if let Some(time) = model.get_client_mut().get_time(smooth_world.get_total_time_shift() + time.time_diff) {
			timer::render(ctx, time);
		}
		let player_count = model.get_world().human_player_count();
		self.text_io.render(time.dt, ctx, lwinsize, player_count, model.get_client());

		if let Some(count) = smooth_world.followed_player().map(|(_, player)| player.get_ammo_string()) {
			self.ammo_status.set_text(count);
			self.ammo_status.render_text(ctx, lwinsize);
		}
	}

	fn get_lwinsize(&self, scale_factor: f32) -> Vec2 {
		self.winsize / scale_factor
	}
}
