use std::{rc::Rc, sync::Arc};

use anm::LoadedFile;
use states::GameStateMachine;
use wgpu::RenderPass;

use crate::engine::{self, Engine, EngineState, UpdateContext};

mod anm;
mod snd;
pub mod states;

pub struct Game {
  sound_manager: snd::Manager,
  anm_manager: anm::Manager,
  state: GameStateMachine,
}

struct GameContext<'a> {
  engine: &'a UpdateContext<'a>,
  sound_manager: &'a mut snd::Manager,
  anm_manager: &'a mut anm::Manager,
}

impl GameContext<'_> {
  pub fn load_anm(&mut self, file_name: &str) -> Arc<LoadedFile> {
    self.anm_manager.load_anm(&self.engine.device, &self.engine.queue, &self.engine.window, file_name)
  }
}

impl EngineState for Game {
  fn new(context: &UpdateContext) -> Self
  where
    Self: Sized,
  {
    let mut sound_manager = snd::Manager::new();
    let mut anm_manager = anm::Manager::new(&context);

    let mut context = GameContext {
      anm_manager: &mut anm_manager,
      sound_manager: &mut sound_manager,
      engine: context,
    };

    let state = GameStateMachine::new(&mut context);

    let game = Self {
      sound_manager,
      anm_manager,
      state,
    };

    game
  }

  fn resize(&mut self, context: &UpdateContext, new_size: winit::dpi::PhysicalSize<u32>) {
    self.anm_manager.resize(context.device, new_size);
  }

  fn update(&mut self, _context: &UpdateContext) {
    self.state.update(&mut GameContext {
      engine: _context,
      sound_manager: &mut self.sound_manager,
      anm_manager: &mut self.anm_manager,
    });
    self.anm_manager.update();
  }

  fn render(&self, engine: &crate::engine::Engine, surface: &wgpu::Texture, encoder: &mut wgpu::CommandEncoder) {
    self.anm_manager.render(&engine, surface, encoder)
  }
}
