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

///|
/// Simple 2D physics debug rendering, aligned with Bevy+Rapier's debug render plugin.
///
/// This uses mgstudio's `render2d/gizmo` lines, and is disabled by default.

///|
pub struct Physics2dDebugRenderConfig {
  enabled : Bool
  colliders : Bool
  rigid_bodies : Bool
  impulse_joints : Bool
  collider_color : @render2d.Color
  sensor_color : @render2d.Color
  rigid_body_color : @render2d.Color
  joint_color : @render2d.Color
}

///|
pub fn Physics2dDebugRenderConfig::default() -> Physics2dDebugRenderConfig {
  {
    enabled: false,
    colliders: true,
    rigid_bodies: true,
    impulse_joints: true,
    collider_color: @render2d.Color::new(0.0F, 1.0F, 0.0F, 1.0F),
    sensor_color: @render2d.Color::new(0.0F, 1.0F, 1.0F, 1.0F),
    rigid_body_color: @render2d.Color::new(1.0F, 1.0F, 0.0F, 1.0F),
    joint_color: @render2d.Color::new(1.0F, 0.0F, 1.0F, 1.0F),
  }
}

///|
let physics2d_debug_render_state : Ref[Physics2dDebugRenderConfig] = Ref::new(
  Physics2dDebugRenderConfig::default(),
)

///|
pub fn physics2d_debug_render_config() -> Physics2dDebugRenderConfig {
  physics2d_debug_render_state.val
}

///|
pub fn physics2d_debug_render_set_config(
  config : Physics2dDebugRenderConfig,
) -> Unit {
  physics2d_debug_render_state.val = config
}

///|
pub fn physics2d_debug_render_set_enabled(enabled : Bool) -> Unit {
  let c = physics2d_debug_render_state.val
  physics2d_debug_render_state.val = Physics2dDebugRenderConfig::{
    ..c,
    enabled,
  }
}

///|
fn iso2_to_gizmo(iso : @rapier_core.Isometry2) -> @render2d.Isometry2d {
  @render2d.Isometry2d::new(
    rapier_vec2_to_mg(iso.translation),
    iso.rotation.angle(),
  )
}

///|
fn transform_point(
  iso : @rapier_core.Isometry2,
  p : @rapier_core.Vec2,
) -> @math.Vec2 {
  rapier_vec2_to_mg(iso.transform_point(p))
}

///|
fn draw_shape_2d(
  gizmos : @render2d.Gizmos,
  iso : @rapier_core.Isometry2,
  shape : @collision.Shape,
  color : @render2d.Color,
) -> Unit {
  match shape {
    Ball(r) => ignore(gizmos.circle_2d(iso2_to_gizmo(iso), r, color))
    Cuboid(hx, hy) =>
      gizmos.rect_2d(
        iso2_to_gizmo(iso),
        @math.Vec2::new(2.0F * hx, 2.0F * hy),
        color,
      )
    CapsuleX(half_height, radius) =>
      ignore(
        gizmos
        .rounded_rect_2d(
          iso2_to_gizmo(iso),
          @math.Vec2::new(2.0F * (half_height + radius), 2.0F * radius),
          color,
        )
        .corner_radius(radius),
      )
    CapsuleY(half_height, radius) =>
      ignore(
        gizmos
        .rounded_rect_2d(
          iso2_to_gizmo(iso),
          @math.Vec2::new(2.0F * radius, 2.0F * (half_height + radius)),
          color,
        )
        .corner_radius(radius),
      )
    Segment(a, b) =>
      gizmos.line_2d(transform_point(iso, a), transform_point(iso, b), color)
    Polyline(verts, edges) =>
      match edges {
        Some(es) =>
          for e in es {
            let (i, j) = e
            if i >= 0 && i < verts.length() && j >= 0 && j < verts.length() {
              gizmos.line_2d(
                transform_point(iso, verts[i]),
                transform_point(iso, verts[j]),
                color,
              )
            }
          }
        None => {
          let ps : Array[@math.Vec2] = []
          for v in verts {
            ps.push(transform_point(iso, v))
          }
          if ps.length() >= 2 {
            gizmos.linestrip_2d(ps, color)
          }
        }
      }
    ConvexPolygon(verts) => {
      let ps : Array[@math.Vec2] = []
      for v in verts {
        ps.push(transform_point(iso, v))
      }
      if ps.length() >= 3 {
        gizmos.lineloop_2d(ps, color)
      }
    }
    TriMesh(verts, tris) =>
      for tri in tris {
        let (i, j, k) = tri
        if i >= 0 &&
          j >= 0 &&
          k >= 0 &&
          i < verts.length() &&
          j < verts.length() &&
          k < verts.length() {
          let a = transform_point(iso, verts[i])
          let b = transform_point(iso, verts[j])
          let c = transform_point(iso, verts[k])
          gizmos.line_2d(a, b, color)
          gizmos.line_2d(b, c, color)
          gizmos.line_2d(c, a, color)
        }
      }
    Compound(parts) =>
      for part in parts {
        let (local_iso, sub) = part
        draw_shape_2d(gizmos, iso.mul(local_iso), sub, color)
      }
  }
}

///|
pub fn[W : HasRes_Physics2dContext] physics2d_debug_render_system(
  world : W,
) -> Unit {
  let config = physics2d_debug_render_state.val
  if !config.enabled {
    return
  }
  let ctx_res = HasRes_Physics2dContext::get_physics2d_context_resource(world)
  guard ctx_res.get_ref() is Some(ctx_ref) else { return }
  let ctx = ctx_ref.val
  let gizmos = @render2d.Gizmos::new()
  if config.colliders {
    for pair in ctx.colliders.iter_enabled() {
      let (_h, col) = pair
      let iso = col.position()
      let color = if col.is_sensor() {
        config.sensor_color
      } else {
        config.collider_color
      }
      draw_shape_2d(gizmos, iso, col.shape(), color)
    }
  }
  if config.rigid_bodies {
    for pair in ctx.rigid_bodies.iter() {
      let (_h, body) = pair
      ignore(
        gizmos.cross_2d(
          iso2_to_gizmo(body.position()),
          0.15F,
          config.rigid_body_color,
        ),
      )
    }
  }
  if config.impulse_joints {
    for pair in ctx.impulse_joints.iter() {
      let (_h, j) = pair
      let body1 = j.body1
      let body2 = j.body2
      guard ctx.rigid_bodies.get(body1) is Some(b1) else { continue }
      guard ctx.rigid_bodies.get(body2) is Some(b2) else { continue }
      let a1 = transform_point(b1.position(), j.data.local_anchor1())
      let a2 = transform_point(b2.position(), j.data.local_anchor2())
      gizmos.line_2d(a1, a2, config.joint_color)
    }
  }
}