use super::UnknownFields;
use crate::tags::{self, PrimaryTag};
use crate::{
    Deserialize, DeserializeError, Deserializer, Serialize, SerializeError, SerializedValue,
    Serializer,
};

#[derive(Debug, Clone, PartialEq, Eq)]
struct Old {
    field1: u32,
    fallback: UnknownFields,
}

impl PrimaryTag for Old {
    type Tag = tags::Value;
}

impl Serialize<tags::Value> for Old {
    fn serialize(self, serializer: Serializer) -> Result<(), SerializeError> {
        serializer.serialize::<tags::Value>(&self)
    }
}

impl Serialize<tags::Value> for &Old {
    fn serialize(self, serializer: Serializer) -> Result<(), SerializeError> {
        let mut serializer = serializer.serialize_struct2()?;

        serializer.serialize::<tags::U32>(1u32, &self.field1)?;
        serializer.serialize_unknown_fields(&self.fallback)?;

        serializer.finish()
    }
}

impl Deserialize<tags::Value> for Old {
    fn deserialize(deserializer: Deserializer) -> Result<Self, DeserializeError> {
        let mut deserializer = deserializer.deserialize_struct()?;

        let mut field1 = None;

        while let Some(deserializer) = deserializer.deserialize()? {
            match deserializer.id() {
                1 => field1 = deserializer.deserialize::<tags::U32, _>().map(Some)?,
                _ => deserializer.add_to_unknown_fields()?,
            }
        }

        deserializer.finish_with(|fallback| {
            Ok(Self {
                field1: field1.ok_or(DeserializeError::InvalidSerialization)?,
                fallback,
            })
        })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct New {
    field1: u32,
    field2: Option<u32>,
    fallback: UnknownFields,
}

impl PrimaryTag for New {
    type Tag = tags::Value;
}

impl Serialize<tags::Value> for New {
    fn serialize(self, serializer: Serializer) -> Result<(), SerializeError> {
        serializer.serialize::<tags::Value>(&self)
    }
}

impl Serialize<tags::Value> for &New {
    fn serialize(self, serializer: Serializer) -> Result<(), SerializeError> {
        let mut serializer = serializer.serialize_struct2()?;

        serializer.serialize::<tags::U32>(1u32, &self.field1)?;
        serializer.serialize_if_some::<tags::Option<tags::U32>>(2u32, &self.field2)?;
        serializer.serialize_unknown_fields(&self.fallback)?;

        serializer.finish()
    }
}

impl Deserialize<tags::Value> for New {
    fn deserialize(deserializer: Deserializer) -> Result<Self, DeserializeError> {
        let mut deserializer = deserializer.deserialize_struct()?;

        let mut field1 = None;
        let mut field2 = None;

        while let Some(deserializer) = deserializer.deserialize()? {
            match deserializer.id() {
                1 => field1 = deserializer.deserialize::<tags::U32, _>().map(Some)?,
                2 => field2 = deserializer.deserialize::<tags::Option<tags::U32>, _>()?,
                _ => deserializer.add_to_unknown_fields()?,
            }
        }

        deserializer.finish_with(|fallback| {
            Ok(Self {
                field1: field1.ok_or(DeserializeError::InvalidSerialization)?,
                field2,
                fallback,
            })
        })
    }
}

#[test]
fn old_new_old_roundtrip() {
    let old = Old {
        field1: 1,
        fallback: UnknownFields::new(),
    };

    let serialized = SerializedValue::serialize(&old).unwrap();

    let new = serialized.deserialize::<New>().unwrap();
    assert_eq!(new.field1, 1);
    assert_eq!(new.field2, None);
    assert!(new.fallback.is_empty());

    let serialized = SerializedValue::serialize(&new).unwrap();

    let old2 = serialized.deserialize::<Old>().unwrap();
    assert_eq!(old2, old);
}

#[test]
fn new_old_new_roundtrip1() {
    let new = New {
        field1: 1,
        field2: None,
        fallback: UnknownFields::new(),
    };

    let serialized = SerializedValue::serialize(&new).unwrap();

    let old = serialized.deserialize::<Old>().unwrap();
    assert_eq!(old.field1, 1);
    assert!(old.fallback.is_empty());
    assert!(!old.fallback.has_fields_set());

    let serialized = SerializedValue::serialize(&old).unwrap();

    let new2 = serialized.deserialize::<New>().unwrap();
    assert_eq!(new2, new);
}

#[test]
fn new_old_new_roundtrip2() {
    let new = New {
        field1: 1,
        field2: Some(2),
        fallback: UnknownFields::new(),
    };

    let serialized = SerializedValue::serialize(&new).unwrap();

    let old = serialized.deserialize::<Old>().unwrap();
    assert_eq!(old.field1, 1);
    assert_eq!(old.fallback.len(), 1);
    assert!(old.fallback.has_fields_set());

    let serialized = SerializedValue::serialize(&old).unwrap();

    let new2 = serialized.deserialize::<New>().unwrap();
    assert_eq!(new2, new);
}
