///|
/// SVG Transform Operations
/// Optimized 2D affine transformation matrix operations

///|
/// Identity transform (no transformation)
fn Transform::identity() -> Transform {
  { a: 1.0, b: 0.0, c: 0.0, d: 1.0, e: 0.0, f: 0.0 }
}

///|
/// Create a translation transform
pub fn Transform::translate(tx : Double, ty : Double) -> Transform {
  { a: 1.0, b: 0.0, c: 0.0, d: 1.0, e: tx, f: ty }
}

///|
/// Create a scaling transform
pub fn Transform::scale(sx : Double, sy : Double) -> Transform {
  { a: sx, b: 0.0, c: 0.0, d: sy, e: 0.0, f: 0.0 }
}

///|
/// Create a uniform scaling transform
pub fn Transform::scale_uniform(s : Double) -> Transform {
  Transform::scale(s, s)
}

///|
/// Create a rotation transform (angle in radians)
pub fn Transform::rotate(angle : Double) -> Transform {
  let cos_a = @math.cos(angle)
  let sin_a = @math.sin(angle)
  { a: cos_a, b: sin_a, c: -sin_a, d: cos_a, e: 0.0, f: 0.0 }
}

///|
/// Create a rotation transform around a point (angle in radians)
pub fn Transform::rotate_around(
  angle : Double,
  cx : Double,
  cy : Double,
) -> Transform {
  // Translate to origin, rotate, translate back
  let t1 = Transform::translate(-cx, -cy)
  let r = Transform::rotate(angle)
  let t2 = Transform::translate(cx, cy)
  t2.multiply(r.multiply(t1))
}

///|
/// Create a skewX transform (angle in radians)
pub fn Transform::skew_x(angle : Double) -> Transform {
  { a: 1.0, b: 0.0, c: @math.tan(angle), d: 1.0, e: 0.0, f: 0.0 }
}

///|
/// Create a skewY transform (angle in radians)
pub fn Transform::skew_y(angle : Double) -> Transform {
  { a: 1.0, b: @math.tan(angle), c: 0.0, d: 1.0, e: 0.0, f: 0.0 }
}

///|
/// Create a transform from a full matrix specification
pub fn Transform::matrix(
  a : Double,
  b : Double,
  c : Double,
  d : Double,
  e : Double,
  f : Double,
) -> Transform {
  { a, b, c, d, e, f }
}

///|
/// Multiply two transforms (self * other)
/// Result: first apply `other`, then apply `self`
pub fn Transform::multiply(self : Transform, other : Transform) -> Transform {
  // Matrix multiplication:
  // | a1 c1 e1 |   | a2 c2 e2 |
  // | b1 d1 f1 | * | b2 d2 f2 |
  // | 0  0  1  |   | 0  0  1  |
  {
    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,
    e: self.a * other.e + self.c * other.f + self.e,
    f: self.b * other.e + self.d * other.f + self.f,
  }
}

///|
/// Apply transform to a point
pub fn Transform::apply(
  self : Transform,
  x : Double,
  y : Double,
) -> (Double, Double) {
  let new_x = self.a * x + self.c * y + self.e
  let new_y = self.b * x + self.d * y + self.f
  (new_x, new_y)
}

///|
/// Apply transform to a point (struct version)
pub fn Transform::apply_point(
  self : Transform,
  point : (Double, Double),
) -> (Double, Double) {
  self.apply(point.0, point.1)
}

///|
/// Compute the determinant of the transform
pub fn Transform::determinant(self : Transform) -> Double {
  self.a * self.d - self.b * self.c
}

///|
/// Check if the transform is invertible
pub fn Transform::is_invertible(self : Transform) -> Bool {
  let det = self.determinant()
  det != 0.0
}

///|
/// Compute the inverse transform
/// Returns identity if not invertible
pub fn Transform::inverse(self : Transform) -> Transform {
  let det = self.determinant()
  if det == 0.0 {
    return Transform::identity()
  }
  let inv_det = 1.0 / det
  {
    a: self.d * inv_det,
    b: -self.b * inv_det,
    c: -self.c * inv_det,
    d: self.a * inv_det,
    e: (self.c * self.f - self.d * self.e) * inv_det,
    f: (self.b * self.e - self.a * self.f) * inv_det,
  }
}

///|
/// Get the translation component
pub fn Transform::get_translate(self : Transform) -> (Double, Double) {
  (self.e, self.f)
}

///|
/// Get the scale factors (approximate, assumes no rotation)
pub fn Transform::get_scale(self : Transform) -> (Double, Double) {
  let sx = (self.a * self.a + self.b * self.b).sqrt()
  let sy = (self.c * self.c + self.d * self.d).sqrt()
  (sx, sy)
}

///|
/// Get the rotation angle in radians (approximate, assumes uniform scale)
pub fn Transform::get_rotation(self : Transform) -> Double {
  @math.atan2(self.b, self.a)
}

///|
/// Transform a bounding box (returns axis-aligned bounding box of transformed corners)
pub fn Transform::apply_bbox(
  self : Transform,
  bbox : BoundingBox,
) -> BoundingBox {
  if bbox.is_empty() {
    return bbox
  }
  // Transform all 4 corners
  let (x1, y1) = self.apply(bbox.min_x, bbox.min_y)
  let (x2, y2) = self.apply(bbox.max_x, bbox.min_y)
  let (x3, y3) = self.apply(bbox.max_x, bbox.max_y)
  let (x4, y4) = self.apply(bbox.min_x, bbox.max_y)
  // Find axis-aligned bounding box
  let min_x = min(min(x1, x2), min(x3, x4))
  let max_x = max(max(x1, x2), max(x3, x4))
  let min_y = min(min(y1, y2), min(y3, y4))
  let max_y = max(max(y1, y2), max(y3, y4))
  { min_x, min_y, max_x, max_y }
}

///|
/// Convert degrees to radians
fn degrees_to_radians(degrees : Double) -> Double {
  degrees * 3.14159265358979323846 / 180.0
}