use super::{
    about_eq, ByteLength, ByteUnits, Dip, DipToPx, Factor, FactorPercent, FactorUnits, LayoutAxis, Px, EQ_EPSILON, EQ_EPSILON_100,
};
use std::{fmt, mem, ops};
use zng_var::{
    animation::{easing::EasingStep, Transitionable},
    impl_from_and_into_var,
};
use crate::context::{LayoutMask, LAYOUT};
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub enum Length {
    Default,
    Dip(Dip),
    Px(Px),
    Pt(f32),
    Factor(Factor),
    Leftover(Factor),
    Em(Factor),
    RootEm(Factor),
    ViewportWidth(Factor),
    ViewportHeight(Factor),
    ViewportMin(Factor),
    ViewportMax(Factor),
    DipF32(f32),
    PxF32(f32),
    Expr(Box<LengthExpr>),
}
impl<L: Into<Length>> ops::Add<L> for Length {
    type Output = Length;
    fn add(self, rhs: L) -> Self::Output {
        use Length::*;
        let rhs = rhs.into();
        if self.is_zero() == Some(true) {
            return rhs; } else if rhs.is_zero() == Some(true) {
            return self; }
        match (self, rhs) {
            (Dip(a), Dip(b)) => Dip(a + b),
            (Px(a), Px(b)) => Px(a + b),
            (Pt(a), Pt(b)) => Pt(a + b),
            (Factor(a), Factor(b)) => Factor(a + b),
            (Leftover(a), Leftover(b)) => Leftover(a + b),
            (Em(a), Em(b)) => Em(a + b),
            (RootEm(a), RootEm(b)) => RootEm(a + b),
            (ViewportWidth(a), ViewportWidth(b)) => ViewportWidth(a + b),
            (ViewportHeight(a), ViewportHeight(b)) => ViewportHeight(a + b),
            (ViewportMin(a), ViewportMin(b)) => ViewportMin(a + b),
            (ViewportMax(a), ViewportMax(b)) => ViewportMax(a + b),
            (PxF32(a), PxF32(b)) => PxF32(a + b),
            (DipF32(a), DipF32(b)) => DipF32(a + b),
            (Px(a), PxF32(b)) | (PxF32(b), Px(a)) => PxF32(a.0 as f32 + b),
            (Dip(a), DipF32(b)) | (DipF32(b), Dip(a)) => DipF32(a.to_f32() + b),
            (a, b) => LengthExpr::Add(a, b).to_length_checked(),
        }
    }
}
impl<L: Into<Length>> ops::AddAssign<L> for Length {
    fn add_assign(&mut self, rhs: L) {
        let lhs = mem::take(self);
        *self = lhs + rhs.into();
    }
}
impl<L: Into<Length>> ops::Sub<L> for Length {
    type Output = Length;
    fn sub(self, rhs: L) -> Self::Output {
        use Length::*;
        let rhs = rhs.into();
        if rhs.is_zero() == Some(true) {
            return self; } else if self.is_zero() == Some(true) {
            return -rhs; }
        match (self, rhs) {
            (Dip(a), Dip(b)) => Dip(a - b),
            (Px(a), Px(b)) => Px(a - b),
            (Pt(a), Pt(b)) => Pt(a - b),
            (Factor(a), Factor(b)) => Factor(a - b),
            (Leftover(a), Leftover(b)) => Leftover(a - b),
            (Em(a), Em(b)) => Em(a - b),
            (RootEm(a), RootEm(b)) => RootEm(a - b),
            (ViewportWidth(a), ViewportWidth(b)) => ViewportWidth(a - b),
            (ViewportHeight(a), ViewportHeight(b)) => ViewportHeight(a - b),
            (ViewportMin(a), ViewportMin(b)) => ViewportMin(a - b),
            (ViewportMax(a), ViewportMax(b)) => ViewportMax(a - b),
            (PxF32(a), PxF32(b)) => PxF32(a - b),
            (DipF32(a), DipF32(b)) => DipF32(a - b),
            (Px(a), PxF32(b)) => PxF32(a.0 as f32 - b),
            (PxF32(a), Px(b)) => PxF32(a - b.0 as f32),
            (Dip(a), DipF32(b)) => DipF32(a.to_f32() - b),
            (DipF32(a), Dip(b)) => DipF32(a - b.to_f32()),
            (a, b) => LengthExpr::Sub(a, b).to_length_checked(),
        }
    }
}
impl<L: Into<Length>> ops::SubAssign<L> for Length {
    fn sub_assign(&mut self, rhs: L) {
        let lhs = mem::take(self);
        *self = lhs - rhs.into();
    }
}
impl<F: Into<Factor>> ops::Mul<F> for Length {
    type Output = Length;
    fn mul(self, rhs: F) -> Self::Output {
        use Length::*;
        let rhs = rhs.into();
        if self.is_zero() == Some(true) || rhs == 1.fct() {
            return self; } else if rhs == 0.fct() {
            return Self::zero(); }
        match self {
            Dip(e) => DipF32(e.to_f32() * rhs.0),
            Px(e) => PxF32(e.0 as f32 * rhs.0),
            Pt(e) => Pt(e * rhs.0),
            Factor(r) => Factor(r * rhs),
            Leftover(r) => Leftover(r * rhs),
            Em(e) => Em(e * rhs),
            RootEm(e) => RootEm(e * rhs),
            ViewportWidth(w) => ViewportWidth(w * rhs),
            ViewportHeight(h) => ViewportHeight(h * rhs),
            ViewportMin(m) => ViewportMin(m * rhs),
            ViewportMax(m) => ViewportMax(m * rhs),
            DipF32(e) => DipF32(e * rhs.0),
            PxF32(e) => PxF32(e * rhs.0),
            e => LengthExpr::Mul(e, rhs).to_length_checked(),
        }
    }
}
impl<F: Into<Factor>> ops::MulAssign<F> for Length {
    fn mul_assign(&mut self, rhs: F) {
        let lhs = mem::take(self);
        *self = lhs * rhs.into();
    }
}
impl<F: Into<Factor>> ops::Div<F> for Length {
    type Output = Length;
    fn div(self, rhs: F) -> Self::Output {
        use Length::*;
        let rhs = rhs.into();
        if self.is_zero() == Some(true) && rhs != 0.fct() {
            return self; }
        match self {
            Dip(e) => DipF32(e.to_f32() / rhs.0),
            Px(e) => PxF32(e.0 as f32 / rhs.0),
            Pt(e) => Pt(e / rhs.0),
            Factor(r) => Factor(r / rhs),
            Leftover(r) => Leftover(r / rhs),
            Em(e) => Em(e / rhs),
            RootEm(e) => RootEm(e / rhs),
            ViewportWidth(w) => ViewportWidth(w / rhs),
            ViewportHeight(h) => ViewportHeight(h / rhs),
            ViewportMin(m) => ViewportMin(m / rhs),
            ViewportMax(m) => ViewportMax(m / rhs),
            DipF32(e) => DipF32(e / rhs.0),
            PxF32(e) => PxF32(e / rhs.0),
            e => LengthExpr::Mul(e, rhs).to_length_checked(),
        }
    }
}
impl<F: Into<Factor>> ops::DivAssign<F> for Length {
    fn div_assign(&mut self, rhs: F) {
        let lhs = mem::take(self);
        *self = lhs / rhs.into();
    }
}
impl Transitionable for Length {
    fn lerp(self, to: &Self, step: EasingStep) -> Self {
        use Length::*;
        if step == 0.fct() {
            return self;
        }
        if step == 1.fct() {
            return to.clone();
        }
        match (self, to) {
            (Dip(a), Dip(b)) => Dip(a.lerp(b, step)),
            (Px(a), Px(b)) => Px(a.lerp(b, step)),
            (Pt(a), Pt(b)) => Pt(a.lerp(b, step)),
            (Factor(a), Factor(b)) => Factor(a.lerp(b, step)),
            (Leftover(a), Leftover(b)) => Leftover(a.lerp(b, step)),
            (Em(a), Em(b)) => Em(a.lerp(b, step)),
            (RootEm(a), RootEm(b)) => RootEm(a.lerp(b, step)),
            (ViewportWidth(a), ViewportWidth(b)) => ViewportWidth(a.lerp(b, step)),
            (ViewportHeight(a), ViewportHeight(b)) => ViewportHeight(a.lerp(b, step)),
            (ViewportMin(a), ViewportMin(b)) => ViewportMin(a.lerp(b, step)),
            (ViewportMax(a), ViewportMax(b)) => ViewportMax(a.lerp(b, step)),
            (PxF32(a), PxF32(b)) => PxF32(a.lerp(b, step)),
            (DipF32(a), DipF32(b)) => DipF32(a.lerp(b, step)),
            (Px(a), PxF32(b)) => PxF32((a.0 as f32).lerp(b, step)),
            (PxF32(a), Px(b)) => PxF32(a.lerp(&(b.0 as f32), step)),
            (Dip(a), DipF32(b)) => DipF32(a.to_f32().lerp(b, step)),
            (DipF32(a), Dip(b)) => DipF32(a.lerp(&b.to_f32(), step)),
            (a, b) => LengthExpr::Lerp(a, b.clone(), step).to_length_checked(),
        }
    }
}
impl ops::Neg for Length {
    type Output = Self;
    fn neg(self) -> Self::Output {
        match self {
            Length::Default => LengthExpr::Neg(Length::Default).to_length_checked(),
            Length::Dip(e) => Length::Dip(-e),
            Length::Px(e) => Length::Px(-e),
            Length::Pt(e) => Length::Pt(-e),
            Length::Factor(e) => Length::Factor(-e),
            Length::Leftover(e) => Length::Leftover(-e),
            Length::Em(e) => Length::Em(-e),
            Length::RootEm(e) => Length::RootEm(-e),
            Length::ViewportWidth(e) => Length::ViewportWidth(-e),
            Length::ViewportHeight(e) => Length::ViewportHeight(-e),
            Length::ViewportMin(e) => Length::ViewportMin(-e),
            Length::ViewportMax(e) => Length::ViewportMax(-e),
            Length::DipF32(e) => Length::DipF32(-e),
            Length::PxF32(e) => Length::PxF32(-e),
            Length::Expr(e) => LengthExpr::Neg(Length::Expr(e)).to_length_checked(),
        }
    }
}
impl Default for Length {
    fn default() -> Self {
        Length::Default
    }
}
impl PartialEq for Length {
    fn eq(&self, other: &Self) -> bool {
        use Length::*;
        match (self, other) {
            (Default, Default) => true,
            (Dip(a), Dip(b)) => a == b,
            (Px(a), Px(b)) => a == b,
            (Pt(a), Pt(b)) => about_eq(*a, *b, EQ_EPSILON_100),
            (DipF32(a), DipF32(b)) | (PxF32(a), PxF32(b)) => about_eq(*a, *b, EQ_EPSILON_100),
            (Factor(a), Factor(b)) | (Em(a), Em(b)) | (RootEm(a), RootEm(b)) | (Leftover(a), Leftover(b)) => a == b,
            (ViewportWidth(a), ViewportWidth(b))
            | (ViewportHeight(a), ViewportHeight(b))
            | (ViewportMin(a), ViewportMin(b))
            | (ViewportMax(a), ViewportMax(b)) => about_eq(a.0, b.0, EQ_EPSILON),
            (Expr(a), Expr(b)) => a == b,
            (Dip(a), DipF32(b)) | (DipF32(b), Dip(a)) => about_eq(a.to_f32(), *b, EQ_EPSILON_100),
            (Px(a), PxF32(b)) | (PxF32(b), Px(a)) => about_eq(a.0 as f32, *b, EQ_EPSILON_100),
            _ => false,
        }
    }
}
impl fmt::Debug for Length {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use Length::*;
        if f.alternate() {
            match self {
                Default => write!(f, "Length::Default"),
                Dip(e) => f.debug_tuple("Length::Dip").field(e).finish(),
                Px(e) => f.debug_tuple("Length::Px").field(e).finish(),
                Pt(e) => f.debug_tuple("Length::Pt").field(e).finish(),
                Factor(e) => f.debug_tuple("Length::Factor").field(e).finish(),
                Leftover(e) => f.debug_tuple("Length::Leftover").field(e).finish(),
                Em(e) => f.debug_tuple("Length::Em").field(e).finish(),
                RootEm(e) => f.debug_tuple("Length::RootEm").field(e).finish(),
                ViewportWidth(e) => f.debug_tuple("Length::ViewportWidth").field(e).finish(),
                ViewportHeight(e) => f.debug_tuple("Length::ViewportHeight").field(e).finish(),
                ViewportMin(e) => f.debug_tuple("Length::ViewportMin").field(e).finish(),
                ViewportMax(e) => f.debug_tuple("Length::ViewportMax").field(e).finish(),
                DipF32(e) => f.debug_tuple("Length::DipF32").field(e).finish(),
                PxF32(e) => f.debug_tuple("Length::PxF32").field(e).finish(),
                Expr(e) => f.debug_tuple("Length::Expr").field(e).finish(),
            }
        } else {
            match self {
                Default => write!(f, "Default"),
                Dip(e) => write!(f, "{}.dip()", e.to_f32()),
                Px(e) => write!(f, "{}.px()", e.0),
                Pt(e) => write!(f, "{e}.pt()"),
                Factor(e) => write!(f, "{}.pct()", e.0 * 100.0),
                Leftover(e) => write!(f, "{}.lft()", e.0),
                Em(e) => write!(f, "{}.em()", e.0),
                RootEm(e) => write!(f, "{}.rem()", e.0),
                ViewportWidth(e) => write!(f, "{e}.vw()"),
                ViewportHeight(e) => write!(f, "{e}.vh()"),
                ViewportMin(e) => write!(f, "{e}.vmin()"),
                ViewportMax(e) => write!(f, "{e}.vmax()"),
                DipF32(e) => write!(f, "{e}.dip()"),
                PxF32(e) => write!(f, "{e}.px()"),
                Expr(e) => write!(f, "{e}"),
            }
        }
    }
}
impl fmt::Display for Length {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use Length::*;
        match self {
            Default => write!(f, "Default"),
            Dip(l) => write!(f, "{l}"),
            Px(l) => write!(f, "{l}"),
            Pt(l) => write!(f, "{l}pt"),
            Factor(n) => write!(f, "{:.*}%", f.precision().unwrap_or(0), n.0 * 100.0),
            Leftover(l) => write!(f, "{l}lft"),
            Em(e) => write!(f, "{e}em"),
            RootEm(re) => write!(f, "{re}rem"),
            ViewportWidth(vw) => write!(f, "{vw}vw"),
            ViewportHeight(vh) => write!(f, "{vh}vh"),
            ViewportMin(vmin) => write!(f, "{vmin}vmin"),
            ViewportMax(vmax) => write!(f, "{vmax}vmax"),
            DipF32(l) => write!(f, "{l}dip"),
            PxF32(l) => write!(f, "{l}px"),
            Expr(e) => write!(f, "{e}"),
        }
    }
}
impl_from_and_into_var! {
    fn from(percent: FactorPercent) -> Length {
        Length::Factor(percent.into())
    }
    fn from(norm: Factor) -> Length {
        Length::Factor(norm)
    }
    fn from(f: f32) -> Length {
        Length::DipF32(f)
    }
    fn from(i: i32) -> Length {
        Length::Dip(Dip::new(i))
    }
    fn from(l: Px) -> Length {
        Length::Px(l)
    }
    fn from(l: Dip) -> Length {
        Length::Dip(l)
    }
}
impl Length {
    pub const fn zero() -> Length {
        Length::Px(Px(0))
    }
    pub const fn fill() -> Length {
        Length::Factor(Factor(1.0))
    }
    pub const fn half() -> Length {
        Length::Factor(Factor(0.5))
    }
    pub fn max(&self, other: impl Into<Length>) -> Length {
        use Length::*;
        match (self.clone(), other.into()) {
            (Default, Default) => Default,
            (Dip(a), Dip(b)) => Dip(a.max(b)),
            (Px(a), Px(b)) => Px(a.max(b)),
            (Pt(a), Pt(b)) => Pt(a.max(b)),
            (Factor(a), Factor(b)) => Factor(a.max(b)),
            (Leftover(a), Leftover(b)) => Leftover(a.max(b)),
            (Em(a), Em(b)) => Em(a.max(b)),
            (RootEm(a), RootEm(b)) => RootEm(a.max(b)),
            (ViewportWidth(a), ViewportWidth(b)) => ViewportWidth(a.max(b)),
            (ViewportHeight(a), ViewportHeight(b)) => ViewportHeight(a.max(b)),
            (ViewportMin(a), ViewportMin(b)) => ViewportMin(a.max(b)),
            (ViewportMax(a), ViewportMax(b)) => ViewportMax(a.max(b)),
            (DipF32(a), DipF32(b)) => DipF32(a.max(b)),
            (PxF32(a), PxF32(b)) => PxF32(a.max(b)),
            (DipF32(a), Dip(b)) | (Dip(b), DipF32(a)) => DipF32(a.max(b.to_f32())),
            (PxF32(a), Px(b)) | (Px(b), PxF32(a)) => PxF32(a.max(b.0 as f32)),
            (a, b) => LengthExpr::Max(a, b).to_length_checked(),
        }
    }
    pub fn min(&self, other: impl Into<Length>) -> Length {
        use Length::*;
        match (self.clone(), other.into()) {
            (Default, Default) => Default,
            (Dip(a), Dip(b)) => Dip(a.min(b)),
            (Px(a), Px(b)) => Px(a.min(b)),
            (Pt(a), Pt(b)) => Pt(a.min(b)),
            (Factor(a), Factor(b)) => Factor(a.min(b)),
            (Leftover(a), Leftover(b)) => Leftover(a.min(b)),
            (Em(a), Em(b)) => Em(a.min(b)),
            (RootEm(a), RootEm(b)) => RootEm(a.min(b)),
            (ViewportWidth(a), ViewportWidth(b)) => ViewportWidth(a.min(b)),
            (ViewportHeight(a), ViewportHeight(b)) => ViewportHeight(a.min(b)),
            (ViewportMin(a), ViewportMin(b)) => ViewportMin(a.min(b)),
            (ViewportMax(a), ViewportMax(b)) => ViewportMax(a.min(b)),
            (DipF32(a), DipF32(b)) => DipF32(a.min(b)),
            (PxF32(a), PxF32(b)) => PxF32(a.min(b)),
            (DipF32(a), Dip(b)) | (Dip(b), DipF32(a)) => DipF32(a.min(b.to_f32())),
            (PxF32(a), Px(b)) | (Px(b), PxF32(a)) => PxF32(a.min(b.0 as f32)),
            (a, b) => LengthExpr::Min(a, b).to_length_checked(),
        }
    }
    pub fn clamp(&self, min: impl Into<Length>, max: impl Into<Length>) -> Length {
        self.max(min).min(max)
    }
    pub fn abs(&self) -> Length {
        use Length::*;
        match self {
            Default => LengthExpr::Abs(Length::Default).to_length_checked(),
            Dip(e) => Dip(e.abs()),
            Px(e) => Px(e.abs()),
            Pt(e) => Pt(e.abs()),
            Factor(r) => Factor(r.abs()),
            Leftover(r) => Leftover(r.abs()),
            Em(e) => Em(e.abs()),
            RootEm(r) => RootEm(r.abs()),
            ViewportWidth(w) => ViewportWidth(w.abs()),
            ViewportHeight(h) => ViewportHeight(h.abs()),
            ViewportMin(m) => ViewportMin(m.abs()),
            ViewportMax(m) => ViewportMax(m.abs()),
            DipF32(e) => DipF32(e.abs()),
            PxF32(e) => PxF32(e.abs()),
            Expr(e) => LengthExpr::Abs(Length::Expr(e.clone())).to_length_checked(),
        }
    }
    pub fn is_zero(&self) -> Option<bool> {
        use Length::*;
        match self {
            Default => None,
            Dip(l) => Some(*l == self::Dip::new(0)),
            Px(l) => Some(*l == self::Px(0)),
            Pt(l) => Some(l.abs() < EQ_EPSILON),
            Factor(f) => Some(f.0.abs() < EQ_EPSILON),
            Leftover(f) => Some(f.0.abs() < EQ_EPSILON),
            Em(f) => Some(f.0.abs() < EQ_EPSILON),
            RootEm(f) => Some(f.0.abs() < EQ_EPSILON),
            ViewportWidth(p) => Some(p.0.abs() < EQ_EPSILON),
            ViewportHeight(p) => Some(p.0.abs() < EQ_EPSILON),
            ViewportMin(p) => Some(p.0.abs() < EQ_EPSILON),
            ViewportMax(p) => Some(p.0.abs() < EQ_EPSILON),
            DipF32(l) => Some(about_eq(*l, 0.0, EQ_EPSILON_100)),
            PxF32(l) => Some(about_eq(*l, 0.0, EQ_EPSILON_100)),
            Expr(_) => None,
        }
    }
    pub fn pt_to_px(pt: f32, scale_factor: Factor) -> Px {
        let px = Self::pt_to_px_f32(pt, scale_factor);
        Px(px.round() as i32)
    }
    pub fn pt_to_px_f32(pt: f32, scale_factor: Factor) -> f32 {
        pt * Self::PT_TO_DIP * scale_factor.0
    }
    pub fn px_to_pt(px: Px, scale_factor: Factor) -> f32 {
        let dip = px.0 as f32 / scale_factor.0;
        dip / Self::PT_TO_DIP
    }
    pub fn is_default(&self) -> bool {
        matches!(self, Length::Default)
    }
    pub fn replace_default(&mut self, overwrite: &Length) {
        if self.is_default() {
            *self = overwrite.clone();
        }
    }
    pub fn round_exact(&mut self) {
        match self {
            Length::PxF32(l) => *self = Length::Px(Px(l.round() as i32)),
            Length::DipF32(l) => *self = Length::Dip(Dip::new_f32(*l)),
            _ => {}
        }
    }
    pub fn memory_used(&self) -> ByteLength {
        std::mem::size_of::<Length>().bytes() + self.heap_memory_used()
    }
    pub fn heap_memory_used(&self) -> ByteLength {
        if let Length::Expr(e) = self {
            e.memory_used()
        } else {
            0.bytes()
        }
    }
    const PT_TO_DIP: f32 = 96.0 / 72.0; }
