use rbx_dom_weak::{types::Ref,InstanceBuilder,WeakDom};

pub fn class_is_a(class:&str,superclass:&str)->bool{
	class==superclass
	||rbx_reflection_database::get().classes.get(class)
	.is_some_and(|descriptor|
		descriptor.superclass.as_ref().is_some_and(|class_super|
			class_is_a(class_super,superclass)
		)
	)
}

#[repr(transparent)]
pub struct Context{
	pub(crate)dom:WeakDom,
}

impl Context{
	pub const fn new(dom:WeakDom)->Self{
		Self{dom}
	}
	pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance,Services){
		let script=InstanceBuilder::new("Script")
			.with_property("Source",rbx_types::Variant::String(source));
		let script_ref=script.referent();
		let mut context=Self::new(WeakDom::new(
			InstanceBuilder::new("DataModel")
			.with_child(script)
		));
		let services=context.convert_into_place();
		(context,crate::runner::instance::Instance::new(script_ref),services)
	}
	pub fn from_ref(dom:&WeakDom)->&Context{
		unsafe{&*(dom as *const WeakDom as *const Context)}
	}
	pub fn from_mut(dom:&mut WeakDom)->&mut Context{
		unsafe{&mut *(dom as *mut WeakDom as *mut Context)}
	}
	/// Creates an iterator over all items of a particular class.
	pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{
		self.dom.descendants().filter(|&instance|
			class_is_a(instance.class.as_ref(),superclass)
		).map(|instance|instance.referent())
	}
	pub fn scripts(&self)->Vec<crate::runner::instance::Instance>{
		self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Instance::new).collect()
	}

	pub fn find_services(&self)->Option<Services>{
		Some(Services{
			workspace:*self.dom.root().children().iter().find(|&&r|
				self.dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace")
			)?,
			game:self.dom.root_ref(),
		})
	}
	pub fn convert_into_place(&mut self)->Services{
		//snapshot root instances
		let children=self.dom.root().children().to_owned();

		//insert services
		let game=self.dom.root_ref();
		let terrain_bldr=InstanceBuilder::new("Terrain");
		let workspace=self.dom.insert(game,
			InstanceBuilder::new("Workspace")
				//Set Workspace.Terrain property equal to Terrain
				.with_property("Terrain",terrain_bldr.referent())
				.with_child(terrain_bldr)
		);
		{
			//Lowercase and upper case workspace property!
			let game=self.dom.root_mut();
			game.properties.insert("workspace".to_owned(),rbx_types::Variant::Ref(workspace));
			game.properties.insert("Workspace".to_owned(),rbx_types::Variant::Ref(workspace));
		}
		self.dom.insert(game,InstanceBuilder::new("Lighting"));

		//transfer original root instances into workspace
		for instance in children{
			self.dom.transfer_within(instance,workspace);
		}

		Services{
			game,
			workspace,
		}
	}
}

pub struct Services{
	pub game:Ref,
	pub workspace:Ref,
}
