pub mod engine;
pub mod game;
pub mod interp;
pub mod utils {
  pub mod context;
}

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

use engine::Engine;
use wgpu::SurfaceError;
use winit::{
  application::ApplicationHandler, dpi::PhysicalSize, event::{ElementState, KeyEvent, StartCause, WindowEvent}, event_loop::EventLoop, keyboard::{KeyCode, PhysicalKey}, window::Window
};

#[derive(Default)]
struct App<'a> {
  window: Option<Arc<Window>>,
  state: Option<Engine<'a>>,
}

impl<'a> ApplicationHandler for App<'a> {
  fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
    let window = event_loop
      .create_window(
        Window::default_attributes()
          .with_inner_size(PhysicalSize {
            width: 640,
            height: 480,
          })
          .with_title("touhou"),
      )
      .expect("failed to create window :(");
    window.set_visible(true);
    window.request_redraw();
    self.window = Some(Arc::new(window));
    self.state = Some(Engine::new(self.window.clone().unwrap()));
  }

  fn exiting(&mut self, _: &winit::event_loop::ActiveEventLoop) {
    std::process::exit(0);
  }

  fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
    // self.window.as_ref().unwrap().request_redraw();
  }

  fn new_events(&mut self, event_loop: &winit::event_loop::ActiveEventLoop, cause: winit::event::StartCause) {
    if let StartCause::ResumeTimeReached { .. } = cause {
      self.window.as_ref().unwrap().request_redraw();
    }
  }

  fn window_event(
    &mut self,
    event_loop: &winit::event_loop::ActiveEventLoop,
    _window_id: winit::window::WindowId,
    event: WindowEvent,
  ) {
    match event {
      WindowEvent::CloseRequested
      | WindowEvent::KeyboardInput {
        event:
          KeyEvent {
            state: ElementState::Pressed,
            physical_key: PhysicalKey::Code(KeyCode::Escape),
            ..
          },
        ..
      } => event_loop.exit(),
      WindowEvent::Resized(new_size) => {
        self.state.as_mut().unwrap().resize(new_size);
      }
      WindowEvent::RedrawRequested => {
        self.state.as_mut().unwrap().update();
        match self.state.as_ref().unwrap().render() {
          Ok(_) => {}
          Err(SurfaceError::Lost) => {
            self.state.as_mut().unwrap().resize(self.window.as_ref().unwrap().inner_size());
          }
          Err(SurfaceError::OutOfMemory) => {
            log::error!("ran out of memory");
            event_loop.exit();
          }
          Err(error) => {
            log::error!("render error: {error:?}")
          }
        };

        event_loop.set_control_flow(winit::event_loop::ControlFlow::wait_duration(Duration::from_millis(
          ((1.0 / 60.0) * 1000.0) as u64,
        )));
      }
      _ => {
        self.state.as_mut().unwrap().handle_event(&event);
      }
    }
  }
}

fn main() {
  env_logger::init();

  let event_loop = EventLoop::new().unwrap();
  event_loop.run_app(&mut App::default()).unwrap();
}
