use std::time::{Duration, Instant};

use cg_lab::painter::PaintService;
use cg_lab::shape::ShapeService;
use cg_lab::support::backend::Backend;
use cg_lab::support::image::Image;
use cg_lab::surface::PixelSurface;
use egui_glow::egui_winit::egui::{FontData, FontDefinitions, FontFamily, SidePanel, Slider};
use egui_glow::EguiGlow;
use glutin::dpi::PhysicalSize;
use glutin::event::{Event, WindowEvent};
use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::window::WindowBuilder;

pub struct AppController {
	backend: Backend,
	painter: PaintService,
	shapes: ShapeService,
	surface: PixelSurface,
	image: Image,
	gui: EguiGlow,
}

impl AppController {
	pub fn new(backend: Backend) -> Self {
		Self {
			gui: {
				const FONT_PATH: &[u8] = include_bytes!("../assets/Merriweather-Light.ttf");
				const FONT_NAME: &str = "Merriweather";

				let result = EguiGlow::new(backend.window(), backend.gl_ctx().clone());
				let mut fonts = FontDefinitions::default();
				fonts.font_data.insert(FONT_NAME.into(), FontData::from_static(FONT_PATH));
				fonts.families.get_mut(&FontFamily::Proportional).unwrap().insert(0, FONT_NAME.into());
				result.egui_ctx.set_fonts(fonts);
				result
			},
			painter: PaintService::default(),
			shapes: ShapeService::default(),
			surface: PixelSurface::new(backend.gl_ctx().clone()),
			image: Image::new(300, 300),
			backend,
		}
	}

	pub fn update(&mut self) {
		self.gui.run(self.backend.window(), |ctx| {
			SidePanel::left("Main UI").show(ctx, |ui| {
				ui.label("UI configurations");

				let mut ui_size = ctx.pixels_per_point();
				if ui
					.add(Slider::new(&mut ui_size, 0.1..=2.0).text("UI size").show_value(false))
					.drag_released()
				{
					ctx.set_pixels_per_point(ui_size);
				}

				let mut h = self.backend.window().inner_size().height;
				let size_slider = Slider::new(&mut h, 500..=1000).show_value(false).text("Window size");
				if ui.add(size_slider).changed() {
					let w = h * self.image.width() as u32 / self.image.height() as u32;
					self.backend.resize(PhysicalSize::new(w, h));
				}
				ui.separator();

				self.painter.show(ui);
				ui.separator();

				self.shapes.show(ui);
				ui.separator();
			});
		});

		self.shapes.paint(&mut self.image);
		self.painter.hint(&mut self.image);
		self.surface.render(&self.image);

		self.gui.paint(self.backend.window());

		self.backend.submit();
	}

	pub fn on_window_event(&mut self, event: &WindowEvent) {
		if self.gui.on_event(event) {
			return;
		}
		if let Some(shape) = self.painter.on_window_event(event) {
			self.shapes.push(shape);
		}
	}
}

fn main() {
	let event_loop = EventLoop::new();

	let window_builder = WindowBuilder::new()
		.with_inner_size(PhysicalSize::new(900, 900))
		.with_title("CG Lab: CPU Renderer (Opengl Backend)");
	let backend = unsafe { Backend::new(window_builder, &event_loop) };

	let mut app = AppController::new(backend);

	event_loop.run(move |event, _, ctrl| {
		*ctrl = ControlFlow::WaitUntil(Instant::now() + Duration::from_millis(16));
		match event {
			Event::MainEventsCleared => app.update(),
			Event::WindowEvent { event, .. } => {
				if let WindowEvent::CloseRequested = event {
					*ctrl = ControlFlow::Exit
				} else {
					app.on_window_event(&event);
				}
			}
			_ => {}
		}
	})
}
