use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::time::Instant;
use serde_derive::Deserialize;

use cgmath::{Euler, Quaternion, Deg};
use legion::world::SubWorld;
use legion::IntoQuery;
use legion::*;
use nalgebra::Quaternion as naQuaternion;
use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodySet};
use rapier3d::geometry::{BroadPhase, ColliderSet, NarrowPhase};
use rapier3d::pipeline::PhysicsPipeline;
use crate::camera::{Camera, CameraController};
use crate::components::{Collider, LoopState, Mesh, Physics, Position};
use crate::geometry::{load_obj, RawMesh};
use std::io::Read;



#[derive(Deserialize, Clone)]
pub struct TomlBallPhysicsBodyDescription {
    pub radius: String
}

#[derive(Deserialize, Clone)]
pub struct TomlCuboidPhysicsBodyDescription {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

#[derive(Deserialize, Clone)]
pub struct TomlPhysicsDescription {
    pub body_status: String,
    pub ball: Option<TomlBallPhysicsBodyDescription>,
    pub cuboid: Option<TomlCuboidPhysicsBodyDescription>,
}

#[derive(Deserialize, Clone)]
pub struct TomlRotationDescription {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

#[derive(Deserialize, Clone)]
pub struct TomlPositionDescription {
    pub x: f32,
    pub y: f32,
    pub z: f32,
    pub rot: Option<TomlRotationDescription>,
}

#[derive(Deserialize, Clone)]
pub struct TomlEntityDescription {
    pub name: String,
    #[serde(rename = "type")]
    pub type_name: String,
    pub mesh: Option<String>,
    pub position: Option<TomlPositionDescription>,
    pub physics: Option<TomlPhysicsDescription>,
}

#[derive(Deserialize, Clone)]
pub struct TomlEntityContainer {
    pub entities: Vec<TomlEntityDescription>,
}

pub struct RuntimeState {
    config_db: TomlEntityContainer,
    mesh_cache: HashMap<String, RawMesh>,
}

impl RuntimeState {
    pub fn new() -> RuntimeState {
        // TODO: Hook this file to the gui
        let mut file = fs::File::open("./conf/entity_spawns.toml").unwrap();
        let mut content = String::new();
        file.read_to_string(&mut content).unwrap();

        // TODO: gracefully fail
        let mut settings : TomlEntityContainer = toml::from_str(content.as_str()).unwrap();

        RuntimeState {
            config_db: settings,
            mesh_cache: Default::default(),
        }
    }

    pub fn get_mesh(&mut self, mesh: &str) -> Option<&RawMesh> {
        self.mesh_cache.get(mesh)
    }

    pub fn get_entities(&mut self) -> Vec<TomlEntityDescription> {
        self.config_db.entities.clone()
    }

    pub fn preload_meshes(&mut self, resources_path: PathBuf) {
        log::info!("Preloading meshes...");

        let paths = fs::read_dir(resources_path).unwrap();
        for file in paths {
            let file = file.unwrap();
            let filepath = file.path().clone();
            let filename = String::from(file.file_name().to_str().unwrap());

            if filename.ends_with(".obj") {
                let mesh = load_obj(filepath.to_str().unwrap()).unwrap();
                self.mesh_cache.insert(filename, mesh);
            }
        }

        panic!("nah");
    }
}
