// A collider shape, as an aspect on a Node2D. PURE GEOMETRY — attaching a Shape2D does not by itself
// make the node rigid or a sensor; pair it with a Physics2D (rigid body) or a Trigger2D (sensor), both
// of which require a Shape2D and turn its geometry into a Box2D fixture. The shape is also what pointer
// picking hits, so a node is clickable once it has a Shape2D + Physics2D/Trigger2D (a body to query).
//
//   wall.aspect(Shape2D, { box: [200, 16] }).aspect(Physics2D, { motion: 'static' })
//   goal.aspect(Shape2D, { circle: 20 }).aspect(Trigger2D)
//   hero.aspect(Shape2D, { capsule: { from: [0, 6], to: [0, 26], radius: 6 } }).aspect(Physics2D, { motion: 'dynamic' })
//
// With no geometry set, Shape2D derives a box from the node's sprite size ("auto").

import { Aspect } from "../core/Aspect"
import { Vec2, cx, cy, type Vec2Like } from "../math/vec"
import type { Node2D } from "./Node2D"

export class Shape2D extends Aspect<"shape", Node2D> {
  static readonly aspect = "shape"

  /** Box half-extents [hw, hh] (world units). */
  box?: Vec2Like
  /** Circle radius. */
  circle?: number
  /** Capsule between two local points, with a radius. */
  capsule?: { from: Vec2Like, to: Vec2Like, radius: number }
  /** Thin segment (an edge — for static ground/walls/slopes) between two local points. */
  segment?: { from: Vec2Like, to: Vec2Like }
  /** Convex polygon, up to 8 local points (a convex hull is computed). */
  polygon?: Vec2Like[]
  /** Local offset of the shape from the node origin. */
  offset?: Vec2Like

  /**
   * @internal — add this geometry as a fixture to `handle` (a body created by Physics2D/Trigger2D).
   * `sensor` selects a sensor (trigger) vs solid fixture; density/friction/restitution are the
   * material (ignored for sensors / segments only use friction+restitution).
   */
  _addTo(handle: number, sensor: boolean, density: number, friction: number, restitution: number): void {
    const ox = this.offset ? cx(this.offset) : 0
    const oy = this.offset ? cy(this.offset) : 0
    if (this.circle !== undefined) {
      _creator2d.physicsAddCircle(handle, this.circle, ox, oy, density, friction, restitution, sensor)
    } else if (this.capsule) {
      const { from, to, radius } = this.capsule
      _creator2d.physicsAddCapsule(handle, cx(from), cy(from), cx(to), cy(to), radius, density, friction, restitution, sensor)
    } else if (this.segment) {
      _creator2d.physicsAddSegment(handle, cx(this.segment.from), cy(this.segment.from), cx(this.segment.to), cy(this.segment.to), friction, restitution)
    } else if (this.polygon) {
      const pts = new Float32Array(this.polygon.length * 2)
      this.polygon.forEach((p, i) => { pts[i * 2] = cx(p); pts[i * 2 + 1] = cy(p) })
      _creator2d.physicsAddPolygon(handle, pts, this.polygon.length, density, friction, restitution, sensor)
    } else {
      // box: explicit half-extents, or "auto" from the node's sprite size (fallback 8×8).
      let hw = 8, hh = 8
      if (this.box) {
        hw = cx(this.box); hh = cy(this.box)
      } else {
        const size = (this.node as { size?: Vec2 }).size
        if (size) { hw = size.x * 0.5; hh = size.y * 0.5 }
      }
      _creator2d.physicsAddBox(handle, hw, hh, ox, oy, density, friction, restitution, sensor)
    }
  }
}
