#![allow(unused_variables, unused_mut)]

mod game;
mod input;
mod render;

use ash::Device;
use ash::Entry;
use ash::Instance;
use ash::vk;
use game::Camera;
use render::SwapchainCtx;
use render::loading_world::Component;
use render::loading_world::WorldComponent;
use render::skybox::Skybox;
use sdl2::{Sdl, video::Window as SdlWindow};

use std::collections::HashMap;

const APP_NAME: &str = "MineClone";

pub trait GameState {}

pub struct Unstarted;

impl GameState for Unstarted {}

/// The render context, this is what is used in most of the rendering,
/// contains everything we create in and need after [`RenderAvailable`]
pub struct RenderCtx {
    entry: Entry,
    instance: Instance,
    pdev: vk::PhysicalDevice,
    dev: Device,
    surface: vk::SurfaceKHR,
    queue: vk::Queue,
    queue_idx: u32,
    cmd_buf: vk::CommandBuffer,
    host_vis_idx: u32,
    host_invis_idx: u32,
}

/// Our window, holds the width and height, since we track those independent of Sdl and
/// the window Sdl gave us, since if we lose that, everything explodes
pub struct Window {
    width: u32,
    height: u32,
    _sdl: SdlWindow,
}

/// The render avalable state, this is the bare minimun state that is not
/// [`Unstarted`], from here we can transition into [`LoadingWorld`] or in
/// the future Menus and such
pub struct RenderAvailable {
    sdl_context: Sdl,
    window: Window,
    ctx: RenderCtx,
}

impl GameState for RenderAvailable {}

/// The state in which we can load a world, this means loading the actual
/// world data, as well as adding components to be rendered, or potentially
/// loading mods.
pub struct LoadingWorld {
    sdl_context: Sdl,
    window: Window,
    ctx: RenderCtx,

    requested_descriptors: HashMap<vk::DescriptorType, u32>,

    swapchain: SwapchainCtx,
    pass: vk::RenderPass,

    skybox: Skybox,
    components: Vec<Box<dyn WorldComponent>>,
}

impl GameState for LoadingWorld {}

/// We are now in a running world and can render this world and do
/// things like movenent or (in the future) physics
pub struct InWorld {
    sdl_context: Sdl,
    window: Window,
    ctx: RenderCtx,

    skybox: (Skybox, vk::DescriptorSet),
    components: Vec<Component>,

    sem_avail: vk::Semaphore,
    sem_finish: vk::Semaphore,
    fence_flight: vk::Fence,

    swapchain: SwapchainCtx,
    pass: vk::RenderPass,

    camera: Camera,
}

impl GameState for InWorld {}

pub struct Game<S: GameState> {
    state: S,
}

impl Game<Unstarted> {
    pub fn new() -> Self {
        Self { state: Unstarted }
    }
}

fn decode_rif(raw: &[u8]) -> &[u8] {
    &raw[17..]
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let mut window = video_subsystem
        .window(APP_NAME, 1024, 768)
        .vulkan()
        .resizable()
        .build()
        .unwrap();

    let game = Game::new().init_render(sdl_context, window);

    // Hardcoded loading of a world, skybox + 1 cube
    let mut game = game.start_load_world();
    let cube = game.create_cube();
    game.add_component(cube);
    let game = game.start_world();

    // Run the actual game
    let game = game.main_loop();
}
