use serde::de::{self, Deserialize, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor};
use serde::ser::{Serialize, SerializeMap, SerializeTuple, Serializer};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::fmt;

use super::{super::CRDT, DeltaVec, StrokeDelta, StrokeID};

impl Serialize for DeltaVec {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        struct DeltaVecSerWrapper<'a>(Vec<((usize, u8, usize), &'a StrokeDelta)>);

        impl<'a> Serialize for DeltaVecSerWrapper<'a> {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer,
            {
                let mut map = serializer.serialize_map(Some(self.0.len()))?;

                for (k, v) in &self.0 {
                    map.serialize_entry(k, v)?;
                }

                map.end()
            }
        }

        let mut buffer: Vec<((usize, u8, usize), &StrokeDelta)> = Vec::with_capacity(self.0.len());

        let mut lut_tmp: HashMap<u128, usize> = HashMap::new();
        let mut lut_out: Vec<u128> = Vec::new();

        for (k, v) in &self.0 {
            let (user, active) = CRDT::canonicalise_uuid(k.user());

            let id = match lut_tmp.entry(user) {
                Occupied(entry) => *entry.get(),
                Vacant(entry) => {
                    let id = lut_out.len();

                    entry.insert(id);
                    lut_out.push(user);

                    id
                }
            };

            buffer.push(((id, active, k.id()), v));
        }

        let mut tuple = serializer.serialize_tuple(2)?;

        tuple.serialize_element(&lut_out)?;
        tuple.serialize_element(&DeltaVecSerWrapper(buffer))?;

        tuple.end()
    }
}

impl<'de> Deserialize<'de> for DeltaVec {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct DeltaVecDeWrapper<'a>(&'a Vec<u128>);

        impl<'de, 'a> DeserializeSeed<'de> for DeltaVecDeWrapper<'a> {
            type Value = HashMap<StrokeID, StrokeDelta>;

            fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
            where
                D: Deserializer<'de>,
            {
                struct DeltaVecDeWrapperVisitor<'a>(&'a Vec<u128>);

                impl<'de, 'a> Visitor<'de> for DeltaVecDeWrapperVisitor<'a> {
                    type Value = HashMap<StrokeID, StrokeDelta>;

                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                        write!(formatter, "struct DeltaVec")
                    }

                    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
                    where
                        M: MapAccess<'de>,
                    {
                        let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(0));

                        while let Some(((id, active, idx), value)) =
                            access.next_entry()?: Option<((usize, u8, usize), StrokeDelta)>
                        {
                            let user = match self.0.get(id) {
                                Some(user) => user,
                                None => {
                                    return Err(de::Error::custom(
                                        "invalid canonincal user lookup id",
                                    ))
                                }
                            };

                            map.insert(
                                StrokeID::new(CRDT::decanonicalise_uuid(*user, active), idx),
                                value,
                            );
                        }

                        Ok(map)
                    }
                }

                deserializer.deserialize_map(DeltaVecDeWrapperVisitor(self.0))
            }
        }

        struct DeltaVecVisitor;

        impl<'de> Visitor<'de> for DeltaVecVisitor {
            type Value = DeltaVec;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("struct DeltaVec")
            }

            fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
            where
                V: SeqAccess<'de>,
            {
                let lut_in: Vec<u128> = seq
                    .next_element()?
                    .ok_or_else(|| de::Error::invalid_length(0, &self))?;

                let deltas = seq
                    .next_element_seed(DeltaVecDeWrapper(&lut_in))?
                    .ok_or_else(|| de::Error::invalid_length(1, &self))?;

                Ok(DeltaVec(deltas))
            }
        }

        deserializer.deserialize_tuple(2, DeltaVecVisitor)
    }
}