use crate::{Color, ColorChannel, Fill, Percent, Rotation, Size, Stroke, Transform2D};
use std::any::Any;
use self::numeric::Numeric;
pub use coercion_impls::CoercionRules;
mod arithmetic;
mod coercion_impls;
mod macros;
pub mod numeric;
mod to_from_impls;
#[derive(Debug)]
pub enum PaxValue {
    Bool(bool),
    Numeric(Numeric),
    String(String),
    Transform2D(Transform2D),
    Size(Size),
    Percent(Percent),
    Color(Color),
    ColorChannel(ColorChannel),
    Rotation(Rotation),
    Fill(Fill),
    Stroke(Stroke),
    Vec(Vec<PaxAny>),
    Component {},
}
pub enum PaxAny {
    Builtin(PaxValue),
    Any(Box<dyn Any>),
}
impl std::fmt::Debug for PaxAny {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "PaxAny {{ .. }}")
    }
}
impl PaxAny {
    pub fn try_coerce<T: ToFromPaxAny + CoercionRules + 'static>(self) -> Result<T, String> {
        let res = match self {
            PaxAny::Builtin(pax_type) => T::try_coerce(pax_type),
            PaxAny::Any(any) => any.downcast().map(|v| *v).map_err(|_| {
                format!(
                    "tried to coerce PaxAny into {} which wasn't the underlying type",
                    std::any::type_name::<T>()
                )
            }),
        };
        res
    }
}
pub trait ToFromPaxValue
where
    Self: Sized + 'static,
{
    fn to_pax_value(self) -> PaxValue;
    fn from_pax_value(pax_value: PaxValue) -> Result<Self, String>;
    fn ref_from_pax_value(pax_value: &PaxValue) -> Result<&Self, String>;
    fn mut_from_pax_value(pax_value: &mut PaxValue) -> Result<&mut Self, String>;
}
pub trait ToFromPaxAny
where
    Self: Sized + 'static,
{
    fn to_pax_any(self) -> PaxAny;
    fn from_pax_any(pax_any: PaxAny) -> Result<Self, String>;
    fn ref_from_pax_any(pax_any: &PaxAny) -> Result<&Self, String>;
    fn mut_from_pax_any(pax_any: &mut PaxAny) -> Result<&mut Self, String>;
}
impl ToFromPaxAny for PaxValue {
    fn to_pax_any(self) -> PaxAny {
        PaxAny::Builtin(self)
    }
    fn from_pax_any(pax_any: PaxAny) -> Result<Self, String> {
        match pax_any {
            PaxAny::Builtin(val) => Ok(val),
            PaxAny::Any(_) => Err("tried to unwrap any as builtin".to_string()),
        }
    }
    fn ref_from_pax_any(pax_any: &PaxAny) -> Result<&Self, String> {
        match pax_any {
            PaxAny::Builtin(val) => Ok(val),
            PaxAny::Any(_) => Err("tried to unwrap any as builtin".to_string()),
        }
    }
    fn mut_from_pax_any(pax_any: &mut PaxAny) -> Result<&mut Self, String> {
        match pax_any {
            PaxAny::Builtin(val) => Ok(val),
            PaxAny::Any(_) => Err("tried to unwrap any as builtin".to_string()),
        }
    }
}
pub trait ImplToFromPaxAny: 'static {}
impl<T: ImplToFromPaxAny> ToFromPaxAny for T {
    fn to_pax_any(self) -> PaxAny {
        PaxAny::Any(Box::new(self) as Box<dyn Any>)
    }
    fn from_pax_any(pax_any: PaxAny) -> Result<Self, String> {
        match pax_any {
            PaxAny::Any(v) => Ok(*v
                .downcast::<Self>()
                .map_err(|_e| "downcast failed".to_string())?),
            _ => Err("wasn't any".to_string()),
        }
    }
    fn ref_from_pax_any(pax_any: &PaxAny) -> Result<&Self, String> {
        match pax_any {
            PaxAny::Any(v) => v
                .downcast_ref::<Self>()
                .ok_or_else(|| "downcast failed".to_string()),
            _ => Err("wasn't any".to_string()),
        }
    }
    fn mut_from_pax_any(pax_any: &mut PaxAny) -> Result<&mut Self, String> {
        match pax_any {
            PaxAny::Any(v) => v
                .downcast_mut::<Self>()
                .ok_or_else(|| "downcast failed".to_string()),
            _ => Err("wasn't any".to_string()),
        }
    }
}
impl ToFromPaxAny for PaxAny {
    fn to_pax_any(self) -> PaxAny {
        self
    }
    fn from_pax_any(pax_any: PaxAny) -> Result<Self, String> {
        Ok(pax_any)
    }
    fn ref_from_pax_any(pax_any: &PaxAny) -> Result<&Self, String> {
        Ok(pax_any)
    }
    fn mut_from_pax_any(pax_any: &mut PaxAny) -> Result<&mut Self, String> {
        Ok(pax_any)
    }
}