use crate::UnclassifiedIris;
pub trait Iris {
    fn data(&self) -> &UnclassifiedIris;
    fn dist_sq(&self, other: &dyn Iris) -> f32 {
        let lhs = self.data();
        let rhs = other.data();
        [
            (lhs.sepal_length - rhs.sepal_length),
            (lhs.sepal_width - rhs.sepal_width),
            (lhs.petal_length - rhs.petal_length),
            (lhs.petal_width - rhs.petal_width),
        ]
        .into_iter()
        .map(|x| x * x)
        .sum()
    }
}
pub mod species {
    use serde::{Deserialize, Deserializer};
    use strum_macros as sm;
    #[derive(
        Clone,
        Copy,
        num_enum::TryFromPrimitive,
        sm::AsRefStr,
        sm::IntoStaticStr,
        Debug,
        sm::Display,
        PartialEq,
        Eq,
        Hash,
        num_enum::IntoPrimitive,
    )]
    #[repr(u8)]
    pub enum IrisSpecies {
        Setosa = 0,
        Versicolor = 1,
        Virginica = 2,
    }
    impl<'de> Deserialize<'de> for IrisSpecies {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            let iris_code = u8::deserialize(deserializer)?;
            num_enum::TryFromPrimitive::try_from_primitive(iris_code)
                .map_err(serde::de::Error::custom)
        }
    }
}
pub mod unclassified {
    use crate::Iris;
    #[derive(serde::Deserialize, Debug, Clone, Copy, PartialEq, tabled::Tabled)]
    pub struct UnclassifiedIris {
        pub sepal_length: f32,
        pub sepal_width: f32,
        pub petal_length: f32,
        pub petal_width: f32,
    }
    impl Iris for UnclassifiedIris {
        fn data(&self) -> &UnclassifiedIris {
            self
        }
    }
}
pub mod classified {
    use derive_more::{Constructor, Deref, DerefMut};
    use num_enum::TryFromPrimitive;
    use serde::{Deserialize, Deserializer};
    use tabled::Tabled;
    use crate::{Iris, IrisSpecies, UnclassifiedIris};
    #[derive(Constructor, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
    pub struct ClassifiedIris {
        #[deref]
        #[deref_mut]
        pub parameters: UnclassifiedIris,
        pub classification: IrisSpecies,
    }
    impl Iris for ClassifiedIris {
        fn data(&self) -> &UnclassifiedIris {
            &self.parameters
        }
    }
    impl Tabled for ClassifiedIris {
        const LENGTH: usize = <UnclassifiedIris as Tabled>::LENGTH + 1;
        fn fields(&self) -> Vec<std::borrow::Cow<'_, str>> {
            let mut parameters = self.parameters.fields();
            parameters.push(self.classification.as_ref().into());
            parameters
        }
        fn headers() -> Vec<std::borrow::Cow<'static, str>> {
            let mut parameters = <UnclassifiedIris as Tabled>::headers();
            parameters.push("species".into());
            parameters
        }
    }
    impl<'de> Deserialize<'de> for ClassifiedIris {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            struct IrisVisitor;
            impl<'de> serde::de::Visitor<'de> for IrisVisitor {
                type Value = ClassifiedIris;
                fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                    formatter.write_str("struct ClassifiedIris")
                }
                fn visit_seq<V>(self, mut seq: V) -> Result<ClassifiedIris, V::Error>
                where
                    V: serde::de::SeqAccess<'de>,
                {
                    let sepal_length = seq
                        .next_element()?
                        .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
                    let sepal_width = seq
                        .next_element()?
                        .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
                    let petal_length = seq
                        .next_element()?
                        .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
                    let petal_width = seq
                        .next_element()?
                        .ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
                    let classification_code: u8 = seq
                        .next_element()?
                        .ok_or_else(|| serde::de::Error::invalid_length(4, &self))?;
                    let classification = IrisSpecies::try_from_primitive(classification_code)
                        .map_err(serde::de::Error::custom)?;
                    Ok(ClassifiedIris {
                        parameters: UnclassifiedIris {
                            sepal_length,
                            sepal_width,
                            petal_length,
                            petal_width,
                        },
                        classification,
                    })
                }
            }
            deserializer.deserialize_tuple(5, IrisVisitor)
        }
    }
}