use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader};

mod bsp;
mod mesh;
mod brush;
pub mod loader;

const VALVE_SCALE:f32=1.0/16.0;
pub(crate) fn valve_transform_dist(d:f32)->strafesnet_common::integer::Planar64{
	(d*VALVE_SCALE).try_into().unwrap()
}
pub(crate) fn valve_transform_normal([x,y,z]:[f32;3])->strafesnet_common::integer::Planar64Vec3{
	strafesnet_common::integer::vec3::try_from_f32_array([x,z,-y]).unwrap()
}
pub(crate) fn valve_transform([x,y,z]:[f32;3])->strafesnet_common::integer::Planar64Vec3{
	strafesnet_common::integer::vec3::try_from_f32_array([x*VALVE_SCALE,z*VALVE_SCALE,-y*VALVE_SCALE]).unwrap()
}

#[derive(Debug)]
pub enum ReadError{
	Bsp(vbsp::BspError),
	Io(std::io::Error),
}
impl std::fmt::Display for ReadError{
	fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
		write!(f,"{self:?}")
	}
}
impl std::error::Error for ReadError{}


#[derive(Debug)]
pub enum LoadError{
	Texture(loader::TextureError),
	Mesh(loader::MeshError),
}
impl std::fmt::Display for LoadError{
	fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
		write!(f,"{self:?}")
	}
}
impl std::error::Error for LoadError{}
impl From<loader::TextureError> for LoadError{
	fn from(value:loader::TextureError)->Self{
		Self::Texture(value)
	}
}
impl From<loader::MeshError> for LoadError{
	fn from(value:loader::MeshError)->Self{
		Self::Mesh(value)
	}
}
pub struct Bsp{
	bsp:vbsp::Bsp,
	case_folded_file_names:std::collections::HashMap<String,String>,
}
impl AsRef<vbsp::Bsp> for Bsp{
	fn as_ref(&self)->&vbsp::Bsp{
		&self.bsp
	}
}

pub fn read<R:std::io::Read>(mut input:R)->Result<Bsp,ReadError>{
	let mut s=Vec::new();

	//TODO: mmap
	input.read_to_end(&mut s).map_err(ReadError::Io)?;

	vbsp::Bsp::read(s.as_slice()).map(Bsp::new).map_err(ReadError::Bsp)
}
impl Bsp{
	pub fn new(bsp:vbsp::Bsp)->Self{
		let case_folded_file_names=bsp.pack.clone().into_zip().lock().unwrap().file_names().map(|s|{
			(s.to_lowercase(),s.to_owned())
		}).collect();
		Self{
			bsp,
			case_folded_file_names,
		}
	}
	pub fn pack_get(&self,name_lowercase:&str)->Result<Option<Vec<u8>>,vbsp::BspError>{
		match self.case_folded_file_names.get(name_lowercase){
			Some(name_folded)=>self.bsp.pack.get(name_folded),
			None=>Ok(None),
		}
	}
	pub fn to_snf(&self,failure_mode:LoadFailureMode,vpk_list:&[Vpk])->Result<strafesnet_common::map::CompleteMap,LoadError>{
		let mut texture_deferred_loader=RenderConfigDeferredLoader::new();
		let mut mesh_deferred_loader=MeshDeferredLoader::new();

		let map_step1=bsp::convert(
			self,
			&mut texture_deferred_loader,
			&mut mesh_deferred_loader,
		);

		let mut mesh_loader=loader::MeshLoader::new(loader::BspFinder{bsp:self,vpks:vpk_list},&mut texture_deferred_loader);
		let prop_meshes=mesh_deferred_loader.into_meshes(&mut mesh_loader,failure_mode).map_err(LoadError::Mesh)?;

		let map_step2=map_step1.add_prop_meshes(prop_meshes);

		let mut texture_loader=loader::TextureLoader::new();
		let render_configs=texture_deferred_loader.into_render_configs(&mut texture_loader,failure_mode).map_err(LoadError::Texture)?;

		let map=map_step2.add_render_configs_and_textures(render_configs);

		Ok(map)
	}
}
pub struct Vpk{
	vpk:vpk::VPK,
	case_folded_file_names:std::collections::HashMap<String,String>,
}
impl AsRef<vpk::VPK> for Vpk{
	fn as_ref(&self)->&vpk::VPK{
		&self.vpk
	}
}
impl Vpk{
	pub fn new(vpk:vpk::VPK)->Vpk{
		let case_folded_file_names=vpk.tree.keys().map(|s|{
			(s.to_lowercase(),s.to_owned())
		}).collect();
		Vpk{
			vpk,
			case_folded_file_names,
		}
	}
	pub fn tree_get(&self,name_lowercase:&str)->Option<&vpk::entry::VPKEntry>{
		let name_folded=self.case_folded_file_names.get(name_lowercase)?;
		self.vpk.tree.get(name_folded)
	}
}