use super::{
	INPUT_HANDLER_REGISTRY, INPUT_METHOD_REF_ASPECT_ALIAS_INFO, INPUT_METHOD_REGISTRY, InputData,
	InputDataTrait, InputDataType, InputHandler, InputMethodAspect, InputMethodRefAspect,
	input_method_client,
};
use crate::{
	core::{
		client::Client,
		error::{Result, ServerError},
		registry::Registry,
	},
	nodes::{
		Node,
		alias::{Alias, AliasList},
		fields::{FIELD_ALIAS_INFO, Field},
		spatial::Spatial,
	},
};
use color_eyre::eyre::eyre;
use parking_lot::Mutex;
use stardust_xr::values::Datamap;
use std::sync::{Arc, Weak};

pub struct InputMethod {
	pub spatial: Arc<Spatial>,
	pub data: Mutex<InputDataType>,
	pub datamap: Mutex<Datamap>,

	handler_aliases: AliasList,
	handler_field_aliases: AliasList,
	pub(super) handler_order: Mutex<Vec<Weak<InputHandler>>>,
	pub capture_attempts: Registry<InputHandler>,
	pub captures: Registry<InputHandler>,
}
impl InputMethod {
	pub fn add_to(
		node: &Arc<Node>,
		data: InputDataType,
		datamap: Datamap,
	) -> Result<Arc<InputMethod>> {
		let method = InputMethod {
			spatial: node.get_aspect::<Spatial>().unwrap().clone(),
			data: Mutex::new(data),
			datamap: Mutex::new(datamap),

			handler_aliases: AliasList::default(),
			handler_field_aliases: AliasList::default(),
			handler_order: Mutex::new(Vec::new()),
			capture_attempts: Registry::new(),
			captures: Registry::new(),
		};
		for handler in INPUT_HANDLER_REGISTRY.get_valid_contents() {
			method.handle_new_handler(&handler);
		}
		let method = INPUT_METHOD_REGISTRY.add(method);
		node.add_aspect_raw(method.clone());
		node.add_aspect(InputMethodRef);
		Ok(method)
	}

	pub fn distance(&self, to: &Field) -> f32 {
		self.data.lock().distance(&self.spatial, to)
	}

	pub fn set_handler_order<'a>(&self, handlers: impl Iterator<Item = &'a Arc<InputHandler>>) {
		*self.handler_order.lock() = handlers.map(Arc::downgrade).collect();
	}

	pub(super) fn make_alias(&self, handler: &InputHandler) {
		let Some(method_node) = self.spatial.node() else {
			return;
		};
		let Some(handler_node) = handler.spatial.node() else {
			return;
		};
		let Some(client) = handler_node.get_client() else {
			return;
		};
		let Ok(method_alias) = Alias::create(
			&method_node,
			&client,
			INPUT_METHOD_REF_ASPECT_ALIAS_INFO.clone(),
			Some(&handler.method_aliases),
		) else {
			return;
		};
		method_alias.set_enabled(false);
	}
	pub(super) fn handle_new_handler(&self, handler: &InputHandler) {
		self.make_alias(handler);

		let Some(method_node) = self.spatial.node() else {
			return;
		};
		let Some(method_client) = method_node.get_client() else {
			return;
		};
		let Some(handler_node) = handler.spatial.node() else {
			return;
		};
		// Receiver itself
		let Ok(handler_alias) = Alias::create(
			&handler_node,
			&method_client,
			INPUT_METHOD_REF_ASPECT_ALIAS_INFO.clone(),
			Some(&self.handler_aliases),
		) else {
			return;
		};

		let Some(handler_field_node) = handler.field.spatial.node() else {
			return;
		};
		// Handler's field
		let Ok(rx_field_alias) = Alias::create(
			&handler_field_node,
			&method_client,
			FIELD_ALIAS_INFO.clone(),
			Some(&self.handler_field_aliases),
		) else {
			return;
		};

		let _ = input_method_client::create_handler(&method_node, &handler_alias, &rx_field_alias);
	}
	pub(super) fn handle_drop_handler(&self, handler: &InputHandler) {
		let Some(tx_node) = self.spatial.node() else {
			return;
		};
		let Some(handler_alias) = self.handler_aliases.get_from_aspect(handler) else {
			return;
		};
		let _ = input_method_client::destroy_handler(&tx_node, handler_alias.id);
		self.handler_aliases.remove_aspect(handler);
		self.handler_field_aliases
			.remove_aspect(handler.field.as_ref());
		self.capture_attempts.remove(handler);
	}

	pub(super) fn serialize(&self, alias_id: u64, handler: &Arc<InputHandler>) -> InputData {
		let mut input = self.data.lock().clone();
		input.transform(self, handler);

		InputData {
			id: alias_id,
			input,
			distance: self.distance(&handler.field),
			datamap: self.datamap.lock().clone(),
			order: self
				.handler_order
				.lock()
				.iter()
				.enumerate()
				.find(|(_, h)| h.ptr_eq(&Arc::downgrade(handler)))
				.unwrap()
				.0 as u32,
			captured: self.captures.get_valid_contents().contains(handler),
		}
	}

	pub(super) fn cull_capture_attempts(&self) {
		let sent = self
			.handler_order
			.lock()
			.iter()
			.filter_map(Weak::upgrade)
			.collect::<Registry<InputHandler>>();
		self.capture_attempts.retain(|handler| {
			!handler
				.spatial
				.node()
				.and_then(|n| n.get_client())
				.map(|c| c.unresponsive())
				.unwrap_or(false)
				&& sent.contains(handler)
		});
	}
}
impl InputMethodAspect for InputMethod {
	#[doc = "Set the spatial input component of this input method. You must keep the same input data type throughout the entire thing."]
	fn set_input(
		node: Arc<Node>,
		_calling_client: Arc<Client>,
		input: InputDataType,
	) -> Result<()> {
		let input_method = node.get_aspect::<InputMethod>()?;
		*input_method.data.lock() = input;
		Ok(())
	}

