use std::ops::Range;
use std::rc::Rc;
use std::cell::RefCell;
use super::ImplToFromPaxAny;
use super::Numeric;
use super::PaxAny;
use super::PaxValue;
use super::ToFromPaxAny;
use super::ToPaxValue;
use crate::impl_to_from_pax_value;
use crate::math::Space;
use crate::math::Transform2;
use crate::math::Vector2;
use crate::properties::PropertyValue;
use crate::Color;
use crate::ColorChannel;
use crate::Fill;
use crate::GradientStop;
use crate::LinearGradient;
use crate::Percent;
use crate::Property;
use crate::RadialGradient;
use crate::Rotation;
use crate::Size;
use crate::Stroke;
use crate::Transform2D;
impl_to_from_pax_value!(bool, PaxValue::Bool);
impl_to_from_pax_value!(u8, PaxValue::Numeric, Numeric::U8);
impl_to_from_pax_value!(u16, PaxValue::Numeric, Numeric::U16);
impl_to_from_pax_value!(u32, PaxValue::Numeric, Numeric::U32);
impl_to_from_pax_value!(u64, PaxValue::Numeric, Numeric::U64);
impl_to_from_pax_value!(i8, PaxValue::Numeric, Numeric::I8);
impl_to_from_pax_value!(i16, PaxValue::Numeric, Numeric::I16);
impl_to_from_pax_value!(i32, PaxValue::Numeric, Numeric::I32);
impl_to_from_pax_value!(i64, PaxValue::Numeric, Numeric::I64);
impl_to_from_pax_value!(f32, PaxValue::Numeric, Numeric::F32);
impl_to_from_pax_value!(f64, PaxValue::Numeric, Numeric::F64);
impl_to_from_pax_value!(isize, PaxValue::Numeric, Numeric::ISize);
impl_to_from_pax_value!(usize, PaxValue::Numeric, Numeric::USize);
impl ImplToFromPaxAny for () {}
impl<F: Space, T: Space> ImplToFromPaxAny for Transform2<F, T> {}
impl_to_from_pax_value!(String, PaxValue::String);
impl_to_from_pax_value!(Numeric, PaxValue::Numeric);
impl_to_from_pax_value!(Size, PaxValue::Size);
impl_to_from_pax_value!(Color, PaxValue::Color);
impl_to_from_pax_value!(Rotation, PaxValue::Rotation);
impl_to_from_pax_value!(Percent, PaxValue::Percent);
impl<T: ToPaxValue> ToPaxValue for Vec<T> {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Vec(
            self.into_iter()
                .map(|v| v.to_pax_value())
                .collect::<Vec<_>>(),
        )
    }
}
impl<T: ToPaxValue> ToPaxValue for Option<T> {
    fn to_pax_value(self) -> PaxValue {
        match self {
            Some(v) => PaxValue::Option(Box::new(Some(v.to_pax_value()))),
            None => PaxValue::Option(Box::new(None)),
        }
    }
}
impl<T: ToPaxValue> ToPaxValue for Range<T> {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Range(
            Box::new(self.start.to_pax_value()),
            Box::new(self.end.to_pax_value()),
        )
    }
}
impl<T: ToPaxValue + Clone> ToPaxValue for Rc<RefCell<T>> {
    fn to_pax_value(self) -> PaxValue {
        crate::borrow!(self).clone().to_pax_value()
    }
}
impl ToPaxValue for PaxValue {
    fn to_pax_value(self) -> PaxValue {
        self
    }
}
impl<T: ToPaxValue + PropertyValue> ToPaxValue for Property<T> {
    fn to_pax_value(self) -> PaxValue {
        self.get().to_pax_value()
    }
}
impl ToPaxValue for Fill {
    fn to_pax_value(self) -> PaxValue {
        match self {
            Fill::Solid(color) => PaxValue::Enum(
                "Fill".to_string(),
                "Solid".to_string(),
                vec![color.to_pax_value()],
            ),
            Fill::LinearGradient(gradient) => PaxValue::Enum(
                "Fill".to_string(),
                "LinearGradient".to_string(),
                vec![gradient.to_pax_value()],
            ),
            Fill::RadialGradient(gradient) => PaxValue::Enum(
                "Fill".to_string(),
                "RadialGradient".to_string(),
                vec![gradient.to_pax_value()],
            ),
        }
    }
}
impl ToPaxValue for ColorChannel {
    fn to_pax_value(self) -> PaxValue {
        match self {
            ColorChannel::Rotation(rot) => PaxValue::Enum(
                "ColorChannel".to_string(),
                "Rotation".to_string(),
                vec![rot.to_pax_value()],
            ),
            ColorChannel::Percent(perc) => PaxValue::Enum(
                "ColorChannel".to_string(),
                "Percent".to_string(),
                vec![perc.to_pax_value()],
            ),
            ColorChannel::Integer(num) => PaxValue::Enum(
                "ColorChannel".to_string(),
                "Integer".to_string(),
                vec![num.to_pax_value()],
            ),
        }
    }
}
impl ToPaxValue for Stroke {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![
                ("color".to_string(), self.color.to_pax_value()),
                ("width".to_string(), self.width.to_pax_value()),
            ]
            .into_iter()
            .collect(),
        )
    }
}
impl ToPaxValue for GradientStop {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![
                ("position".to_string(), self.position.to_pax_value()),
                ("color".to_string(), self.color.to_pax_value()),
            ]
            .into_iter()
            .collect(),
        )
    }
}
impl ToPaxValue for LinearGradient {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![
                (
                    "start".to_string(),
                    PaxValue::Vec(vec![
                        self.start.0.to_pax_value(),
                        self.start.1.to_pax_value(),
                    ]),
                ),
                (
                    "end".to_string(),
                    PaxValue::Vec(vec![self.end.0.to_pax_value(), self.end.1.to_pax_value()]),
                ),
                ("stops".to_string(), self.stops.to_pax_value()),
            ]
            .into_iter()
            .collect(),
        )
    }
}
impl ToPaxValue for RadialGradient {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![
                (
                    "start".to_string(),
                    PaxValue::Vec(vec![
                        self.start.0.to_pax_value(),
                        self.start.1.to_pax_value(),
                    ]),
                ),
                (
                    "end".to_string(),
                    PaxValue::Vec(vec![self.end.0.to_pax_value(), self.end.1.to_pax_value()]),
                ),
                ("radius".to_string(), self.radius.to_pax_value()),
                ("stops".to_string(), self.stops.to_pax_value()),
            ]
            .into_iter()
            .collect(),
        )
    }
}
impl ToPaxValue for Transform2D {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![
                (
                    "previous".to_string(),
                    self.previous
                        .map(|p| p.to_pax_value())
                        .unwrap_or(PaxValue::Option(Box::new(None))),
                ),
                (
                    "rotate".to_string(),
                    self.rotate
                        .map(|r| r.to_pax_value())
                        .unwrap_or(PaxValue::Option(Box::new(None))),
                ),
                (
                    "translate".to_string(),
                    self.translate
                        .map(|t| PaxValue::Option(Box::new(Some(t.to_vec().to_pax_value()))))
                        .unwrap_or(PaxValue::Option(Box::new(None))),
                ),
                (
                    "anchor".to_string(),
                    self.anchor
                        .map(|a| PaxValue::Option(Box::new(Some(a.to_vec().to_pax_value()))))
                        .unwrap_or(PaxValue::Option(Box::new(None))),
                ),
                (
                    "scale".to_string(),
                    self.scale
                        .map(|s| PaxValue::Option(Box::new(Some(s.to_vec().to_pax_value()))))
                        .unwrap_or(PaxValue::Option(Box::new(None))),
                ),
                (
                    "skew".to_string(),
                    self.skew
                        .map(|s| PaxValue::Option(Box::new(Some(s.to_vec().to_pax_value()))))
                        .unwrap_or(PaxValue::Option(Box::new(None))),
                ),
            ]
            .into_iter()
            .collect(),
        )
    }
}
impl ToPaxValue for Transform2 {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![(
                "m".to_string(),
                PaxValue::Vec(self.m.iter().map(|v| v.to_pax_value()).collect::<Vec<_>>()),
            )]
            .into_iter()
            .collect(),
        )
    }
}
impl ToPaxValue for Vector2 {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![
                ("x".to_string(), self.x.to_pax_value()),
                ("y".to_string(), self.y.to_pax_value()),
            ]
            .into_iter()
            .collect(),
        )
    }
}