// 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.

///|
/// Rapier public-surface parity shims for the narrow-phase and contact-pair APIs.
///
/// Notes:
/// - This file focuses on making all Rapier `pub` items (default features) reachable
///   from this MoonBit port. Some advanced low-level fields (solver warmstart data,
///   SIMD contact layout, etc.) are currently exposed for API parity but are not
///   yet fully integrated with the solver in this port.

///|
/// Equivalent to Rapier's `geometry::Contact` (`TrackedContact`).
///
/// This port's 2D narrow-phase currently exposes `ContactManifoldPoint` as the
/// primary per-contact record, so we alias `Contact` to that type.
pub type Contact = ContactManifoldPoint

///|
/// Equivalent to Rapier's `geometry::contact_pair::ContactData`.
pub(all) struct ContactData {
  impulse : @core.Real
  tangent_impulse : @core.Vec2
  warmstart_impulse : @core.Real
  warmstart_tangent_impulse : @core.Vec2
  // Rapier's dim3-only `warmstart_twist_impulse`.
  warmstart_twist_impulse : @core.Real
}

///|
pub fn ContactData::default() -> ContactData {
  {
    impulse: 0.0F,
    tangent_impulse: @core.Vec2::zero(),
    warmstart_impulse: 0.0F,
    warmstart_tangent_impulse: @core.Vec2::zero(),
    warmstart_twist_impulse: 0.0F,
  }
}

///|
pub fn ContactData::ContactData(
  impulse : @core.Real,
  tangent_impulse : @core.Vec2,
  warmstart_impulse : @core.Real,
  warmstart_tangent_impulse : @core.Vec2,
  warmstart_twist_impulse : @core.Real,
) -> ContactData {
  {
    impulse,
    tangent_impulse,
    warmstart_impulse,
    warmstart_tangent_impulse,
    warmstart_twist_impulse,
  }
}

///|
/// Equivalent to Rapier's `geometry::contact_pair::SolverContactGeneric`.
///
/// This is exposed primarily for pub-surface parity. The solver warmstart fields
/// are currently not written by the narrow-phase in this port.
pub struct SolverContactGeneric {
  point : @core.Vec2
  dist : @core.Real
  friction : @core.Real
  restitution : @core.Real
  tangent_velocity : @core.Vec2
  warmstart_impulse : @core.Real
  warmstart_tangent_impulse : @core.Vec2
  warmstart_twist_impulse : @core.Real
  // Rapier uses a scalar for SIMD friendliness; we keep it as a scalar too.
  is_new : @core.Real
  // Rapier stores a fixed-size lane array; this port uses a dynamic array.
  contact_id : Array[Int]
}

///|
pub fn SolverContactGeneric::default() -> SolverContactGeneric {
  {
    point: @core.Vec2::zero(),
    dist: 0.0F,
    friction: 0.0F,
    restitution: 0.0F,
    tangent_velocity: @core.Vec2::zero(),
    warmstart_impulse: 0.0F,
    warmstart_tangent_impulse: @core.Vec2::zero(),
    warmstart_twist_impulse: 0.0F,
    is_new: 1.0F,
    contact_id: [],
  }
}

///|
/// Equivalent to Rapier's SIMD solver-contact alias.
///
/// This port does not currently implement SIMD storage; we use the same record type.
pub type SimdSolverContact = SolverContactGeneric

///|
/// Equivalent to Rapier's `geometry::contact_pair::ContactManifoldData`.
pub struct ContactManifoldData {
  solver_flags : SolverFlags
  solver_contacts : Array[SolverContactGeneric]
  relative_dominance : Int
}

///|
pub fn ContactManifoldData::default() -> ContactManifoldData {
  {
    solver_flags: SolverFlags::default(),
    solver_contacts: [],
    relative_dominance: 0,
  }
}

///|
pub fn ContactManifoldData::num_active_contacts(
  self : ContactManifoldData,
) -> Int {
  self.solver_contacts.length()
}

///|
pub fn ContactPair::has_any_active_contact(self : ContactPair) -> Bool {
  // This port currently doesn't build solver contacts, so we treat any manifold point
  // as an "active" contact.
  for i in 0.. 0 {
      return true
    }
  }
  false
}

