// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use alloc::format;
use alloc::string::ToString;
use core::convert::TryFrom;
use smallvec::SmallVec;

pub mod reference {
    use super::super::reference::Skeleton;
    use super::*;

    use ::serde::{Serialize, ser};
    use serde::{Deserialize, Deserializer, de};
    /// This is an implementation of the serde deserialization visitor pattern.
    pub(super) struct DeserializeSkeletonUTS35String;

    impl de::Visitor<'_> for DeserializeSkeletonUTS35String {
        type Value = Skeleton;

        fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
            write!(formatter, "Expected to find a valid skeleton.")
        }

        /// A [`Skeleton`] serialized into a string follows UTS-35.
        /// <https://unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table>
        /// This string consists of a symbol that is repeated N times. This string is
        /// deserialized here into the Skeleton format which is used in memory
        /// when working with formatting datetimes.
        fn visit_str<E>(self, skeleton_string: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            Skeleton::try_from(skeleton_string).map_err(|err| {
                de::Error::invalid_value(
                    de::Unexpected::Other(&format!("{skeleton_string:?} {err}")),
                    &"field symbols representing a skeleton",
                )
            })
        }
    }

    impl<'de> Deserialize<'de> for Skeleton {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            if deserializer.is_human_readable() {
                deserializer.deserialize_str(DeserializeSkeletonUTS35String)
            } else {
                let sv = SmallVec::deserialize(deserializer)?;
                Ok(sv.into())
            }
        }
    }

    impl Serialize for Skeleton {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: ser::Serializer,
        {
            if serializer.is_human_readable() {
                // Serialize into the UTS 35 string representation.
                let string = self.to_string();
                serializer.serialize_str(&string)
            } else {
                self.0.serialize(serializer)
            }
        }
    }
}

pub mod runtime {
    use super::super::runtime::Skeleton;
    use super::*;

    use ::serde::{Serialize, ser};
    use serde::{Deserialize, Deserializer, de};
    use zerovec::ZeroVec;
    /// This is an implementation of the serde deserialization visitor pattern.
    struct DeserializeSkeletonUTS35String;

    impl<'de> de::Visitor<'de> for DeserializeSkeletonUTS35String {
        type Value = Skeleton<'de>;

        fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
            write!(formatter, "Expected to find a valid skeleton.")
        }

        fn visit_borrowed_str<E>(self, skeleton_string: &'de str) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            let reference_deserializer = reference::DeserializeSkeletonUTS35String;
            let skeleton = reference_deserializer.visit_str(skeleton_string)?;

            Ok(skeleton.into())
        }
    }

    impl<'de> Deserialize<'de> for Skeleton<'de> {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            if deserializer.is_human_readable() {
                deserializer.deserialize_str(DeserializeSkeletonUTS35String)
            } else {
                let zv = ZeroVec::deserialize(deserializer)?;
                Ok(zv.into())
            }
        }
    }

    impl Serialize for Skeleton<'_> {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: ser::Serializer,
        {
            if serializer.is_human_readable() {
                // Serialize into the UTS 35 string representation.
                let string = self.to_string();
                serializer.serialize_str(&string)
            } else {
                self.0.serialize(serializer)
            }
        }
    }
}
