use ribir_core::prelude::*;

#[derive(SingleChild, Declare, Clone)]
pub struct TransformBox {
  pub matrix: Transform,
}

impl Render for TransformBox {
  fn measure(&self, clamp: BoxClamp, ctx: &mut MeasureCtx) -> Size {
    self
      .matrix
      .inverse()
      .map_or_else(Size::zero, |t| {
        let min_box = t.outer_transformed_box(&Box2D::from_size(clamp.min));
        let min = min_box.size();

        let max_box = t.outer_transformed_box(&Box2D::from_size(clamp.max));
        let max = max_box.size();

        let child_clamp = BoxClamp { min, max };

        let size = ctx.assert_perform_single_child_layout(child_clamp);
        let rect = self
          .matrix
          .outer_transformed_rect(&Rect::from_size(size));
        rect.size
      })
  }

  #[inline]
  fn paint(&self, ctx: &mut PaintingCtx) { ctx.painter().apply_transform(&self.matrix); }

  #[cfg(feature = "debug")]
  fn debug_name(&self) -> std::borrow::Cow<'static, str> {
    std::borrow::Cow::Borrowed("transformBox")
  }
}

impl TransformBox {
  #[inline]
  pub fn new(matrix: Transform) -> Self { Self { matrix } }
}

#[cfg(test)]
mod tests {
  use ribir_core::test_helper::*;
  use ribir_dev_helper::*;

  use super::*;

  widget_layout_test!(
    smoke,
    WidgetTester::new(fn_widget! {
      @TransformBox {
        matrix: Transform::new(2., 0., 0., 2., 0., 0.),
        @Container { size: Size::new(100., 100.) }
      }
    }),
    LayoutCase::default().with_size(Size::new(200., 200.))
  );
}
