use {
	crate::{
		lua::{LuaError, LuaVariant},
		prelude::*,
	},
	glib::{
		variant::{ToVariant, Variant, VariantTypeMismatchError},
		VariantTy, VariantType,
	},
	serde::{ser, Serialize},
};
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub fn to_variant<S: ser::Serialize>(v: S) -> Result<LuaVariant<'static>, LuaError> {
	v.serialize(Serializer::new())
}
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
#[derive(Debug, Copy, Clone)]
pub struct Serializer {
	humanize: bool,
}
impl Default for Serializer {
	fn default() -> Self {
		Self::new()
	}
}
impl Serializer {
	pub fn new() -> Self {
		Self { humanize: true }
	}
	pub fn inhuman(self) -> Self {
		Self { humanize: false }
	}
}
impl ser::Serializer for Serializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	type SerializeSeq = SeqSerializer;
	type SerializeTuple = SeqSerializer;
	type SerializeTupleStruct = Self::SerializeTuple;
	type SerializeTupleVariant = VariantSerializer<Self::SerializeTuple>;
	type SerializeMap = MapSerializer;
	type SerializeStruct = StructSerializer;
	type SerializeStructVariant = VariantSerializer<Self::SerializeStruct>;
	fn is_human_readable(&self) -> bool {
		self.humanize
	}
	fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
		self.serialize_i16(v.into())
	}
	fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
		self.serialize_u16(v.into())
	}
	fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
		self.serialize_f64(v as f64)
	}
	fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
		self.serialize_str(&v.to_string())
	}
	fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
		v.to_variant().try_into()
	}
	fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
		Ok(LuaVariant::with_bytes(v))
	}
	fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
		self.serialize_unit()
	}
	fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error> {
		value.serialize(self)
	}
	fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
		Ok(LuaVariant::nil())
	}
	fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
		self.serialize_unit()
	}
	fn serialize_unit_variant(
		self,
		_name: &'static str,
		variant_index: u32,
		variant: &'static str,
	) -> Result<Self::Ok, Self::Error> {
		if self.is_human_readable() {
			self.serialize_str(variant)
		} else {
			self.serialize_u32(variant_index)
		}
	}
	fn serialize_newtype_struct<T: Serialize + ?Sized>(
		self,
		_name: &'static str,
		value: &T,
	) -> Result<Self::Ok, Self::Error> {
		value.serialize(self)
	}
	fn serialize_newtype_variant<T: Serialize + ?Sized>(
		self,
		name: &'static str,
		variant_index: u32,
		variant: &'static str,
		value: &T,
	) -> Result<Self::Ok, Self::Error> {
		VariantSerializer::new(self, value, name, variant_index, variant).serialize()
	}
	fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
		Ok(SeqSerializer::new(self, len))
	}
	fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
		self.serialize_seq(Some(len))
	}
	fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeTupleStruct, Self::Error> {
		self.serialize_tuple(len)
	}
	fn serialize_tuple_variant(
		self,
		name: &'static str,
		variant_index: u32,
		variant: &'static str,
		len: usize,
	) -> Result<Self::SerializeTupleVariant, Self::Error> {
		self
			.serialize_tuple(len)
			.map(|ser| VariantSerializer::new(self, ser, name, variant_index, variant))
	}
	fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
		Ok(MapSerializer::new(self, None, len))
	}
	fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct, Self::Error> {
		Ok(if self.is_human_readable() {
			StructSerializer::Map(MapSerializer::new(self, Some(VariantTy::STRING), Some(len)))
		} else {
			StructSerializer::Seq(SeqSerializer::new(self, Some(len)))
		})
	}
	fn serialize_struct_variant(
		self,
		name: &'static str,
		variant_index: u32,
		variant: &'static str,
		len: usize,
	) -> Result<Self::SerializeStructVariant, Self::Error> {
		self
			.serialize_struct(name, len)
			.map(|ser| VariantSerializer::new(self, ser, name, variant_index, variant))
	}
}
pub struct SeqSerializer {
	ser: Serializer,
	size: Option<usize>,
	variants: Vec<LuaVariant<'static>>,
}
impl SeqSerializer {
	fn new(ser: Serializer, size: Option<usize>) -> Self {
		Self {
			variants: Vec::with_capacity(size.unwrap_or_default()),
			ser,
			size,
		}
	}
}
impl ser::SerializeSeq for SeqSerializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_element<S: Serialize + ?Sized>(&mut self, value: &S) -> Result<(), Self::Error> {
		value.serialize(self.ser).map(|v| self.variants.push(v))
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		match self.size {
			Some(size) if size != self.variants.len() =>
				return Err(LuaError::LengthMismatch {
					actual: self.variants.len(),
					expected: size,
				}),
			_ => (),
		}
		Ok(LuaVariant::from_iter(self.variants))
	}
}
impl ser::SerializeTuple for SeqSerializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_element<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> {
		ser::SerializeSeq::serialize_element(self, value)
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		ser::SerializeSeq::end(self)
	}
}
impl ser::SerializeTupleStruct for SeqSerializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> {
		ser::SerializeTuple::serialize_element(self, value)
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		ser::SerializeTuple::end(self)
	}
}
impl ser::SerializeStruct for SeqSerializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_field<T: Serialize + ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<(), Self::Error> {
		ser::SerializeTupleStruct::serialize_field(self, value)
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		ser::SerializeTupleStruct::end(self)
	}
}
pub struct MapSerializer {
	ser: Serializer,
	key_type: Option<&'static VariantTy>,
	keys: Vec<Variant>,
	values: Vec<Variant>,
	size: Option<usize>,
}
impl MapSerializer {
	fn new(ser: Serializer, key_type: Option<&'static VariantTy>, size: Option<usize>) -> Self {
		Self {
			ser,
			key_type,
			keys: Vec::with_capacity(size.unwrap_or_default()),
			values: Vec::with_capacity(size.unwrap_or_default()),
			size,
		}
	}
}
impl ser::SerializeMap for MapSerializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_key<T: Serialize + ?Sized>(&mut self, key: &T) -> Result<(), Self::Error> {
		let key = key.serialize(self.ser)?;
		match self.key_type {
			Some(key_type) if !key.as_variant().type_().is_subtype_of(key_type) =>
				return Err(LuaError::TypeMismatch(VariantTypeMismatchError::new(
					key.as_variant().type_().to_owned(),
					key_type.to_owned(),
				))),
			_ => (),
		}
		Ok(self.keys.push(key.into_variant()))
	}
	fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> {
		value.serialize(self.ser).map(|v| self.values.push(v.into_variant()))
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		match self.size {
			Some(expected) if expected != self.values.len() =>
				return Err(LuaError::LengthMismatch {
					actual: self.values.len(),
					expected,
				}),
			_ => (),
		}
		let key_type = self.key_type;
		let elem_ty = VariantType::new_dict_entry(&self.key_type.unwrap_or(VariantTy::VARIANT), VariantTy::VARIANT);
		let entries = self
			.keys
			.into_iter()
			.map(|k| if key_type.is_some() { k } else { k.to_variant() })
			.zip(self.values.into_iter())
			.map(|(k, v)| Variant::from_dict_entry(&k, &v));
		Variant::array_from_iter_with_type(&elem_ty, entries).try_into()
	}
}
impl ser::SerializeStruct for MapSerializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
	where
		T: Serialize,
	{
		ser::SerializeMap::serialize_entry(self, key, value)
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		ser::SerializeMap::end(self)
	}
}
pub enum StructSerializer {
	Map(MapSerializer),
	Seq(SeqSerializer),
}
impl ser::SerializeStruct for StructSerializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
	where
		T: Serialize,
	{
		match self {
			StructSerializer::Map(s) => ser::SerializeStruct::serialize_field(s, key, value),
			StructSerializer::Seq(s) => ser::SerializeStruct::serialize_field(s, key, value),
		}
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		match self {
			StructSerializer::Map(s) => ser::SerializeStruct::end(s),
			StructSerializer::Seq(s) => ser::SerializeStruct::end(s),
		}
	}
}
impl ser::SerializeTupleStruct for StructSerializer {
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> {
		match self {
			StructSerializer::Map(_) => unimplemented!(),
			StructSerializer::Seq(s) => ser::SerializeTupleStruct::serialize_field(s, value),
		}
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		match self {
			StructSerializer::Map(s) => ser::SerializeStruct::end(s),
			StructSerializer::Seq(s) => ser::SerializeStruct::end(s),
		}
	}
}
struct VariantSerializerState {
	ser: Serializer,
	_name: &'static str,
	variant_index: u32,
	variant: &'static str,
}
impl VariantSerializerState {
	fn tag<V: AsRef<Variant> + Into<LuaVariant<'static>>>(self, variant: V) -> Result<LuaVariant<'static>, LuaError> {
		let variant = LuaVariant::convert_from(variant.as_ref())?;
		Ok(match ser::Serializer::is_human_readable(&self.ser) {
			true => iter::once((self.variant, variant)).collect(),
			false => iter::once((self.variant_index, variant)).collect(),
		})
	}
}
pub struct VariantSerializer<S> {
	inner: S,
	state: VariantSerializerState,
}
impl<S> VariantSerializer<S> {
	fn new(ser: Serializer, inner: S, _name: &'static str, variant_index: u32, variant: &'static str) -> Self {
		Self {
			inner,
			state: VariantSerializerState {
				ser,
				_name,
				variant_index,
				variant,
			},
		}
	}
	fn serialize(self) -> Result<LuaVariant<'static>, LuaError>
	where
		S: Serialize,
	{
		self.inner.serialize(self.state.ser).and_then(|v| self.state.tag(v))
	}
}
impl<S: ser::SerializeTuple> ser::SerializeTupleVariant for VariantSerializer<S>
where
	S::Ok: AsRef<Variant> + Into<LuaVariant<'static>>,
	S::Error: Into<LuaError>,
{
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_field<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), Self::Error> {
		self.inner.serialize_element(value).map_err(Into::into)
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		let state = self.state;
		self.inner.end().map_err(Into::into).and_then(|ok| state.tag(ok))
	}
}
impl<S: ser::SerializeStruct> ser::SerializeStructVariant for VariantSerializer<S>
where
	S::Ok: AsRef<Variant> + Into<LuaVariant<'static>>,
	S::Error: Into<LuaError>,
{
	type Ok = LuaVariant<'static>;
	type Error = LuaError;
	fn serialize_field<T: Serialize + ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> {
		self.inner.serialize_field(key, value).map_err(Into::into)
	}
	fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
		self.inner.skip_field(key).map_err(Into::into)
	}
	fn end(self) -> Result<Self::Ok, Self::Error> {
		let state = self.state;
		self.inner.end().map_err(Into::into).and_then(|ok| state.tag(ok))
	}
}