///|
pub fn ContactPair::total_impulse(self : ContactPair) -> @core.Vec2 {
  fn vec2_scale(v : @core.Vec2, s : @core.Real) -> @core.Vec2 {
    Vec2(v.x * s, v.y * s)
  }

  let mut out = @core.Vec2::zero()
  let manifolds = self.manifolds()
  for mi in 0.. @core.Real {
  let manifolds = self.manifolds()
  let mut out = 0.0F
  for mi in 0.. (@core.Real, @core.Vec2) {
  let manifolds = self.manifolds()
  let mut best = 0.0F
  let mut best_dir = @core.Vec2::zero()
  for mi in 0.. best {
      best = total
      best_dir = m.normal()
    }
  }
  (best, best_dir)
}

///|
pub fn ContactPair::find_deepest_contact(
  self : ContactPair,
) -> (ContactManifold, Contact)? {
  let mut found = false
  let mut best_m = ContactManifold::{ normal: @core.Vec2::zero(), points: [] }
  let mut best_c = ContactManifoldPoint::{
    local_p1: @core.Vec2::zero(),
    local_p2: @core.Vec2::zero(),
    dist: 0.0F,
    fid1: Unknown,
    fid2: Unknown,
    data: ContactData::default(),
  }
  let mut best_dist = 0.0F
  for mi in 0.. ContactForceEvent {
  fn vec2_scale(v : @core.Vec2, s : @core.Real) -> @core.Vec2 {
    Vec2(v.x * s, v.y * s)
  }

  if dt <= 0.0F {
    return ContactForceEvent(
      collider1,
      collider2,
      @core.Vec2::zero(),
      total_force_magnitude,
      @core.Vec2::zero(),
      0.0F,
    )
  }
  let mut total_impulse = @core.Vec2::zero()
  let mut max_impulse = 0.0F
  let mut max_dir = @core.Vec2::zero()
  let manifolds = pair.manifolds()
  for mi in 0.. max_impulse {
        max_impulse = imp
        max_dir = m.normal()
      }
    }
    total_impulse = total_impulse.add(vec2_scale(m.normal(), manifold_impulse))
  }
  let inv_dt = 1.0F / dt
  ContactForceEvent(
    collider1,
    collider2,
    vec2_scale(total_impulse, inv_dt),
    total_force_magnitude,
    max_dir,
    max_impulse * inv_dt,
  )
}

///|
/// Equivalent broad-phase type alias for Rapier's `DefaultBroadPhase`.
pub type DefaultBroadPhase = BroadPhaseBvh

///|
/// Index types used by Rapier's interaction graphs.
pub type ColliderGraphIndex = Int

///|
pub type RigidBodyGraphIndex = Int

///|
pub type TemporaryInteractionIndex = Int

///|
/// Minimal interaction-graph surface for pub parity with Rapier.
pub struct InteractionGraph[T] {
  interactions : Array[(ColliderHandle, ColliderHandle, T)]
}

///|
pub fn[T] InteractionGraph::InteractionGraph() -> InteractionGraph[T] {
  { interactions: [] }
}

///|
pub fn[T] InteractionGraph::raw_graph(
  self : InteractionGraph[T],
) -> Array[(ColliderHandle, ColliderHandle, T)] {
  let out : Array[(ColliderHandle, ColliderHandle, T)] = []
  for i in 0.. Array[(ColliderHandle, ColliderHandle, T)] {
  self.raw_graph()
}

///|
pub fn[T] InteractionGraph::index_interaction(
  self : InteractionGraph[T],
  a : ColliderHandle,
  b : ColliderHandle,
) -> Int? {
  for i in 0.. T? {
  match self.index_interaction(a, b) {
    None => None
    Some(i) => Some(self.interactions[i].2)
  }
}

///|
/// MoonBit does not currently expose `&mut` references; this returns a copy for parity.
pub fn[T] InteractionGraph::interaction_pair_mut(
  self : InteractionGraph[T],
  a : ColliderHandle,
  b : ColliderHandle,
) -> T? {
  self.interaction_pair(a, b)
}

///|
pub fn[T] InteractionGraph::interactions_with(
  self : InteractionGraph[T],
  collider : ColliderHandle,
) -> Array[(ColliderHandle, ColliderHandle, T)] {
  let out : Array[(ColliderHandle, ColliderHandle, T)] = []
  for i in 0.. Array[(ColliderHandle, ColliderHandle, T)] {
  let out : Array[(ColliderHandle, ColliderHandle, T)] = []
  for i in 0.. Array[(ColliderHandle, ColliderHandle, T)] {
  // See `interaction_pair_mut` note.
  self.interactions_with(collider)
}

///|
fn handle_id_equals(a : ColliderHandle, b : ColliderHandle) -> Bool {
  a.id == b.id
}

///|
fn contains_handle_id(
  handles : Array[ColliderHandle],
  target : ColliderHandle,
) -> Bool {
  for i in 0.. InteractionGraph[ContactPair] {
  { interactions: self.contacts }
}

///|
pub fn NarrowPhase::intersection_graph(
  self : NarrowPhase,
) -> InteractionGraph[IntersectionPair] {
  let out : Array[(ColliderHandle, ColliderHandle, IntersectionPair)] = []
  for i in 0.. (ColliderHandle, ColliderHandle, ContactPair)? {
  if index < 0 || index >= self.contacts.length() {
    None
  } else {
    Some(self.contacts[index])
  }
}

///|
pub fn NarrowPhase::contact_pair_unknown_gen(
  self : NarrowPhase,
  first : ColliderHandle,
  second : ColliderHandle,
) -> ContactPair? {
  for i in 0.. Array[(ColliderHandle, ColliderHandle, ContactPair)] {
  let out : Array[(ColliderHandle, ColliderHandle, ContactPair)] = []
  for i in 0.. IntersectionPair? {
  for i in 0.. Array[IntersectionPair] {
  let out : Array[IntersectionPair] = []
  for i in 0.. Unit {
  handle_user_changes_to_colliders(bodies, colliders, modified_colliders)
  if modified_colliders.length() == 0 {
    return
  }
  let kept_contacts : Array[(ColliderHandle, ColliderHandle, ContactPair)] = []
  for i in 0.. QueryDispatcher {
  self.query_dispatcher
}

///|
pub fn NarrowPhase::with_query_dispatcher(
  self : NarrowPhase,
  dispatcher : QueryDispatcher,
) -> NarrowPhase {
  self.query_dispatcher = dispatcher
  self
}