use std::io::Read;
use rbx_dom_weak::WeakDom;

mod rbx;
mod mesh;
mod primitives;

pub mod data{
	pub struct RobloxMeshBytes(Vec<u8>);
	impl RobloxMeshBytes{
		pub fn new(bytes:Vec<u8>)->Self{
			Self(bytes)
		}
		pub(crate) fn cursor(self)->std::io::Cursor<Vec<u8>>{
			std::io::Cursor::new(self.0)
		}
	}
}

pub struct Model{
	dom:WeakDom,
}
impl Model{
	fn new(dom:WeakDom)->Self{
		Self{dom}
	}
	pub fn into_place(self)->Place{
		let Self{mut dom}=self;
		let context=roblox_emulator::context::Context::from_mut(&mut dom);
		let services=context.convert_into_place();
		Place{dom,services}
	}
}
impl AsRef<WeakDom> for Model{
	fn as_ref(&self)->&WeakDom{
		&self.dom
	}
}

pub struct Place{
	dom:WeakDom,
	services:roblox_emulator::context::Services,
}
impl Place{
	fn new(dom:WeakDom)->Option<Self>{
		let context=roblox_emulator::context::Context::from_ref(&dom);
		Some(Self{
			services:context.find_services()?,
			dom,
		})
	}
	pub fn run_scripts(&mut self){
		let Place{dom,services}=self;
		let runner=roblox_emulator::runner::Runner::new().unwrap();
		let context=roblox_emulator::context::Context::from_mut(dom);
		let scripts=context.scripts();
		let runnable=runner.runnable_context_with_services(context,services).unwrap();
		for script in scripts{
			if let Err(e)=runnable.run_script(script){
				println!("runner error: {e}");
			}
		}
	}
}
impl AsRef<WeakDom> for Place{
	fn as_ref(&self)->&WeakDom{
		&self.dom
	}
}

#[derive(Debug)]
pub enum ReadError{
	RbxBinary(rbx_binary::DecodeError),
	RbxXml(rbx_xml::DecodeError),
	Io(std::io::Error),
	UnknownFileFormat,
}
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{}

pub fn read<R:Read>(input:R)->Result<Model,ReadError>{
	let mut buf=std::io::BufReader::new(input);
	let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
	match &peek[0..8]{
		b"<roblox!"=>rbx_binary::from_reader(buf).map(Model::new).map_err(ReadError::RbxBinary),
		b"<roblox "=>rbx_xml::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxXml),
		_=>Err(ReadError::UnknownFileFormat),
	}
}

//ConvertError

pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
	dom:impl AsRef<WeakDom>,
	acquire_render_config_id:AcquireRenderConfigId,
	acquire_mesh_id:AcquireMeshId
)->rbx::PartialMap1
where
	AcquireRenderConfigId:FnMut(Option<&str>)->strafesnet_common::model::RenderConfigId,
	AcquireMeshId:FnMut(&str)->strafesnet_common::model::MeshId,
{
	rbx::convert(&dom.as_ref(),acquire_render_config_id,acquire_mesh_id)
}
