use std::{error::Error, io::{BufReader, BufWriter}};
use std::fs::File;

use serde_derive::{Deserialize, Serialize};

use crate::vulkan::{MeshHandle, VulkanRenderer, gameobject::GameObjectHandle};
use crate::game::TestGame;

#[derive(Debug, Serialize, Deserialize)]
struct MeshJson {
    path: String
}

#[derive(Debug, Serialize, Deserialize)]
struct ObjectJson {
    mesh_index: Option<usize>,
    position: [f32; 3],
    rotation: [f32; 4],
    scale: [f32; 3]
}

#[derive(Debug, Serialize, Deserialize)]
struct LevelJson {
    meshes: Vec<MeshJson>,
    objects: Vec<ObjectJson>,
    player: ObjectJson
}

pub fn load_level(path: &str, game: &mut TestGame, renderer: &mut VulkanRenderer) -> Result<Vec<GameObjectHandle>, Box<dyn Error>> {
    let file = File::open(path)?;
    let reader = BufReader::new(file);
    let level_json: LevelJson = serde_json::from_reader(reader)?;
    let meshes: Vec<MeshHandle> = level_json.meshes.iter().map(|json_mesh| {
        let mesh_handles = game.load_gltf(renderer, &json_mesh.path);
        // TODO: Add empty parent GO instead of just loading the first mesh
        mesh_handles[0].clone()
    }).collect();
    let objects: Vec<GameObjectHandle> = level_json.objects.iter().filter_map(|json_obj| {
        // TODO: Parenting
        if let Some(mesh_index) = json_obj.mesh_index {
            let handle = game.add_game_object(renderer, meshes[mesh_index].clone());
            let game_object = &mut game.game_objects[handle];
            game_object.position = json_obj.position.into();
            game_object.rotation = json_obj.rotation.into();
            game_object.scale = json_obj.scale.into();
            Some(handle)
        } else {
            None
        }
    }).collect();
    game.player.camera.position = level_json.player.position.into();
    game.player.camera.rotation = level_json.player.rotation.into();
    Ok(objects)
}

pub fn save_level(path: &str, game: &mut TestGame, renderer: &mut VulkanRenderer) -> Result<(), Box<dyn Error>> {
    let meshes = renderer.game_data.meshes.iter().filter(|m| m.original_path.is_some()).map(|mesh| {
        MeshJson {
            path: mesh.original_path.clone().unwrap()
        }
    }).collect();

    let objects = game.game_objects.iter().enumerate().filter_map(|(i, game_object)| {
        if game_object.pipeline_index != 0 { return None; }
        Some(ObjectJson {
            mesh_index: Some(i),
            position: game_object.position.into(),
            rotation: game_object.rotation.into(),
            scale: game_object.scale.into()
        })
    }).collect();

    let player = ObjectJson {
        mesh_index: None,
        position: game.player.camera.position.into(),
        rotation: game.player.camera.rotation.into(),
        scale: [1., 1., 1.]
    };
    
    let level_json = LevelJson {
        meshes,
        objects,
        player
    };

    let file = File::create(path)?;
    let writer = BufWriter::new(file);
    serde_json::to_writer_pretty(writer, &level_json)?;

    Ok(())
}