	#[doc = "Set the datmap of this input method"]
	fn set_datamap(node: Arc<Node>, _calling_client: Arc<Client>, datamap: Datamap) -> Result<()> {
		let input_method = node.get_aspect::<InputMethod>()?;
		*input_method.datamap.lock() = datamap;
		Ok(())
	}

	#[doc = "Manually set the order of handlers to propagate input to, or else let the server decide."]
	fn set_handler_order(
		node: Arc<Node>,
		_calling_client: Arc<Client>,
		handlers: Vec<Arc<Node>>,
	) -> Result<()> {
		let input_method = node.get_aspect::<InputMethod>()?;
		let handlers = handlers
			.into_iter()
			.filter_map(|p| p.get_aspect::<InputHandler>().ok())
			.map(|i| Arc::downgrade(&i))
			.collect::<Vec<_>>();

		*input_method.handler_order.lock() = handlers;
		Ok(())
	}

	#[doc = "Set which handlers are captured."]
	fn set_captures(
		node: Arc<Node>,
		_calling_client: Arc<Client>,
		handlers: Vec<Arc<Node>>,
	) -> Result<()> {
		let input_method = node.get_aspect::<InputMethod>()?;
		input_method.captures.clear();
		for handler in handlers {
			let Ok(handler) = handler.get_aspect::<InputHandler>() else {
				continue;
			};
			input_method.captures.add_raw(&handler);
		}
		Ok(())
	}
}
impl Drop for InputMethod {
	fn drop(&mut self) {
		INPUT_METHOD_REGISTRY.remove(self);
	}
}

pub struct InputMethodRef;
impl InputMethodRefAspect for InputMethodRef {
	#[doc = "Try to capture the input method with the given handler. When the handler does not get input from the method, it will be released."]
	fn try_capture(
		node: Arc<Node>,
		_calling_client: Arc<Client>,
		handler: Arc<Node>,
	) -> Result<()> {
		let input_method = node.get_aspect::<InputMethod>()?;
		let input_handler = handler.get_aspect::<InputHandler>()?;

		input_method.capture_attempts.add_raw(&input_handler);

		let Some(handler_alias) = input_method
			.handler_aliases
			.get_from_aspect(&*input_handler)
		else {
			return Err(ServerError::Report(eyre!(
				"Internal: Couldn't get handler alias somehow?"
			)));
		};
		input_method_client::request_capture_handler(&node, handler_alias.get_id())
	}

	#[doc = "If captured by this handler, release it (e.g. the object is let go of after grabbing)."]
	fn release(node: Arc<Node>, _calling_client: Arc<Client>, handler: Arc<Node>) -> Result<()> {
		let input_method = node.get_aspect::<InputMethod>()?;
		let input_handler = handler.get_aspect::<InputHandler>()?;

		input_method.capture_attempts.remove(&input_handler);

		let Some(handler_alias) = input_method
			.handler_aliases
			.get_from_aspect(&*input_handler)
		else {
			return Err(ServerError::Report(eyre!(
				"Internal: Couldn't get handler alias somehow?"
			)));
		};
		input_method_client::release_handler(&node, handler_alias.get_id())
	}
}
