pub use zng_unit::*;
mod alignment;
pub use alignment::*;
mod constraints;
pub use constraints::*;
mod factor;
pub use factor::*;
mod grid;
pub use grid::*;
mod length;
pub use length::*;
mod line;
pub use line::*;
mod point;
pub use point::*;
mod rect;
pub use rect::*;
mod resolution;
pub use resolution::*;
mod side_offsets;
pub use side_offsets::*;
mod size;
pub use size::*;
mod transform;
pub use transform::*;
mod vector;
pub use vector::*;
use crate::context::LayoutMask;
macro_rules! impl_length_comp_conversions {
    ($(
        $(#[$docs:meta])*
        fn from($($n:ident : $N:ident),+) -> $For:ty {
            $convert:expr
        }
    )+) => {
        $(
            impl<$($N),+> From<($($N),+)> for $For
            where
                $($N: Into<Length>,)+
            {
                $(#[$docs])*
                fn from(($($n),+) : ($($N),+)) -> Self {
                    $convert
                }
            }
            impl<$($N),+> zng_var::IntoVar<$For> for ($($N),+)
            where
            $($N: Into<Length> + Clone,)+
            {
                type Var = zng_var::LocalVar<$For>;
                $(#[$docs])*
                fn into_var(self) -> Self::Var {
                    zng_var::LocalVar(self.into())
                }
            }
        )+
    };
}
use impl_length_comp_conversions;
pub trait Layout2d {
    type Px: Default;
    fn layout(&self) -> Self::Px {
        self.layout_dft(Default::default())
    }
    fn layout_dft(&self, default: Self::Px) -> Self::Px;
    fn affect_mask(&self) -> LayoutMask;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum LayoutAxis {
    X,
    Y,
    Z,
}
pub trait Layout1d {
    fn layout(&self, axis: LayoutAxis) -> Px {
        self.layout_dft(axis, Px(0))
    }
    fn layout_dft(&self, axis: LayoutAxis, default: Px) -> Px;
    fn layout_x(&self) -> Px {
        self.layout(LayoutAxis::X)
    }
    fn layout_y(&self) -> Px {
        self.layout(LayoutAxis::Y)
    }
    fn layout_z(&self) -> Px {
        self.layout(LayoutAxis::Z)
    }
    fn layout_dft_x(&self, default: Px) -> Px {
        self.layout_dft(LayoutAxis::X, default)
    }
    fn layout_dft_y(&self, default: Px) -> Px {
        self.layout_dft(LayoutAxis::Y, default)
    }
    fn layout_dft_z(&self, default: Px) -> Px {
        self.layout_dft(LayoutAxis::Z, default)
    }
    fn layout_f32(&self, axis: LayoutAxis) -> f32 {
        self.layout_f32_dft(axis, 0.0)
    }
    fn layout_f32_dft(&self, axis: LayoutAxis, default: f32) -> f32;
    fn layout_f32_x(&self) -> f32 {
        self.layout_f32(LayoutAxis::X)
    }
    fn layout_f32_y(&self) -> f32 {
        self.layout_f32(LayoutAxis::Y)
    }
    fn layout_f32_z(&self) -> f32 {
        self.layout_f32(LayoutAxis::Z)
    }
    fn layout_f32_dft_x(&self, default: f32) -> f32 {
        self.layout_f32_dft(LayoutAxis::X, default)
    }
    fn layout_f32_dft_y(&self, default: f32) -> f32 {
        self.layout_f32_dft(LayoutAxis::Y, default)
    }
    fn layout_f32_dft_z(&self, default: f32) -> f32 {
        self.layout_f32_dft(LayoutAxis::Z, default)
    }
    fn affect_mask(&self) -> LayoutMask;
}
#[cfg(test)]
mod tests {
    use std::f32::consts::{PI, TAU};
    use zng_app_context::{AppId, LocalContext};
    use crate::context::{LayoutMetrics, LAYOUT};
    use super::*;
    #[test]
    pub fn zero() {
        all_equal(0.rad(), 0.grad(), 0.deg(), 0.turn());
    }
    #[test]
    pub fn half_circle() {
        all_equal(PI.rad(), 200.grad(), 180.deg(), 0.5.turn())
    }
    #[test]
    pub fn full_circle() {
        all_equal(TAU.rad(), 400.grad(), 360.deg(), 1.turn())
    }
    #[test]
    pub fn one_and_a_half_circle() {
        all_equal((TAU + PI).rad(), 600.grad(), 540.deg(), 1.5.turn())
    }
    #[test]
    pub fn modulo_rad() {
        assert_eq!(PI.rad(), (TAU + PI).rad().modulo());
    }
    #[test]
    pub fn modulo_grad() {
        assert_eq!(200.grad(), 600.grad().modulo());
    }
    #[test]
    pub fn modulo_deg() {
        assert_eq!(180.deg(), 540.deg().modulo());
    }
    #[test]
    pub fn modulo_turn() {
        assert_eq!(0.5.turn(), 1.5.turn().modulo());
    }
    #[test]
    pub fn length_expr_same_unit() {
        let a = Length::from(200);
        let b = Length::from(300);
        let c = a + b;
        assert_eq!(c, 500.dip());
    }
    #[test]
    pub fn length_expr_diff_units() {
        let a = Length::from(200);
        let b = Length::from(10.pct());
        let c = a + b;
        assert_eq!(c, Length::Expr(Box::new(LengthExpr::Add(200.into(), 10.pct().into()))))
    }
    #[test]
    pub fn length_expr_eval() {
        let _app = LocalContext::start_app(AppId::new_unique());
        let l = (Length::from(200) - 100.pct()).abs();
        let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(600), Px(400)), Px(0));
        let l = LAYOUT.with_context(metrics, || l.layout_x());
        assert_eq!(l.0, (200i32 - 600i32).abs());
    }
    #[test]
    pub fn length_expr_clamp() {
        let _app = LocalContext::start_app(AppId::new_unique());
        let l = Length::from(100.pct()).clamp(100, 500);
        assert!(matches!(l, Length::Expr(_)));
        let metrics = LayoutMetrics::new(1.fct(), PxSize::new(Px(200), Px(50)), Px(0));
        LAYOUT.with_context(metrics, || {
            let r = l.layout_x();
            assert_eq!(r.0, 200);
            let r = l.layout_y();
            assert_eq!(r.0, 100);
            LAYOUT.with_constraints(LAYOUT.constraints().with_new_max_x(Px(550)), || {
                let r = l.layout_x();
                assert_eq!(r.0, 500);
            });
        });
    }
    fn all_equal(rad: AngleRadian, grad: AngleGradian, deg: AngleDegree, turn: AngleTurn) {
        assert_eq!(rad, AngleRadian::from(grad));
        assert_eq!(rad, AngleRadian::from(deg));
        assert_eq!(rad, AngleRadian::from(turn));
        assert_eq!(grad, AngleGradian::from(rad));
        assert_eq!(grad, AngleGradian::from(deg));
        assert_eq!(grad, AngleGradian::from(turn));
        assert_eq!(deg, AngleDegree::from(rad));
        assert_eq!(deg, AngleDegree::from(grad));
        assert_eq!(deg, AngleDegree::from(turn));
        assert_eq!(turn, AngleTurn::from(rad));
        assert_eq!(turn, AngleTurn::from(grad));
        assert_eq!(turn, AngleTurn::from(deg));
    }
    #[test]
    fn distance_bounds() {
        assert_eq!(DistanceKey::MAX.distance(), Some(Px::MAX));
        assert_eq!(DistanceKey::MIN.distance(), Some(Px(0)));
    }
    #[test]
    fn orientation_box_above() {
        let a = PxRect::from_size(PxSize::splat(Px(40)));
        let mut b = a;
        b.origin.y = -Px(82);
        let a = a.to_box2d();
        let b = b.to_box2d();
        assert!(Orientation2D::Above.box_is(a, b));
        assert!(!Orientation2D::Below.box_is(a, b));
        assert!(!Orientation2D::Left.box_is(a, b));
        assert!(!Orientation2D::Right.box_is(a, b));
    }
    #[test]
    fn orientation_box_below() {
        let a = PxRect::from_size(PxSize::splat(Px(40)));
        let mut b = a;
        b.origin.y = Px(42);
        let a = a.to_box2d();
        let b = b.to_box2d();
        assert!(!Orientation2D::Above.box_is(a, b));
        assert!(Orientation2D::Below.box_is(a, b));
        assert!(!Orientation2D::Left.box_is(a, b));
        assert!(!Orientation2D::Right.box_is(a, b));
    }
    #[test]
    fn orientation_box_left() {
        let a = PxRect::from_size(PxSize::splat(Px(40)));
        let mut b = a;
        b.origin.x = -Px(82);
        let a = a.to_box2d();
        let b = b.to_box2d();
        assert!(!Orientation2D::Above.box_is(a, b));
        assert!(!Orientation2D::Below.box_is(a, b));
        assert!(Orientation2D::Left.box_is(a, b));
        assert!(!Orientation2D::Right.box_is(a, b));
    }
    #[test]
    fn orientation_box_right() {
        let a = PxRect::from_size(PxSize::splat(Px(40)));
        let mut b = a;
        b.origin.x = Px(42);
        let a = a.to_box2d();
        let b = b.to_box2d();
        assert!(!Orientation2D::Above.box_is(a, b));
        assert!(!Orientation2D::Below.box_is(a, b));
        assert!(!Orientation2D::Left.box_is(a, b));
        assert!(Orientation2D::Right.box_is(a, b));
    }
}