// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
pub(all) enum FillMode {
  /// Only keep voxels intersecting the input polyline segments.
  SurfaceOnly
  /// Keep surface voxels and fill the interior using a flood-fill from the volume boundary.
  ///
  /// NOTE: `detect_self_intersections` is accepted for API parity with Parry/Rapier, but this 2D
  /// port currently ignores it.
  FloodFill(Bool, Bool)
}

///|
pub fn FillMode::default() -> FillMode {
  FloodFill(false, false)
}

///|
pub fn FillMode::surface_only() -> FillMode {
  SurfaceOnly
}

///|
fn max_real(a : @core.Real, b : @core.Real) -> @core.Real {
  if a > b {
    a
  } else {
    b
  }
}

///|
fn min_real(a : @core.Real, b : @core.Real) -> @core.Real {
  if a < b {
    a
  } else {
    b
  }
}

///|
fn max_int(a : Int, b : Int) -> Int {
  if a > b {
    a
  } else {
    b
  }
}

///|
fn min_int(a : Int, b : Int) -> Int {
  if a < b {
    a
  } else {
    b
  }
}

///|
fn floor_div_to_int(value : @core.Real, denom : @core.Real) -> Int {
  if denom == 0.0F {
    0
  } else {
    @math.floor((value / denom).to_double()).to_int()
  }
}

///|
fn ceil_to_int(value : @core.Real) -> Int {
  @math.ceil(value.to_double()).to_int()
}

///|
fn segment_intersects_aabb(
  p0 : @core.Vec2,
  p1 : @core.Vec2,
  mins : @core.Vec2,
  maxs : @core.Vec2,
) -> Bool {
  let d = p1.sub(p0)
  let eps = 1.0e-12F
  let mut tmin = 0.0F
  let mut tmax = 1.0F
  fn update_slab(
    p0a : @core.Real,
    da : @core.Real,
    mina : @core.Real,
    maxa : @core.Real,
    tmin : @core.Real,
    tmax : @core.Real,
    eps : @core.Real,
  ) -> (@core.Real, @core.Real, Bool) {
    let mut tmin2 = tmin
    let mut tmax2 = tmax
    if @core.abs(da) < eps {
      if p0a < mina || p0a > maxa {
        (tmin2, tmax2, false)
      } else {
        (tmin2, tmax2, true)
      }
    } else {
      let inv = 1.0F / da
      let t1 = (mina - p0a) * inv
      let t2 = (maxa - p0a) * inv
      let ta = min_real(t1, t2)
      let tb = max_real(t1, t2)
      if ta > tmin2 {
        tmin2 = ta
      }
      if tb < tmax2 {
        tmax2 = tb
      }
      (tmin2, tmax2, tmin2 <= tmax2)
    }
  }

  let (tmin_x, tmax_x, ok_x) = update_slab(
    p0.x,
    d.x,
    mins.x,
    maxs.x,
    tmin,
    tmax,
    eps,
  )
  if !ok_x {
    return false
  }
  tmin = tmin_x
  tmax = tmax_x
  let (tmin_y, tmax_y, ok_y) = update_slab(
    p0.y,
    d.y,
    mins.y,
    maxs.y,
    tmin,
    tmax,
    eps,
  )
  if !ok_y {
    return false
  }
  tmin = tmin_y
  tmax = tmax_y
  tmin <= tmax
}

///|
fn collider_builder_with_shape(shape : Shape) -> ColliderBuilder {
  {
    shape,
    translation: @core.Vec2::zero(),
    rotation: 0.0F,
    active_collision_types: ActiveCollisionTypes::default(),
    sensor: false,
    active_events: ActiveEvents::empty(),
    active_hooks: ActiveHooks::empty(),
    collision_groups: @dynamics.InteractionGroups::all(),
    solver_groups: @dynamics.InteractionGroups::all(),
    density: 1.0F,
    friction: 0.5F,
    friction_combine_rule: Average,
    restitution: 0.0F,
    restitution_combine_rule: Average,
    contact_skin: 0.0F,
    contact_force_event_threshold: 0.0F,
    user_data: @core.UserData128::zero(),
    enabled: true,
  }
}

///|
pub fn ColliderBuilder::ColliderBuilder(shape : Shape) -> ColliderBuilder {
  collider_builder_with_shape(shape)
}

///|
fn voxel_rectangles_from_points(
  voxel_size : @core.Vec2,
  points : Array[@core.Vec2],
) -> Array[(Int, Int, Int, Int)] {
  if points.length() == 0 {
    return []
  }
  if voxel_size.x == 0.0F || voxel_size.y == 0.0F {
    return []
  }
  let coords : Array[(Int, Int)] = []
  let mut min_x = 0
  let mut max_x = 0
  let mut min_y = 0
  let mut max_y = 0
  let mut first = true
  for i in 0..= width || y >= height {
      continue
    }
    occ[x + y * width] = true
  }
  let rects : Array[(Int, Int, Int, Int)] = []
  for y in 0.. Shape {
  let rects = voxel_rectangles_from_points(voxel_size, points)
  let parts : Array[(@core.Isometry2, Shape)] = []
  for i in 0.. ColliderBuilder {
  ColliderBuilder(voxels_shape_from_points(voxel_size, points))
}

///|
pub fn Shape::voxelized_mesh(
  vertices : Array[@core.Vec2],
  indices : Array[(Int, Int)],
  voxel_size : @core.Real,
  fill_mode : FillMode,
) -> Shape {
  if vertices.length() == 0 || indices.length() == 0 || voxel_size <= 0.0F {
    return Compound([])
  }

  // Compute local AABB of the input polyline.
  let mut min_pt = vertices[0]
  let mut max_pt = vertices[0]
  for i in 1..= vertices.length() || ib >= vertices.length() {
      continue
    }
    let a0 = vertices[ia]
    let b0 = vertices[ib]
    let da = a0.sub(origin)
    let db = b0.sub(origin)
    let a = @core.Vec2(da.x * inv_scale, da.y * inv_scale)
    let b = @core.Vec2(db.x * inv_scale, db.y * inv_scale)
    let iax = (a.x + 0.5F).to_int()
    let iay = (a.y + 0.5F).to_int()
    let ibx = (b.x + 0.5F).to_int()
    let iby = (b.y + 0.5F).to_int()
    let mut i0 = min_int(iax, ibx)
    let mut j0 = min_int(iay, iby)
    let mut i1 = max_int(iax, ibx)
    let mut j1 = max_int(iay, iby)
    i0 = max_int(i0 - 1, 0)
    j0 = max_int(j0 - 1, 0)
    i1 = min_int(i1 + 1, res_x)
    j1 = min_int(j1 + 1, res_y)
    for i in i0.. {
      // Keep only the on-surface voxels.
      let centers : Array[@core.Vec2] = []
      for j in 0.. {
      // Flood fill outside voxels starting from the volume boundary.
      let queue : Array[(Int, Int)] = []
      let mut qh = 0
      fn enqueue_if_undef(
        states : Array[Int],
        queue : Array[(Int, Int)],
        i : Int,
        j : Int,
        res_x : Int,
        res_y : Int,
      ) -> Unit {
        if i < 0 || j < 0 || i >= res_x || j >= res_y {
          return
        }
        let idx = i + j * res_x
        if states[idx] == 0 {
          states[idx] = 2
          queue.push((i, j))
        }
      }

      // Mark boundary undefined voxels as outside seeds.
      for i in 0.. ColliderBuilder {
  ColliderBuilder(
    Shape::voxelized_mesh(vertices, indices, voxel_size, fill_mode),
  )
}