use super::de::Error;
use super::*;
use crate::dict::{DictBuilder, Key};
use crate::functor::Functor;
use crate::{atom, functor};
use serde::ser::Impossible;
use serde::{self, ser, Serialize};
use std::cell::Cell;
pub(crate) const ATOM_STRUCT_NAME: &str = "$swipl::private::atom";
impl ser::Error for Error {
    fn custom<T>(c: T) -> Self
    where
        T: std::fmt::Display,
    {
        Self::Message(c.to_string())
    }
}
pub fn to_term<'a, T, C: QueryableContextType>(
    context: &'a Context<C>,
    term: &Term<'a>,
    obj: &T,
) -> Result<(), Error>
where
    T: Serialize,
{
    let serializer = Serializer::new(context, term.clone());
    let _state = SerializingSwiplTermState::start();
    obj.serialize(serializer)
}
pub fn to_term_with_config<'a, T, C: QueryableContextType>(
    context: &'a Context<C>,
    term: &Term<'a>,
    obj: &T,
    configuration: SerializerConfiguration,
) -> Result<(), Error>
where
    T: Serialize,
{
    let serializer = Serializer::new_with_config(context, term.clone(), configuration);
    let _state = SerializingSwiplTermState::start();
    obj.serialize(serializer)
}
fn attempt_unify<U: Unifiable>(term: &Term, v: U) -> Result<(), Error> {
    if attempt(term.unify(v))? {
        Ok(())
    } else {
        Err(Error::UnificationFailed)
    }
}
#[derive(Debug, Clone)]
pub struct SerializerConfiguration {
    default_tag: Option<Atom>,
    tag_struct_dicts: bool,
    unit_atom: Option<Atom>,
}
impl Default for SerializerConfiguration {
    fn default() -> Self {
        Self::new()
    }
}
impl SerializerConfiguration {
    pub fn new() -> Self {
        Self {
            default_tag: None,
            tag_struct_dicts: false,
            unit_atom: None,
        }
    }
    pub fn set_default_tag<A: AsAtom>(&mut self, tag: A) {
        self.default_tag = Some(tag.as_atom());
    }
    pub fn default_tag<A: AsAtom>(mut self, tag: A) -> Self {
        self.set_default_tag(tag.as_atom());
        self
    }
    pub fn set_tag_struct_dicts(&mut self) {
        self.tag_struct_dicts = true;
    }
    pub fn tag_struct_dicts(mut self) -> Self {
        self.set_tag_struct_dicts();
        self
    }
    pub fn set_unit_atom(&mut self, unit: Atom) {
        self.unit_atom = Some(unit);
    }
    pub fn unit_atom(mut self, unit: Atom) -> Self {
        self.set_unit_atom(unit);
        self
    }
}
pub struct Serializer<'a, C: QueryableContextType> {
    context: &'a Context<'a, C>,
    term: Term<'a>,
    configuration: SerializerConfiguration,
}
impl<'a, C: QueryableContextType> Serializer<'a, C> {
    pub fn new(context: &'a Context<'a, C>, term: Term<'a>) -> Self {
        Self {
            context,
            term,
            configuration: SerializerConfiguration::new(),
        }
    }
    pub fn new_with_config(
        context: &'a Context<'a, C>,
        term: Term<'a>,
        configuration: SerializerConfiguration,
    ) -> Self {
        Self {
            context,
            term,
            configuration,
        }
    }
}
impl<'a, C: QueryableContextType> serde::Serializer for Serializer<'a, C> {
    type Ok = ();
    type Error = Error;
    type SerializeSeq = SerializeSeq<'a, C>;
    type SerializeTuple = SerializeTuple<'a, C>;
    type SerializeTupleStruct = SerializeNamedTuple<'a, C>;
    type SerializeTupleVariant = SerializeNamedTuple<'a, C>;
    type SerializeMap = SerializeMap<'a, C>;
    type SerializeStruct = SerializeMap<'a, C>;
    type SerializeStructVariant = SerializeMap<'a, C>;
    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, v)
    }
    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
        self.serialize_i64(v as i64)
    }
    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
        self.serialize_i64(v as i64)
    }
    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
        self.serialize_i64(v as i64)
    }
    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, v)
    }
    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
        self.serialize_u64(v as u64)
    }
    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
        self.serialize_u64(v as u64)
    }
    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
        self.serialize_u64(v as u64)
    }
    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, v)
    }
    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> {
        attempt_unify(&self.term, v)
    }
    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, Atomable::String(v.to_string()))
    }
    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, v)
    }
    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, v)
    }
    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, atom!("none"))
    }
    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        if attempt(self.term.unify(functor!("some/1")))? {
            let [term] = attempt_opt(self.context.compound_terms(&self.term))?.expect("having just unified the functor some/1, retrieving its argument list should have been possible");
            let inner_serializer =
                Serializer::new_with_config(self.context, term.clone(), self.configuration.clone());
            let result = value.serialize(inner_serializer);
            unsafe {
                term.reset();
            }
            result
        } else {
            Err(Error::UnificationFailed)
        }
    }
    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
        if let Some(unit) = self.configuration.unit_atom {
            attempt_unify(&self.term, unit)
        } else {
            attempt_unify(&self.term, Nil)
        }
    }
    fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, Atom::new(name))
    }
    fn serialize_unit_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
    ) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, Atom::new(variant))
    }
    fn serialize_newtype_struct<T: ?Sized>(
        self,
        name: &'static str,
        value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        if name == ATOM_STRUCT_NAME {
            value.serialize(AtomEmitter(self.term))
        } else if attempt(self.term.unify(Functor::new(name, 1)))? {
            let [term] = attempt_opt(self.context.compound_terms(&self.term))?.expect("having just unified the functor with arity 1, retrieving its argument list should have been possible");
            let inner_serializer =
                Serializer::new_with_config(self.context, term.clone(), self.configuration.clone());
            let result = value.serialize(inner_serializer);
            unsafe {
                term.reset();
            }
            result
        } else {
            Err(Error::UnificationFailed)
        }
    }
    fn serialize_newtype_variant<T: ?Sized>(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        if attempt(self.term.unify(Functor::new(variant, 1)))? {
            let [term] = attempt_opt(self.context.compound_terms(&self.term))?.expect("having just unified the functor with arity 1, retrieving its argument list should have been possible");
            let inner_serializer =
                Serializer::new_with_config(self.context, term.clone(), self.configuration.clone());
            let result = value.serialize(inner_serializer);
            unsafe {
                term.reset();
            }
            result
        } else {
            Err(Error::UnificationFailed)
        }
    }
    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
        Ok(SerializeSeq {
            context: self.context,
            term: self.term.clone(),
            configuration: self.configuration.clone(),
        })
    }
    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
        Ok(SerializeTuple {
            context: self.context,
            term: self.term.clone(),
            len,
            configuration: self.configuration.clone(),
        })
    }
    fn serialize_tuple_struct(
        self,
        name: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
        if len > u16::MAX as usize {
            return Err(Error::UnsupportedValue);
        }
        attempt_unify(&self.term, Functor::new(name, len as u16))?;
        Ok(SerializeNamedTuple {
            context: self.context,
            term: self.term.clone(),
            pos: 0,
            configuration: self.configuration.clone(),
        })
    }
    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        len: usize,
    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
        attempt_unify(&self.term, Functor::new(variant, len as u16))?;
        Ok(SerializeNamedTuple {
            context: self.context,
            term: self.term.clone(),
            pos: 0,
            configuration: self.configuration.clone(),
        })
    }
    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
        Ok(SerializeMap::new(
            self.context,
            self.term,
            None,
            self.configuration,
        ))
    }
    fn serialize_struct(
        self,
        name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStruct, Self::Error> {
        let tag = if self.configuration.tag_struct_dicts {
            Some(name)
        } else {
            None
        };
        Ok(SerializeMap::new(
            self.context,
            self.term,
            tag,
            self.configuration,
        ))
    }
    fn serialize_struct_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStructVariant, Self::Error> {
        Ok(SerializeMap::new(
            self.context,
            self.term,
            Some(variant),
            self.configuration,
        ))
    }
}
thread_local! {
    static SERIALIZING_SWIPL_TERM: Cell<bool> = const { Cell::new(false) };
}
struct SerializingSwiplTermState;
impl SerializingSwiplTermState {
    fn start() -> Self {
        SERIALIZING_SWIPL_TERM.with(|sst| {
            if sst.get() {
                panic!("swipl term serialization was already set. did we recurse?");
            }
            sst.set(true)
        });
        Self
    }
    fn is_serializing_swipl_term() -> bool {
        SERIALIZING_SWIPL_TERM.with(|sst| sst.get())
    }
}
impl Drop for SerializingSwiplTermState {
    fn drop(&mut self) {
        SERIALIZING_SWIPL_TERM.with(|sst| sst.set(false));
    }
}
impl ser::Serialize for Atom {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        if SerializingSwiplTermState::is_serializing_swipl_term() {
            serializer.serialize_newtype_struct(ATOM_STRUCT_NAME, &self.atom_ptr())
        } else {
            serializer.serialize_newtype_struct(ATOM_STRUCT_NAME, &self.name())
        }
    }
}
struct AtomEmitter<'a>(Term<'a>);
fn attempt_unify_atom(term: &Term, atom_ptr: usize) -> Result<(), Error> {
    let atom = unsafe { Atom::wrap(atom_ptr) };
    let result = attempt_unify(term, &atom);
    std::mem::forget(atom);
    result
}
impl<'a> ser::Serializer for AtomEmitter<'a> {
    type Ok = ();
    type Error = Error;
    type SerializeSeq = Impossible<(), Error>;
    type SerializeTuple = Impossible<(), Error>;
    type SerializeTupleStruct = Impossible<(), Error>;
    type SerializeTupleVariant = Impossible<(), Error>;
    type SerializeMap = Impossible<(), Error>;
    type SerializeStruct = Impossible<(), Error>;
    type SerializeStructVariant = Impossible<(), Error>;
    fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    #[cfg(target_pointer_width = "32")]
    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
        attempt_unify_atom(&self.0, v as usize)
    }
    #[cfg(target_pointer_width = "32")]
    fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    #[cfg(target_pointer_width = "64")]
    fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    #[cfg(target_pointer_width = "64")]
    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
        attempt_unify_atom(&self.0, v as usize)
    }
    fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.0, Atomable::Str(v))
    }
    fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_unit_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
    ) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_newtype_struct<T: ?Sized>(
        self,
        _name: &'static str,
        _value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_newtype_variant<T: ?Sized>(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_tuple_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStruct, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
    fn serialize_struct_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStructVariant, Self::Error> {
        Err(Error::ValueNotOfExpectedType("string"))
    }
}
pub struct SerializeSeq<'a, C: QueryableContextType> {
    context: &'a Context<'a, C>,
    term: Term<'a>,
    configuration: SerializerConfiguration,
}
impl<'a, C: QueryableContextType> ser::SerializeSeq for SerializeSeq<'a, C> {
    type Ok = ();
    type Error = Error;
    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
    where
        T: Serialize,
    {
        if let Some((head, tail)) = attempt_opt(self.context.unify_list_functor(&self.term))? {
            let inner_serializer =
                Serializer::new_with_config(self.context, head, self.configuration.clone());
            value.serialize(inner_serializer)?;
            self.term = tail;
            Ok(())
        } else {
            Err(Error::UnificationFailed)
        }
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, Nil)
    }
}
pub struct SerializeTuple<'a, C: QueryableContextType> {
    context: &'a Context<'a, C>,
    term: Term<'a>,
    len: usize,
    configuration: SerializerConfiguration,
}
impl<'a, C: QueryableContextType> ser::SerializeTuple for SerializeTuple<'a, C> {
    type Ok = ();
    type Error = Error;
    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
    where
        T: Serialize,
    {
        self.len -= 1;
        if self.len == 0 {
            let inner_serializer = Serializer::new_with_config(
                self.context,
                self.term.clone(),
                self.configuration.clone(),
            );
            value.serialize(inner_serializer)
        } else {
            attempt_unify(&self.term, functor!(",/2"))?;
            let [head, tail] = attempt_opt(self.context.compound_terms(&self.term))?
                .expect("should have two terms");
            let inner_serializer =
                Serializer::new_with_config(self.context, head, self.configuration.clone());
            value.serialize(inner_serializer)?;
            self.term = tail;
            Ok(())
        }
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        assert!(self.len == 0);
        Ok(())
    }
}
pub struct SerializeNamedTuple<'a, C: QueryableContextType> {
    context: &'a Context<'a, C>,
    term: Term<'a>,
    pos: usize,
    configuration: SerializerConfiguration,
}
impl<'a, C: QueryableContextType> SerializeNamedTuple<'a, C> {
    fn serialize_field_impl<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
    where
        T: Serialize,
    {
        self.pos += 1;
        let term = self.context.new_term_ref();
        assert!(attempt(self.term.unify_arg(self.pos, &term))?);
        let inner_serializer =
            Serializer::new_with_config(self.context, term, self.configuration.clone());
        value.serialize(inner_serializer)?;
        Ok(())
    }
}
impl<'a, C: QueryableContextType> ser::SerializeTupleStruct for SerializeNamedTuple<'a, C> {
    type Ok = ();
    type Error = Error;
    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
    where
        T: Serialize,
    {
        self.serialize_field_impl(value)
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        Ok(())
    }
}
impl<'a, C: QueryableContextType> ser::SerializeTupleVariant for SerializeNamedTuple<'a, C> {
    type Ok = ();
    type Error = Error;
    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
    where
        T: Serialize,
    {
        self.serialize_field_impl(value)
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        Ok(())
    }
}
pub struct SerializeMap<'a, C: QueryableContextType> {
    context: &'a Context<'a, C>,
    term: Term<'a>,
    configuration: SerializerConfiguration,
    reset_term: Term<'a>,
    builder: DictBuilder<'a>,
    last_key: Option<Key>,
}
impl<'a, C: QueryableContextType> SerializeMap<'a, C> {
    fn new(
        context: &'a Context<'a, C>,
        term: Term<'a>,
        tag: Option<&str>,
        configuration: SerializerConfiguration,
    ) -> Self {
        let reset_term = context.new_term_ref();
        let mut builder = DictBuilder::new();
        if let Some(tag) = tag {
            builder.set_tag(tag);
        } else if let Some(tag) = configuration.default_tag.as_ref() {
            builder.set_tag(tag);
        }
        Self {
            context,
            term,
            configuration,
            reset_term,
            builder,
            last_key: None,
        }
    }
}
impl<'a, C: QueryableContextType> ser::SerializeMap for SerializeMap<'a, C> {
    type Ok = ();
    type Error = Error;
    fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
    where
        T: Serialize,
    {
        let serializer = KeyEmitter {
            key: &mut self.last_key,
            getting_atom: false,
        };
        key.serialize(serializer)
    }
    fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
    where
        T: Serialize,
    {
        let val_term = self.context.new_term_ref();
        let serializer =
            Serializer::new_with_config(self.context, val_term.clone(), self.configuration.clone());
        value.serialize(serializer)?;
        let mut key = None;
        std::mem::swap(&mut key, &mut self.last_key);
        self.builder
            .add_entry(key.expect("key should have been set"), val_term);
        Ok(())
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, self.builder)?;
        unsafe { self.reset_term.reset() };
        Ok(())
    }
}
impl<'a, C: QueryableContextType> ser::SerializeStruct for SerializeMap<'a, C> {
    type Ok = ();
    type Error = Error;
    fn serialize_field<T: ?Sized>(
        &mut self,
        key: &'static str,
        value: &T,
    ) -> Result<(), Self::Error>
    where
        T: Serialize,
    {
        let value_term = self.context.new_term_ref();
        let serializer = Serializer::new_with_config(
            self.context,
            value_term.clone(),
            self.configuration.clone(),
        );
        value.serialize(serializer)?;
        self.builder.add_entry(key, value_term);
        Ok(())
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, self.builder)?;
        unsafe { self.reset_term.reset() };
        Ok(())
    }
}
impl<'a, C: QueryableContextType> ser::SerializeStructVariant for SerializeMap<'a, C> {
    type Ok = ();
    type Error = Error;
    fn serialize_field<T: ?Sized>(
        &mut self,
        key: &'static str,
        value: &T,
    ) -> Result<(), Self::Error>
    where
        T: Serialize,
    {
        let value_term = self.context.new_term_ref();
        let serializer = Serializer::new_with_config(
            self.context,
            value_term.clone(),
            self.configuration.clone(),
        );
        value.serialize(serializer)?;
        self.builder.add_entry(key, value_term);
        Ok(())
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        attempt_unify(&self.term, self.builder)?;
        unsafe { self.reset_term.reset() };
        Ok(())
    }
}
struct KeyEmitter<'a> {
    key: &'a mut Option<Key>,
    getting_atom: bool,
}
impl<'a> ser::Serializer for KeyEmitter<'a> {
    type Ok = ();
    type Error = Error;
    type SerializeSeq = Impossible<(), Error>;
    type SerializeTuple = Impossible<(), Error>;
    type SerializeTupleStruct = Impossible<(), Error>;
    type SerializeTupleVariant = Impossible<(), Error>;
    type SerializeMap = Impossible<(), Error>;
    type SerializeStruct = Impossible<(), Error>;
    type SerializeStructVariant = Impossible<(), Error>;
    fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
        match v {
            true => *self.key = Some(Key::Atom(atom!("true"))),
            false => *self.key = Some(Key::Atom(atom!("false"))),
        }
        Ok(())
    }
    fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
        if v >= 0 {
            self.serialize_u8(v as u8)
        } else {
            Err(Error::ValueNotOfExpectedType("key"))
        }
    }
    fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
        if v >= 0 {
            self.serialize_u16(v as u16)
        } else {
            Err(Error::ValueNotOfExpectedType("key"))
        }
    }
    fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
        if v >= 0 {
            self.serialize_u32(v as u32)
        } else {
            Err(Error::ValueNotOfExpectedType("key"))
        }
    }
    fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
        if v >= 0 {
            self.serialize_u64(v as u64)
        } else {
            Err(Error::ValueNotOfExpectedType("key"))
        }
    }
    fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
        *self.key = Some(Key::Int(v as u64));
        Ok(())
    }
    fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
        *self.key = Some(Key::Int(v as u64));
        Ok(())
    }
    #[cfg(target_pointer_width = "32")]
    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
        if self.getting_atom {
            let atom = unsafe { Atom::wrap(v as atom_t) };
            atom.increment_refcount();
            *self.key = Some(Key::Atom(atom));
        } else {
            *self.key = Some(Key::Int(v as u64));
        }
        Ok(())
    }
    #[cfg(target_pointer_width = "32")]
    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
        *self.key = Some(Key::Int(v));
        Ok(())
    }
    #[cfg(target_pointer_width = "64")]
    fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
        *self.key = Some(Key::Int(v as u64));
        Ok(())
    }
    #[cfg(target_pointer_width = "64")]
    fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
        if self.getting_atom {
            let atom = unsafe { Atom::wrap(v as atom_t) };
            atom.increment_refcount();
            *self.key = Some(Key::Atom(atom));
        } else {
            *self.key = Some(Key::Int(v));
        }
        Ok(())
    }
    fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
        let mut buf = [0u8; 4];
        let s = v.encode_utf8(&mut buf);
        *self.key = Some(Key::Atom(Atom::new(s)));
        Ok(())
    }
    fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
        *self.key = Some(Key::Atom(Atom::new(v)));
        Ok(())
    }
    fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_unit_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
    ) -> Result<Self::Ok, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_newtype_struct<T: ?Sized>(
        mut self,
        name: &'static str,
        value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        if name == ATOM_STRUCT_NAME {
            self.getting_atom = true;
            value.serialize(self)
        } else {
            value.serialize(self)
        }
    }
    fn serialize_newtype_variant<T: ?Sized>(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_tuple_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleStruct, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_tuple_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeTupleVariant, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_struct(
        self,
        _name: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStruct, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
    fn serialize_struct_variant(
        self,
        _name: &'static str,
        _variant_index: u32,
        _variant: &'static str,
        _len: usize,
    ) -> Result<Self::SerializeStructVariant, Self::Error> {
        Err(Error::ValueNotOfExpectedType("key"))
    }
}
#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use super::*;
    #[test]
    fn serialize_u32() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let num: u32 = 42;
        let term = context.new_term_ref();
        to_term(&context, &term, &num).unwrap();
        let term_string = context.string_from_term(&term).unwrap();
        assert_eq!("42", term_string);
    }
    #[test]
    fn serialize_string() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let s = "hello";
        let term = context.new_term_ref();
        to_term(&context, &term, &s).unwrap();
        let term_string = context.string_from_term(&term).unwrap();
        assert_eq!("\"hello\"", term_string);
    }
    #[test]
    fn serialize_atom() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let a = atom!("hello");
        let term = context.new_term_ref();
        to_term(&context, &term, &a).unwrap();
        let term_string = context.string_from_term(&term).unwrap();
        assert_eq!("hello", term_string);
    }
    #[test]
    fn serialize_list() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let list = [42, 43, 44];
        let term = context.new_term_ref();
        to_term(&context, &term, &list.as_slice()).unwrap();
        let term_string = context.string_from_term(&term).unwrap();
        assert_eq!("[42,43,44]", term_string);
    }
    #[test]
    fn serialize_tuple() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let tuple = (42, 43, 44);
        let term = context.new_term_ref();
        to_term(&context, &term, &tuple).unwrap();
        let term_string = context.string_from_term(&term).unwrap();
        assert_eq!("42,43,44", term_string);
    }
    #[test]
    fn serialize_tuple_list() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let list = [42, 43, 44];
        let term = context.new_term_ref();
        to_term(&context, &term, &list).unwrap();
        let term_string = context.string_from_term(&term).unwrap();
        assert_eq!("42,43,44", term_string);
    }
    #[derive(Serialize)]
    #[serde(rename = "flargh")]
    struct Flargh(u64, String, Atom);
    #[test]
    fn serialize_named_tuple() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let flargh = Flargh(42, "hello".to_string(), atom!("moo"));
        let term = context.new_term_ref();
        to_term(&context, &term, &flargh).unwrap();
        let term_string = context.string_from_term(&term).unwrap();
        assert_eq!("flargh(42,\"hello\",moo)", term_string);
    }
    #[test]
    fn serialize_map_with_str_keys() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let map = HashMap::from([("foo", "bar"), ("baz", "quux")]);
        let term = context.new_term_ref();
        to_term(&context, &term, &map).unwrap();
        let return_map: HashMap<Atom, String> = context.deserialize_from_term(&term).unwrap();
        assert_eq!(
            HashMap::from([
                (atom!("foo"), "bar".to_string()),
                (atom!("baz"), "quux".to_string())
            ]),
            return_map
        );
    }
    #[test]
    fn serialize_map_with_atom_keys() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let map = HashMap::from([(atom!("foo"), "bar"), (atom!("baz"), "quux")]);
        let term = context.new_term_ref();
        to_term(&context, &term, &map).unwrap();
        let tag = term.get_dict_tag().unwrap();
        assert_eq!(None, tag);
        let return_map: HashMap<Atom, String> = context.deserialize_from_term(&term).unwrap();
        assert_eq!(
            HashMap::from([
                (atom!("foo"), "bar".to_string()),
                (atom!("baz"), "quux".to_string())
            ]),
            return_map
        );
    }
    #[test]
    fn serialize_map_with_atom_keys_default_tag() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let map = HashMap::from([(atom!("foo"), "bar"), (atom!("baz"), "quux")]);
        let term = context.new_term_ref();
        to_term_with_config(
            &context,
            &term,
            &map,
            SerializerConfiguration::new().default_tag("default_tag"),
        )
        .unwrap();
        let tag = term.get_dict_tag().unwrap();
        assert_eq!(Some(atom!("default_tag")), tag);
        let return_map: HashMap<Atom, String> = context.deserialize_from_term(&term).unwrap();
        assert_eq!(
            HashMap::from([
                (atom!("foo"), "bar".to_string()),
                (atom!("baz"), "quux".to_string())
            ]),
            return_map
        );
    }
    #[test]
    fn serialize_map_with_u8_keys() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let map = HashMap::from([(12u8, "bar"), (42, "quux")]);
        let term = context.new_term_ref();
        to_term(&context, &term, &map).unwrap();
        let return_map: HashMap<u8, String> = context.deserialize_from_term(&term).unwrap();
        assert_eq!(
            HashMap::from([(12, "bar".to_string()), (42, "quux".to_string())]),
            return_map
        );
    }
    #[test]
    fn serialize_map_with_i8_keys() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let map = HashMap::from([(12i8, "bar"), (42, "quux")]);
        let term = context.new_term_ref();
        to_term(&context, &term, &map).unwrap();
        let return_map: HashMap<u8, String> = context.deserialize_from_term(&term).unwrap();
        assert_eq!(
            HashMap::from([(12, "bar".to_string()), (42, "quux".to_string())]),
            return_map
        );
    }
    #[derive(Debug, Clone, PartialEq, Serialize, serde::Deserialize)]
    struct AStruct {
        foo: String,
        bar: u32,
    }
    #[test]
    fn serialize_struct() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let s = AStruct {
            foo: "hello".to_string(),
            bar: 120,
        };
        let term = context.new_term_ref();
        to_term(&context, &term, &s).unwrap();
        let foo: String = term.get_dict_key("foo").unwrap();
        let bar: u64 = term.get_dict_key("bar").unwrap();
        let tag = term.get_dict_tag().unwrap();
        assert_eq!("hello", foo);
        assert_eq!(120, bar);
        assert_eq!(None, tag);
        let result: AStruct = context.deserialize_from_term(&term).unwrap();
        assert_eq!(s, result);
    }
    #[test]
    fn serialize_tagged_struct() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let s = AStruct {
            foo: "hello".to_string(),
            bar: 120,
        };
        let term = context.new_term_ref();
        to_term_with_config(
            &context,
            &term,
            &s,
            SerializerConfiguration::new().tag_struct_dicts(),
        )
        .unwrap();
        let foo: String = term.get_dict_key("foo").unwrap();
        let bar: u64 = term.get_dict_key("bar").unwrap();
        let tag = term.get_dict_tag().unwrap();
        assert_eq!("hello", foo);
        assert_eq!(120, bar);
        assert_eq!(Some(atom!("AStruct")), tag);
        let result: AStruct = context.deserialize_from_term(&term).unwrap();
        assert_eq!(s, result);
    }
    #[test]
    fn serialize_default_tagged_struct() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let s = AStruct {
            foo: "hello".to_string(),
            bar: 120,
        };
        let term = context.new_term_ref();
        to_term_with_config(
            &context,
            &term,
            &s,
            SerializerConfiguration::new().default_tag("json"),
        )
        .unwrap();
        let foo: String = term.get_dict_key("foo").unwrap();
        let bar: u64 = term.get_dict_key("bar").unwrap();
        let tag = term.get_dict_tag().unwrap();
        assert_eq!("hello", foo);
        assert_eq!(120, bar);
        assert_eq!(Some(atom!("json")), tag);
        let result: AStruct = context.deserialize_from_term(&term).unwrap();
        assert_eq!(s, result);
    }
    #[derive(Debug, Clone, PartialEq, Serialize, serde::Deserialize)]
    enum EnumStruct {
        Variant1,
        Variant2(String),
        Variant3(String, u32),
        Variant4 { foo: String, bar: u32 },
    }
    #[test]
    fn serialize_enum_unit_variant() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let v = EnumStruct::Variant1;
        let term = context.new_term_ref();
        to_term(&context, &term, &v).unwrap();
        assert_eq!("'Variant1'", context.string_from_term(&term).unwrap());
        let r: EnumStruct = context.deserialize_from_term(&term).unwrap();
        assert_eq!(r, v);
    }
    #[test]
    fn serialize_enum_newtype_variant() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let v = EnumStruct::Variant2("hello".to_string());
        let term = context.new_term_ref();
        to_term(&context, &term, &v).unwrap();
        assert_eq!(
            "'Variant2'(\"hello\")",
            context.string_from_term(&term).unwrap()
        );
        let r: EnumStruct = context.deserialize_from_term(&term).unwrap();
        assert_eq!(r, v);
    }
    #[test]
    fn serialize_enum_tuple_variant() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let v = EnumStruct::Variant3("hello".to_string(), 103);
        let term = context.new_term_ref();
        to_term(&context, &term, &v).unwrap();
        assert_eq!(
            "'Variant3'(\"hello\",103)",
            context.string_from_term(&term).unwrap()
        );
        let r: EnumStruct = context.deserialize_from_term(&term).unwrap();
        assert_eq!(r, v);
    }
    #[test]
    fn serialize_enum_struct_variant() {
        let engine = Engine::new();
        let activation = engine.activate();
        let context: Context<_> = activation.into();
        let v = EnumStruct::Variant4 {
            foo: "hello".to_string(),
            bar: 103,
        };
        let term = context.new_term_ref();
        to_term(&context, &term, &v).unwrap();
        let foo: String = term.get_dict_key("foo").unwrap();
        let bar: u64 = term.get_dict_key("bar").unwrap();
        assert_eq!("hello", foo);
        assert_eq!(103, bar);
        let r: EnumStruct = context.deserialize_from_term(&term).unwrap();
        assert_eq!(r, v);
    }
}