use serde::{
    ser::{self, SerializeMap, SerializeSeq},
    Serializer,
};
use crate::{error::Ensure, ser::formatter::JsoncFormatter};
use super::{jsonc::JsoncSerializer, map::MapSerializer, seq::SeqSerializer};
pub(crate) enum Delegate<S, M> {
    Seq(S),
    Map(M),
}
pub struct EnumSerializer<'a, W, F>
where
    F: JsoncFormatter,
{
    delegate: Delegate<SeqSerializer<'a, W, F>, MapSerializer<'a, W, F>>,
}
impl<'a, W, F> EnumSerializer<'a, W, F>
where
    W: std::io::Write,
    F: JsoncFormatter,
{
    pub fn start_newtype_variant<T: ?Sized>(
        serializer: &mut JsoncSerializer<W, F>,
        variant: &'static str,
        value: &T,
    ) -> crate::Result<()>
    where
        T: ser::Serialize,
    {
        serializer.formatter.write_object_start(&mut serializer.write)?;
        serializer.formatter.write_object_key_start(&mut serializer.write, 0, Some(1))?;
        serializer.serialize_str(variant)?;
        serializer.formatter.write_object_key_end(&mut serializer.write, 0, Some(1))?;
        serializer.formatter.write_object_value_start(&mut serializer.write, 0, Some(1))?;
        value.serialize(&mut *serializer)?;
        serializer.formatter.write_object_value_end(&mut serializer.write, 0, Some(1))?;
        serializer.formatter.write_object_end(&mut serializer.write)
    }
    pub fn start_tuple_variant(
        serializer: &'a mut JsoncSerializer<W, F>,
        variant: &'static str,
        len: usize,
    ) -> crate::Result<Self> {
        Self::start(serializer, variant, len, Delegate::<_, ()>::Seq(()))
    }
    pub fn start_struct_variant(
        serializer: &'a mut JsoncSerializer<W, F>,
        variant: &'static str,
        len: usize,
    ) -> crate::Result<Self> {
        Self::start(serializer, variant, len, Delegate::<(), _>::Map(()))
    }
    fn start<S, M>(
        serializer: &'a mut JsoncSerializer<W, F>,
        variant: &'static str,
        len: usize,
        delegate_type: Delegate<S, M>,
    ) -> crate::Result<Self> {
        serializer.formatter.write_object_start(&mut serializer.write)?;
        serializer.formatter.write_object_key_start(&mut serializer.write, 0, Some(1))?;
        serializer.serialize_str(variant)?;
        serializer.formatter.write_object_key_end(&mut serializer.write, 0, Some(1))?;
        let delegate = match delegate_type {
            Delegate::Seq(_) => Delegate::Seq(SeqSerializer::start(serializer, Some(len))?),
            Delegate::Map(_) => Delegate::Map(MapSerializer::start(serializer, Some(len))?),
        };
        Ok(Self { delegate })
    }
}
impl<'a, W, F> ser::SerializeTupleVariant for EnumSerializer<'a, W, F>
where
    W: std::io::Write,
    F: JsoncFormatter,
{
    type Ok = ();
    type Error = crate::Error;
    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<Self::Ok, Self::Error>
    where
        T: ser::Serialize,
    {
        match &mut self.delegate {
            Delegate::Seq(seq) => seq.serialize_element(value),
            Delegate::Map(_) => Err(Ensure::SeqLikeVariant)?,
        }
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        match self.delegate {
            Delegate::Seq(seq) => {
                seq.serializer.formatter.write_array_end(&mut seq.serializer.write)?;
                seq.serializer.formatter.write_object_end(&mut seq.serializer.write)
            }
            Delegate::Map(_) => Err(Ensure::SeqLikeVariant)?,
        }
    }
}
impl<'a, W, F> ser::SerializeStructVariant for EnumSerializer<'a, W, F>
where
    W: std::io::Write,
    F: JsoncFormatter,
{
    type Ok = ();
    type Error = crate::Error;
    fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<Self::Ok, Self::Error>
    where
        T: ser::Serialize,
    {
        match &mut self.delegate {
            Delegate::Seq(_) => Err(Ensure::MapLikeVariant)?,
            Delegate::Map(map) => map.serialize_entry(key, value),
        }
    }
    fn end(self) -> Result<Self::Ok, Self::Error> {
        match self.delegate {
            Delegate::Seq(_) => Err(Ensure::MapLikeVariant)?,
            Delegate::Map(map) => {
                map.serializer.formatter.write_object_end(&mut map.serializer.write)?;
                map.serializer.formatter.write_object_end(&mut map.serializer.write)
            }
        }
    }
}