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

///|
/// Bevy-like 2D joint authoring components.
///
/// Notes:
/// - v1 assumes at most one joint component per entity.
/// - Joints are inserted only when both entities have `RapierBodyHandle2d`.

///|
// ECS component marker for codegen (`mgstudio gen`).
#ecs.component
pub struct ImpulseJoint2d {
  other : @core.Entity
  data : @dynamics.GenericJoint
  wake_up : Bool
}

///|
pub fn ImpulseJoint2d::new(
  other : @core.Entity,
  data : @dynamics.GenericJoint,
) -> ImpulseJoint2d {
  { other, data, wake_up: true }
}

///|
pub fn ImpulseJoint2d::with_wake_up(
  self : ImpulseJoint2d,
  wake_up : Bool,
) -> ImpulseJoint2d {
  ImpulseJoint2d::{ ..self, wake_up, }
}

///|
// ECS component marker for codegen (`mgstudio gen`).
#ecs.component
pub struct RapierImpulseJointHandle2d {
  handle : @dynamics.ImpulseJointHandle
  other : @core.Entity
}

///|
pub fn RapierImpulseJointHandle2d::new(
  handle : @dynamics.ImpulseJointHandle,
  other : @core.Entity,
) -> RapierImpulseJointHandle2d {
  { handle, other }
}

///|
// ECS component marker for codegen (`mgstudio gen`).
#ecs.component
pub struct MultibodyJoint2d {
  other : @core.Entity
  /// moon_rapier currently models multibody joints as revolute joints.
  data : @dynamics.RevoluteJoint
  wake_up : Bool
  /// If true, insert using `MultibodyJointSet::insert_kinematic`.
  kinematic : Bool
}

///|
pub fn MultibodyJoint2d::new(
  other : @core.Entity,
  data : @dynamics.RevoluteJoint,
) -> MultibodyJoint2d {
  { other, data, wake_up: true, kinematic: false }
}

///|
pub fn MultibodyJoint2d::with_wake_up(
  self : MultibodyJoint2d,
  wake_up : Bool,
) -> MultibodyJoint2d {
  MultibodyJoint2d::{ ..self, wake_up, }
}

///|
pub fn MultibodyJoint2d::with_kinematic(
  self : MultibodyJoint2d,
  kinematic : Bool,
) -> MultibodyJoint2d {
  MultibodyJoint2d::{ ..self, kinematic, }
}

///|
// ECS component marker for codegen (`mgstudio gen`).
#ecs.component
pub struct RapierMultibodyJointHandle2d {
  handle : @dynamics.MultibodyJointHandle
  other : @core.Entity
}

///|
pub fn RapierMultibodyJointHandle2d::new(
  handle : @dynamics.MultibodyJointHandle,
  other : @core.Entity,
) -> RapierMultibodyJointHandle2d {
  { handle, other }
}

///|
/// Mgstudio-friendly 2D joint builders that produce `@dynamics.GenericJoint`.
///
/// These helpers mirror Bevy+Rapier's intent: keep user code in mgstudio math
/// types and let the integration layer handle conversions.

///|
pub fn joint2d_fixed(
  local_anchor1 : @math.Vec2,
  local_anchor2 : @math.Vec2,
) -> @dynamics.GenericJoint {
  let j = @dynamics.FixedJointBuilder::new()
    .local_anchor1(mg_vec2_to_rapier(local_anchor1))
    .local_anchor2(mg_vec2_to_rapier(local_anchor2))
    .build()
  @dynamics.GenericJoint::from_fixed(j)
}

///|
pub fn joint2d_revolute(
  local_anchor1 : @math.Vec2,
  local_anchor2 : @math.Vec2,
) -> @dynamics.GenericJoint {
  let j = @dynamics.RevoluteJointBuilder::new()
    .local_anchor1(mg_vec2_to_rapier(local_anchor1))
    .local_anchor2(mg_vec2_to_rapier(local_anchor2))
    .build()
  @dynamics.GenericJoint::from_revolute(j)
}

///|
pub fn joint2d_prismatic(
  local_axis : @math.Vec2,
  local_anchor1 : @math.Vec2,
  local_anchor2 : @math.Vec2,
) -> @dynamics.GenericJoint {
  let j = @dynamics.PrismaticJointBuilder::new(mg_vec2_to_rapier(local_axis))
    .local_anchor1(mg_vec2_to_rapier(local_anchor1))
    .local_anchor2(mg_vec2_to_rapier(local_anchor2))
    .build()
  @dynamics.GenericJoint::from_prismatic(j)
}

///|
pub fn joint2d_rope(
  max_distance : Float,
  local_anchor1 : @math.Vec2,
  local_anchor2 : @math.Vec2,
) -> @dynamics.GenericJoint {
  let j = @dynamics.RopeJointBuilder::new(max_distance)
    .local_anchor1(mg_vec2_to_rapier(local_anchor1))
    .local_anchor2(mg_vec2_to_rapier(local_anchor2))
    .build()
  @dynamics.GenericJoint::from_rope(j)
}

///|
pub fn joint2d_pin_slot(
  local_axis : @math.Vec2,
  local_anchor1 : @math.Vec2,
  local_anchor2 : @math.Vec2,
) -> @dynamics.GenericJoint {
  let j = @dynamics.PinSlotJointBuilder::new(mg_vec2_to_rapier(local_axis))
    .local_anchor1(mg_vec2_to_rapier(local_anchor1))
    .local_anchor2(mg_vec2_to_rapier(local_anchor2))
    .build()
  @dynamics.GenericJoint::from_pin_slot(j)
}