import { Vector3 } from "three";

import * as utils from "./../engine/engine_three_utils.js";
import { Behaviour, GameObject } from "./Component.js";

/**
 * The [BasicIKConstraint](https://engine.needle.tools/docs/api/BasicIKConstraint) provides simple two-bone inverse kinematics.
 * Positions this GameObject as a "joint" between `from` and `to` targets,
 * using a `hint` object to determine the bend direction.
 *
 * **Use cases:**
 * - Simple arm/leg IK (elbow/knee positioning)
 * - Mechanical linkages
 * - Procedural animation joints
 *
 * **How it works:**
 * - Calculates joint position based on `desiredDistance` (bone length)
 * - Uses `hint` to determine which way the joint bends
 * - Automatically handles stretching when targets are too far apart
 *
 * @example Setup basic limb IK
 * ```ts
 * // Attach to the elbow/knee joint object
 * const ik = elbowJoint.addComponent(BasicIKConstraint);
 * // Configure via serialized properties in editor:
 * // - from: shoulder/hip
 * // - to: wrist/ankle
 * // - hint: control point for bend direction
 * ```
 *
 * @summary Two-bone inverse kinematics constraint
 * @category Animation and Sequencing
 * @group Components
 * @see {@link AlignmentConstraint} for simpler alignment
 */
export class BasicIKConstraint extends Behaviour {

    private from!: GameObject;
    private to!: GameObject;
    private hint!: GameObject;
    private desiredDistance: number = 1;

    onEnable(): void {
        // console.log(this);
    }

    update() {
        if (!this.from || !this.to || !this.hint) return;

        // console.log(this);

        // find center
        const toPos = utils.getWorldPosition(this.to).clone();
        const fromPos = utils.getWorldPosition(this.from).clone();
        const dist = toPos.distanceTo(fromPos);

        const dir0 = toPos.clone();
        dir0.sub(fromPos);
        const center = fromPos.clone();
        center.add(toPos);
        center.multiplyScalar(0.5);
        
        // find direction we should offset in
        const hintDir = utils.getWorldPosition(this.hint).clone();
        hintDir.sub(center);
        
        const offsetDir = new Vector3();
        offsetDir.crossVectors(hintDir, dir0);
        offsetDir.crossVectors(dir0, offsetDir);
        offsetDir.normalize();

        const halfDist = dist * 0.5;
        const stretchDistance = Math.max(this.desiredDistance, halfDist);
        const offsetLength = Math.sqrt(stretchDistance * stretchDistance - halfDist * halfDist);
        
        const resultPos = offsetDir.clone();
        resultPos.multiplyScalar(offsetLength);
        resultPos.add(center);
        utils.setWorldPosition(this.gameObject, resultPos);

        const lookPos = center.clone();
        lookPos.sub(offsetDir);
        this.gameObject.lookAt(lookPos);
    }
}