use std::{fmt, ops};
use zng_var::{animation::Transitionable, impl_from_and_into_var};
use super::{impl_length_comp_conversions, DipRect, Factor2d, LayoutMask, Length, Point, PxRect, Size, Vector};
#[derive(Clone, Default, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
pub struct Rect {
    pub origin: Point,
    pub size: Size,
}
impl fmt::Debug for Rect {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.debug_struct("Rect")
                .field("origin", &self.origin)
                .field("size", &self.size)
                .finish()
        } else {
            write!(f, "{:?}.at{:?}", self.origin, self.size)
        }
    }
}
impl fmt::Display for Rect {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(p) = f.precision() {
            write!(f, "{:.p$} {:.p$}", self.origin, self.size, p = p)
        } else {
            write!(f, "{} {}", self.origin, self.size)
        }
    }
}
impl Rect {
    pub fn new<O: Into<Point>, S: Into<Size>>(origin: O, size: S) -> Self {
        Rect {
            origin: origin.into(),
            size: size.into(),
        }
    }
    pub fn from_size<S: Into<Size>>(size: S) -> Self {
        Self::new(Point::zero(), size)
    }
    pub fn zero() -> Self {
        Self::new(Point::zero(), Size::zero())
    }
    pub fn fill() -> Self {
        Self::from_size(Size::fill())
    }
    pub fn min(&self) -> Point {
        self.origin.clone()
    }
    pub fn max(&self) -> Point {
        self.origin.clone() + self.size.clone()
    }
    pub fn min_x(&self) -> Length {
        self.origin.x.clone()
    }
    pub fn min_y(&self) -> Length {
        self.origin.y.clone()
    }
    pub fn max_x(&self) -> Length {
        self.origin.x.clone() + self.size.width.clone()
    }
    pub fn max_y(&self) -> Length {
        self.origin.y.clone() + self.size.height.clone()
    }
    pub fn translate(&self, by: impl Into<Vector>) -> Self {
        let mut r = self.clone();
        r.origin += by.into();
        r
    }
    pub fn is_default(&self) -> bool {
        self.origin.is_default() && self.size.is_default()
    }
    pub fn replace_default(&mut self, overwrite: &Rect) {
        self.origin.replace_default(&overwrite.origin);
        self.size.replace_default(&overwrite.size);
    }
}
impl super::Layout2d for Rect {
    type Px = PxRect;
    fn layout_dft(&self, default: Self::Px) -> Self::Px {
        PxRect {
            origin: self.origin.layout_dft(default.origin),
            size: self.size.layout_dft(default.size),
        }
    }
    fn affect_mask(&self) -> LayoutMask {
        self.origin.affect_mask() | self.size.affect_mask()
    }
}
impl_length_comp_conversions! {
    fn from(x: X, y: Y, width: W, height: H) -> Rect {
        Rect::new((x, y), (width, height))
    }
}
impl_from_and_into_var! {
    fn from(rect: PxRect) -> Rect {
        Rect::new(rect.origin, rect.size)
    }
    fn from(rect: DipRect) -> Rect {
        Rect::new(rect.origin, rect.size)
    }
    fn from(size: Size) -> Rect {
        Rect::from_size(size)
    }
    fn from<O: Into<Point>, S: Into<Size>>((origin, size): (O, S)) -> Rect {
        Rect::new(origin, size)
    }
}
impl<S: Into<Factor2d>> ops::Mul<S> for Rect {
    type Output = Self;
    fn mul(mut self, rhs: S) -> Self {
        self *= rhs;
        self
    }
}
impl<'a, S: Into<Factor2d>> ops::Mul<S> for &'a Rect {
    type Output = Rect;
    fn mul(self, rhs: S) -> Self::Output {
        self.clone() * rhs
    }
}
impl<S: Into<Factor2d>> ops::MulAssign<S> for Rect {
    fn mul_assign(&mut self, rhs: S) {
        let rhs = rhs.into();
        self.origin *= rhs;
        self.size *= rhs;
    }
}
impl<S: Into<Factor2d>> ops::Div<S> for Rect {
    type Output = Self;
    fn div(mut self, rhs: S) -> Self {
        self /= rhs;
        self
    }
}
impl<'a, S: Into<Factor2d>> ops::Div<S> for &'a Rect {
    type Output = Rect;
    fn div(self, rhs: S) -> Self::Output {
        self.clone() / rhs
    }
}
impl<S: Into<Factor2d>> ops::DivAssign<S> for Rect {
    fn div_assign(&mut self, rhs: S) {
        let rhs = rhs.into();
        self.origin /= rhs;
        self.size /= rhs;
    }
}
impl ops::Add for Rect {
    type Output = Self;
    fn add(mut self, rhs: Self) -> Self {
        self += rhs;
        self
    }
}
impl ops::AddAssign for Rect {
    fn add_assign(&mut self, rhs: Self) {
        self.origin += rhs.origin;
        self.size += rhs.size;
    }
}
impl ops::Sub for Rect {
    type Output = Self;
    fn sub(mut self, rhs: Self) -> Self {
        self -= rhs;
        self
    }
}
impl ops::SubAssign for Rect {
    fn sub_assign(&mut self, rhs: Self) {
        self.origin -= rhs.origin;
        self.size -= rhs.size;
    }
}
pub trait RectFromTuplesBuilder {
    fn at<X: Into<Length>, Y: Into<Length>>(self, x: X, y: Y) -> Rect;
}
impl<W: Into<Length>, H: Into<Length>> RectFromTuplesBuilder for (W, H) {
    fn at<X: Into<Length>, Y: Into<Length>>(self, x: X, y: Y) -> Rect {
        Rect::new((x, y), self)
    }
}