///|
/// 2D Affine Transformation Matrix
/// Represents a transformation of the form:
/// x' = a*x + c*y + tx
/// y' = b*x + d*y + ty
pub(all) struct Matrix2D {
  a : Double
  b : Double
  c : Double
  d : Double
  tx : Double
  ty : Double
} derive(Eq, Debug, ToJson)

///|
/// Returns the identity matrix
pub fn Matrix2D::identity() -> Matrix2D {
  { a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0 }
}

///|
/// Creates a translation matrix
pub fn Matrix2D::translate(tx : Double, ty : Double) -> Matrix2D {
  { a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx, ty }
}

///|
/// Creates a rotation matrix around the origin (angle in radians)
pub fn Matrix2D::rotate(angle : Double) -> Matrix2D {
  let cos_a = @math.cos(angle)
  let sin_a = @math.sin(angle)
  { a: cos_a, b: sin_a, c: -sin_a, d: cos_a, tx: 0.0, ty: 0.0 }
}

///|
/// Creates a scaling matrix
pub fn Matrix2D::scale(sx : Double, sy : Double) -> Matrix2D {
  { a: sx, b: 0.0, c: 0.0, d: sy, tx: 0.0, ty: 0.0 }
}

///|
/// Creates a reflection matrix across a line through the origin at angle (radians)
pub fn Matrix2D::reflect(angle : Double) -> Matrix2D {
  let cos_2a = @math.cos(2.0 * angle)
  let sin_2a = @math.sin(2.0 * angle)
  { a: cos_2a, b: sin_2a, c: sin_2a, d: -cos_2a, tx: 0.0, ty: 0.0 }
}

///|
/// Multiplies two matrices: self * other
pub fn Matrix2D::multiply(self : Matrix2D, other : Matrix2D) -> Matrix2D {
  {
    a: self.a * other.a + self.c * other.b,
    b: self.b * other.a + self.d * other.b,
    c: self.a * other.c + self.c * other.d,
    d: self.b * other.c + self.d * other.d,
    tx: self.a * other.tx + self.c * other.ty + self.tx,
    ty: self.b * other.tx + self.d * other.ty + self.ty,
  }
}

///|
/// Applies the matrix transformation to a 2D point (x, y)
pub fn Matrix2D::transform_point(
  self : Matrix2D,
  x : Double,
  y : Double,
) -> (Double, Double) {
  (self.a * x + self.c * y + self.tx, self.b * x + self.d * y + self.ty)
}