impl super::Layout1d for Length {
    fn layout_dft(&self, axis: LayoutAxis, default: Px) -> Px {
        use Length::*;
        match self {
            Default => default,
            Dip(l) => l.to_px(LAYOUT.scale_factor()),
            Px(l) => *l,
            Pt(l) => Self::pt_to_px(*l, LAYOUT.scale_factor()),
            Factor(f) => LAYOUT.constraints_for(axis).fill() * f.0,
            Leftover(f) => {
                if let Some(l) = LAYOUT.leftover_for(axis) {
                    l
                } else {
                    let fill = LAYOUT.constraints_for(axis).fill();
                    (fill * f.0).clamp(self::Px(0), fill)
                }
            }
            Em(f) => LAYOUT.font_size() * f.0,
            RootEm(f) => LAYOUT.root_font_size() * f.0,
            ViewportWidth(p) => LAYOUT.viewport().width * *p,
            ViewportHeight(p) => LAYOUT.viewport().height * *p,
            ViewportMin(p) => LAYOUT.viewport_min() * *p,
            ViewportMax(p) => LAYOUT.viewport_max() * *p,
            DipF32(l) => self::Px((l * LAYOUT.scale_factor().0).round() as i32),
            PxF32(l) => self::Px(l.round() as i32),
            Expr(e) => e.layout_dft(axis, default),
        }
    }
    fn layout_f32_dft(&self, axis: LayoutAxis, default: f32) -> f32 {
        use Length::*;
        match self {
            Default => default,
            Dip(l) => l.to_f32() * LAYOUT.scale_factor().0,
            Px(l) => l.0 as f32,
            Pt(l) => Self::pt_to_px_f32(*l, LAYOUT.scale_factor()),
            Factor(f) => LAYOUT.constraints_for(axis).fill().0 as f32 * f.0,
            Leftover(f) => {
                if let Some(l) = LAYOUT.leftover_for(axis) {
                    l.0 as f32
                } else {
                    let fill = LAYOUT.constraints_for(axis).fill().0 as f32;
                    (fill * f.0).clamp(0.0, fill)
                }
            }
            Em(f) => LAYOUT.font_size().0 as f32 * f.0,
            RootEm(f) => LAYOUT.root_font_size().0 as f32 * f.0,
            ViewportWidth(p) => LAYOUT.viewport().width.0 as f32 * *p,
            ViewportHeight(p) => LAYOUT.viewport().height.0 as f32 * *p,
            ViewportMin(p) => LAYOUT.viewport_min().0 as f32 * *p,
            ViewportMax(p) => LAYOUT.viewport_max().0 as f32 * *p,
            DipF32(l) => *l * LAYOUT.scale_factor().0,
            PxF32(l) => *l,
            Expr(e) => e.layout_f32_dft(axis, default),
        }
    }
    fn affect_mask(&self) -> LayoutMask {
        use Length::*;
        match self {
            Default => LayoutMask::DEFAULT_VALUE,
            Dip(_) => LayoutMask::SCALE_FACTOR,
            Px(_) => LayoutMask::empty(),
            Pt(_) => LayoutMask::SCALE_FACTOR,
            Factor(_) => LayoutMask::CONSTRAINTS,
            Leftover(_) => LayoutMask::LEFTOVER,
            Em(_) => LayoutMask::FONT_SIZE,
            RootEm(_) => LayoutMask::ROOT_FONT_SIZE,
            ViewportWidth(_) => LayoutMask::VIEWPORT,
            ViewportHeight(_) => LayoutMask::VIEWPORT,
            ViewportMin(_) => LayoutMask::VIEWPORT,
            ViewportMax(_) => LayoutMask::VIEWPORT,
            DipF32(_) => LayoutMask::SCALE_FACTOR,
            PxF32(_) => LayoutMask::empty(),
            Expr(e) => e.affect_mask(),
        }
    }
}
#[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum LengthExpr {
    Add(Length, Length),
    Sub(Length, Length),
    Mul(Length, Factor),
    Div(Length, Factor),
    Max(Length, Length),
    Min(Length, Length),
    Abs(Length),
    Neg(Length),
    Lerp(Length, Length, Factor),
}
impl LengthExpr {
    pub fn memory_used(&self) -> ByteLength {
        use LengthExpr::*;
        std::mem::size_of::<LengthExpr>().bytes()
            + match self {
                Add(a, b) => a.heap_memory_used() + b.heap_memory_used(),
                Sub(a, b) => a.heap_memory_used() + b.heap_memory_used(),
                Mul(a, _) => a.heap_memory_used(),
                Div(a, _) => a.heap_memory_used(),
                Max(a, b) => a.heap_memory_used() + b.heap_memory_used(),
                Min(a, b) => a.heap_memory_used() + b.heap_memory_used(),
                Abs(a) => a.heap_memory_used(),
                Neg(a) => a.heap_memory_used(),
                Lerp(a, b, _) => a.heap_memory_used() + b.heap_memory_used(),
            }
    }
    pub fn to_length_checked(self) -> Length {
        let bytes = self.memory_used();
        if bytes > 20.kibibytes() {
            tracing::error!(target: "to_length_checked", "length alloc > 20kB, replaced with zero");
            return Length::zero();
        }
        Length::Expr(Box::new(self))
    }
}
impl super::Layout1d for LengthExpr {
    fn layout_dft(&self, axis: LayoutAxis, default: Px) -> Px {
        let l = self.layout_f32_dft(axis, default.0 as f32);
        Px(l.round() as i32)
    }
    fn layout_f32_dft(&self, axis: LayoutAxis, default: f32) -> f32 {
        use LengthExpr::*;
        match self {
            Add(a, b) => a.layout_f32_dft(axis, default) + b.layout_f32_dft(axis, default),
            Sub(a, b) => a.layout_f32_dft(axis, default) - b.layout_f32_dft(axis, default),
            Mul(l, s) => l.layout_f32_dft(axis, default) * s.0,
            Div(l, s) => l.layout_f32_dft(axis, default) / s.0,
            Max(a, b) => {
                let a = a.layout_f32_dft(axis, default);
                let b = b.layout_f32_dft(axis, default);
                a.max(b)
            }
            Min(a, b) => {
                let a = a.layout_f32_dft(axis, default);
                let b = b.layout_f32_dft(axis, default);
                a.min(b)
            }
            Abs(e) => e.layout_f32_dft(axis, default).abs(),
            Neg(e) => -e.layout_f32_dft(axis, default),
            Lerp(a, b, f) => a.layout_f32_dft(axis, default).lerp(&b.layout_f32_dft(axis, default), *f),
        }
    }
    fn affect_mask(&self) -> LayoutMask {
        use LengthExpr::*;
        match self {
            Add(a, b) => a.affect_mask() | b.affect_mask(),
            Sub(a, b) => a.affect_mask() | b.affect_mask(),
            Mul(a, _) => a.affect_mask(),
            Div(a, _) => a.affect_mask(),
            Max(a, b) => a.affect_mask() | b.affect_mask(),
            Min(a, b) => a.affect_mask() | b.affect_mask(),
            Abs(a) => a.affect_mask(),
            Neg(a) => a.affect_mask(),
            Lerp(a, b, _) => a.affect_mask() | b.affect_mask(),
        }
    }
}
impl fmt::Debug for LengthExpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use LengthExpr::*;
        if f.alternate() {
            match self {
                Add(a, b) => f.debug_tuple("LengthExpr::Add").field(a).field(b).finish(),
                Sub(a, b) => f.debug_tuple("LengthExpr::Sub").field(a).field(b).finish(),
                Mul(l, s) => f.debug_tuple("LengthExpr::Mul").field(l).field(s).finish(),
                Div(l, s) => f.debug_tuple("LengthExpr::Div").field(l).field(s).finish(),
                Max(a, b) => f.debug_tuple("LengthExpr::Max").field(a).field(b).finish(),
                Min(a, b) => f.debug_tuple("LengthExpr::Min").field(a).field(b).finish(),
                Abs(e) => f.debug_tuple("LengthExpr::Abs").field(e).finish(),
                Neg(e) => f.debug_tuple("LengthExpr::Neg").field(e).finish(),
                Lerp(a, b, n) => f.debug_tuple("LengthExpr::Lerp").field(a).field(b).field(n).finish(),
            }
        } else {
            match self {
                Add(a, b) => write!(f, "({a:?} + {b:?})"),
                Sub(a, b) => write!(f, "({a:?} - {b:?})"),
                Mul(l, s) => write!(f, "({l:?} * {:?}.pct())", s.0 * 100.0),
                Div(l, s) => write!(f, "({l:?} / {:?}.pct())", s.0 * 100.0),
                Max(a, b) => write!(f, "max({a:?}, {b:?})"),
                Min(a, b) => write!(f, "min({a:?}, {b:?})"),
                Abs(e) => write!(f, "abs({e:?})"),
                Neg(e) => write!(f, "-({e:?})"),
                Lerp(a, b, n) => write!(f, "lerp({a:?}, {b:?}, {n:?})"),
            }
        }
    }
}
impl fmt::Display for LengthExpr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use LengthExpr::*;
        match self {
            Add(a, b) => write!(f, "({a} + {b})"),
            Sub(a, b) => write!(f, "({a} - {b})"),
            Mul(l, s) => write!(f, "({l} * {}%)", s.0 * 100.0),
            Div(l, s) => write!(f, "({l} / {}%)", s.0 * 100.0),
            Max(a, b) => write!(f, "max({a}, {b})"),
            Min(a, b) => write!(f, "min({a}, {b})"),
            Abs(e) => write!(f, "abs({e})"),
            Neg(e) => write!(f, "-({e})"),
            Lerp(a, b, n) => write!(f, "lerp({a}, {b}, {n})"),
        }
    }
}
pub trait LengthUnits {
    fn dip(self) -> Length;
    fn px(self) -> Length;
    fn pt(self) -> Length;
    fn fct_l(self) -> Length;
    fn pct_l(self) -> Length;
    fn em(self) -> Length;
    fn em_pct(self) -> Length;
    fn rem(self) -> Length;
    fn rem_pct(self) -> Length;
    fn vw(self) -> Length;
    fn vw_pct(self) -> Length;
    fn vh(self) -> Length;
    fn vh_pct(self) -> Length;
    fn vmin(self) -> Length;
    fn vmin_pct(self) -> Length;
    fn vmax(self) -> Length;
    fn vmax_pct(self) -> Length;
    fn lft(self) -> Length;
}
impl LengthUnits for f32 {
    fn dip(self) -> Length {
        Length::DipF32(self)
    }
    fn px(self) -> Length {
        Length::PxF32(self)
    }
    fn pt(self) -> Length {
        Length::Pt(self)
    }
    fn fct_l(self) -> Length {
        Length::Factor(self.fct())
    }
    fn pct_l(self) -> Length {
        Length::Factor(self.pct().fct())
    }
    fn em(self) -> Length {
        Length::Em(self.into())
    }
    fn rem(self) -> Length {
        Length::RootEm(self.into())
    }
    fn vw(self) -> Length {
        Length::ViewportWidth(self.into())
    }
    fn vh(self) -> Length {
        Length::ViewportHeight(self.into())
    }
    fn vmin(self) -> Length {
        Length::ViewportMin(self.into())
    }
    fn vmax(self) -> Length {
        Length::ViewportMax(self.into())
    }
    fn em_pct(self) -> Length {
        Length::Em(self.pct().into())
    }
    fn rem_pct(self) -> Length {
        Length::RootEm(self.pct().into())
    }
    fn vw_pct(self) -> Length {
        Length::ViewportWidth(self.pct().into())
    }
    fn vh_pct(self) -> Length {
        Length::ViewportHeight(self.pct().into())
    }
    fn vmin_pct(self) -> Length {
        Length::ViewportMin(self.pct().into())
    }
    fn vmax_pct(self) -> Length {
        Length::ViewportMax(self.pct().into())
    }
    fn lft(self) -> Length {
        Length::Leftover(self.fct())
    }
}
impl LengthUnits for i32 {
    fn dip(self) -> Length {
        Length::Dip(Dip::new(self))
    }
    fn px(self) -> Length {
        Length::Px(Px(self))
    }
    fn pt(self) -> Length {
        Length::Pt(self as f32)
    }
    fn fct_l(self) -> Length {
        Length::Factor(self.fct())
    }
    fn pct_l(self) -> Length {
        Length::Factor(self.pct().fct())
    }
    fn em(self) -> Length {
        Length::Em(self.fct())
    }
    fn rem(self) -> Length {
        Length::RootEm(self.fct())
    }
    fn vw(self) -> Length {
        Length::ViewportWidth(self.fct())
    }
    fn vh(self) -> Length {
        Length::ViewportHeight(self.fct())
    }
    fn vmin(self) -> Length {
        Length::ViewportMin(self.fct())
    }
    fn vmax(self) -> Length {
        Length::ViewportMax(self.fct())
    }
    fn em_pct(self) -> Length {
        Length::Em(self.pct().into())
    }
    fn rem_pct(self) -> Length {
        Length::RootEm(self.pct().into())
    }
    fn vw_pct(self) -> Length {
        Length::ViewportWidth(self.pct().into())
    }
    fn vh_pct(self) -> Length {
        Length::ViewportHeight(self.pct().into())
    }
    fn vmin_pct(self) -> Length {
        Length::ViewportMin(self.pct().into())
    }
    fn vmax_pct(self) -> Length {
        Length::ViewportMax(self.pct().into())
    }
    fn lft(self) -> Length {
        Length::Leftover(self.fct())
    }
}