use std::io::Read;
use std::collections::HashMap;
use crate::roblox_mesh;
use crate::texture::{RenderConfigs,Texture};
use strafesnet_common::model::{MeshId,RenderConfig,RenderConfigId,TextureId};
use crate::rbxassetid::RobloxAssetId;

#[derive(Default)]
pub struct RenderConfigLoader{
	texture_count:u32,
	render_configs:Vec<RenderConfig>,
	render_config_id_from_asset_id:HashMap<Option<RobloxAssetId>,RenderConfigId>,
}

impl RenderConfigLoader{
	pub fn acquire_render_config_id(&mut self,name:Option<&str>)->RenderConfigId{
		let render_id=RenderConfigId::new(self.render_config_id_from_asset_id.len() as u32);
		let index=name.and_then(|name|{
			match name.parse::<RobloxAssetId>(){
				Ok(asset_id)=>Some(asset_id),
				Err(e)=>{
					println!("Failed to parse AssetId: {e}");
					None
				},
			}
		});
		*self.render_config_id_from_asset_id.entry(index).or_insert_with(||{
			//create the render config.
			let render_config=if name.is_some(){
				let render_config=RenderConfig::texture(TextureId::new(self.texture_count));
				self.texture_count+=1;
				render_config
			}else{
				RenderConfig::default()
			};
			self.render_configs.push(render_config);
			render_id
		})
	}
}

#[derive(Default)]
pub struct MeshLoader{
	mesh_id_from_asset_id:HashMap<Option<RobloxAssetId>,MeshId>,
}

impl MeshLoader{
	pub fn acquire_mesh_id(&mut self,name:&str)->MeshId{
		let mesh_id=MeshId::new(self.mesh_id_from_asset_id.len() as u32);
		let index=match name.parse::<RobloxAssetId>(){
			Ok(asset_id)=>Some(asset_id),
			Err(e)=>{
				println!("Failed to parse AssetId: {e}");
				None
			},
		};
		*self.mesh_id_from_asset_id.entry(index).or_insert(mesh_id)
	}
	pub fn load_meshes(&mut self)->Result<roblox_mesh::Meshes,std::io::Error>{
		let mut mesh_data=vec![None;self.mesh_id_from_asset_id.len()];
		for (asset_id_option,mesh_id) in &self.mesh_id_from_asset_id{
			if let Some(asset_id)=asset_id_option{
				if let Ok(mut file)=std::fs::File::open(format!("meshes/{}",asset_id.0)){
					//TODO: parallel
					let mut data=Vec::<u8>::new();
					file.read_to_end(&mut data)?;
					mesh_data[mesh_id.get() as usize]=Some(roblox_mesh::RobloxMeshData::new(data));
				}else{
					println!("[roblox_legacy] no mesh name={}",asset_id.0);
				}
			}
		}
		Ok(roblox_mesh::Meshes::new(mesh_data))
	}
}

pub struct Loader{
	render_config_loader:RenderConfigLoader,
	mesh_loader:MeshLoader,
}
impl Loader{
	pub fn new()->Self{
		Self{
			render_config_loader:RenderConfigLoader::default(),
			mesh_loader:MeshLoader::default(),
		}
	}
	pub fn get_inner_mut(&mut self)->(&mut RenderConfigLoader,&mut MeshLoader){
		(&mut self.render_config_loader,&mut self.mesh_loader)
	}
	pub fn into_render_configs(mut self)->Result<RenderConfigs,std::io::Error>{
		let mut sorted_textures=vec![None;self.render_config_loader.texture_count as usize];
		for (asset_id_option,render_config_id) in self.render_config_loader.render_config_id_from_asset_id{
			let render_config=self.render_config_loader.render_configs.get_mut(render_config_id.get() as usize).unwrap();
			if let (Some(asset_id),Some(texture_id))=(asset_id_option,render_config.texture){
				if let Ok(mut file)=std::fs::File::open(format!("textures/{}.dds",asset_id.0)){
					//TODO: parallel
					let mut data=Vec::<u8>::new();
					file.read_to_end(&mut data)?;
					sorted_textures[texture_id.get() as usize]=Some(Texture::ImageDDS(data));
				}else{
					//texture failed to load
					render_config.texture=None;
				}
			}
		}
		Ok(RenderConfigs::new(
			sorted_textures,
			self.render_config_loader.render_configs,
		))
	}
}