use serde::{
    de::{self, Visitor},
    Deserializer,
};

struct StringifiedStorageTypeVisitor;

impl Visitor<'_> for StringifiedStorageTypeVisitor {
    type Value = Option<i32>;

    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str("an integer or name representing a StorageType")
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        match v {
            "MultiFileGame" => Ok(Some(crate::retrom::StorageType::MultiFileGame as i32)),
            "SingleFileGame" => Ok(Some(crate::retrom::StorageType::SingleFileGame as i32)),
            _ => Err(de::Error::unknown_variant(
                v,
                &["MultiFileGame", "SingleFileGame"],
            )),
        }
    }

    fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        Ok(Some(v as i32))
    }
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<i32>, D::Error>
where
    D: Deserializer<'de>,
{
    deserializer.deserialize_any(StringifiedStorageTypeVisitor)
}

pub fn serialize<S>(value: &Option<i32>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: serde::Serializer,
{
    use crate::retrom::StorageType;

    let value: Option<StorageType> = value.map(|v| StorageType::try_from(v).unwrap_or_default());

    match value {
        Some(storage_type) => match storage_type {
            StorageType::MultiFileGame => serializer.serialize_i32(0),
            StorageType::SingleFileGame => serializer.serialize_i32(1),
            StorageType::Custom => serializer.serialize_i32(2),
        },
        None => serializer.serialize_none(),
    }
}
