///|
/// Represents a closed boundary contour with an ordered array of 2D points.
pub(all) struct Contour {
  points : Array[Point2D]
} derive(Debug, Eq)

///|
/// Creates a new Contour.
pub fn Contour::new(points : Array[Point2D]) -> Contour {
  { points, }
}

///|
/// Computes the enclosed geometric area using Gauss's Shoelace formula.
pub fn Contour::area(self : Contour) -> Double {
  let n = self.points.length()
  if n < 3 {
    return 0.0
  }
  let mut sum = 0.0
  for i in 0.. Double {
  let n = self.points.length()
  if n < 2 {
    return 0.0
  }
  let mut total = 0.0
  for i in 0.. Point2D {
  let n = self.points.length()
  if n == 0 {
    return { x: 0.0, y: 0.0 }
  }
  let mut sx = 0.0
  let mut sy = 0.0
  for p in self.points {
    sx += p.x
    sy += p.y
  }
  { x: sx / n.to_double(), y: sy / n.to_double() }
}

///|
/// Computes bounding rectangle enclosing this contour.
pub fn Contour::bounding_rect(self : Contour) -> Rect2D {
  let n = self.points.length()
  if n == 0 {
    return { x: 0.0, y: 0.0, width: 0.0, height: 0.0 }
  }
  let mut min_x = self.points[0].x
  let mut max_x = self.points[0].x
  let mut min_y = self.points[0].y
  let mut max_y = self.points[0].y
  for p in self.points {
    if p.x < min_x {
      min_x = p.x
    }
    if p.x > max_x {
      max_x = p.x
    }
    if p.y < min_y {
      min_y = p.y
    }
    if p.y > max_y {
      max_y = p.y
    }
  }
  { x: min_x, y: min_y, width: max_x - min_x, height: max_y - min_y }
}

///|
/// Computes 7 Hu invariant moments from contour points.
pub fn Contour::hu_moments(self : Contour) -> Array[Double] {
  let n = self.points.length()
  if n < 3 {
    return Array::make(7, 0.0)
  }

  // Spatial moments m_pq using Green's theorem on polygon
  let mut m00 = 0.0
  let mut m10 = 0.0
  let mut m01 = 0.0
  let mut m20 = 0.0
  let mut m02 = 0.0
  let mut m11 = 0.0
  let mut m30 = 0.0
  let mut m03 = 0.0
  let mut m21 = 0.0
  let mut m12 = 0.0

  for i in 0.. Array[Contour] {
  let w = binary_img.width
  let h = binary_img.height
  let min_pts = min_points
  let contours : Array[Contour] = []

  let visited = Array::make(w * h, false)

  // 8 neighborhood offsets in clockwise order: E, SE, S, SW, W, NW, N, NE
  let dxs : Array[Int] = [1, 1, 0, -1, -1, -1, 0, 1]
  let dys : Array[Int] = [0, 1, 1, 1, 0, -1, -1, -1]

  for y in 0.. 0 && !visited[y * w + x] {
        // Check if starting border pixel (has background neighbor)
        let is_border = x == 0 ||
          y == 0 ||
          x == w - 1 ||
          y == h - 1 ||
          binary_img.get(x - 1, y) == 0 ||
          binary_img.get(x + 1, y) == 0 ||
          binary_img.get(x, y - 1) == 0 ||
          binary_img.get(x, y + 1) == 0

        if is_border {
          let points : Array[Point2D] = []
          let start_x = x
          let start_y = y
          let mut curr_x = x
          let mut curr_y = y
          let mut dir = 0

          let max_trace = w * h * 2
          let mut trace_count = 0

          for ;; {
            points.push(Point2D::new(curr_x.to_double(), curr_y.to_double()))
            visited[curr_y * w + curr_x] = true
            trace_count += 1

            // Search next border pixel in clockwise order starting from (dir + 5) % 8
            let mut next_found = false
            let search_start = (dir + 5) % 8

            for step in 0..<8 {
              let next_dir = (search_start + step) % 8
              let nx = curr_x + dxs[next_dir]
              let ny = curr_y + dys[next_dir]
              if nx >= 0 && nx < w && ny >= 0 && ny < h {
                if binary_img.get(nx, ny) > 0 {
                  curr_x = nx
                  curr_y = ny
                  dir = next_dir
                  next_found = true
                  break
                }
              }
            }

            if !next_found || trace_count >= max_trace {
              break
            }

            if curr_x == start_x && curr_y == start_y && points.length() >= 3 {
              break
            }
          }

          if points.length() >= min_pts {
            contours.push(Contour::new(points))
          }
        }
      }
    }
  }

  contours
}