import { Object3D, Quaternion, Vector3 } from "three";

export class RotationC {
    private static readonly completeAngle = 0.01;

    private readonly target: Object3D;
    private readonly speed: number;
    private readonly targetDirection = new Vector3(0, 0, 1);
    private readonly worldPosition = new Vector3();
    private readonly currentQ = new Quaternion();
    private readonly targetQ = new Quaternion();
    private readonly lookAtPoint = new Vector3();

    constructor(target: Object3D, speed: number = 5) {
        this.target = target;
        this.speed = speed;
        this.currentQ.copy(target.quaternion);
        this.targetQ.copy(target.quaternion);
        target.getWorldDirection(this.targetDirection);
        this.targetDirection.y = 0;
        if (this.targetDirection.lengthSq() > 0) {
            this.targetDirection.normalize();
        } else {
            this.targetDirection.set(0, 0, 1);
        }
    }

    setTargetDirection(direction: Vector3) {
        if (direction.lengthSq() === 0) return;
        this.targetDirection.copy(direction).normalize();
    }

    setTargetWorldPosition(worldPosition: Vector3) {
        this.target.getWorldPosition(this.worldPosition);
        this.targetDirection.set(
            worldPosition.x - this.worldPosition.x,
            0,
            worldPosition.z - this.worldPosition.z,
        );
        if (this.targetDirection.lengthSq() === 0) return;
        this.targetDirection.normalize();
    }

    syncToCurrentFacing() {
        this.target.getWorldDirection(this.targetDirection);
        this.targetDirection.y = 0;
        if (this.targetDirection.lengthSq() === 0) return;
        this.targetDirection.normalize();
    }

    isComplete() {
        this.computeTargetQuaternion(this.targetQ);
        return this.currentQ.angleTo(this.targetQ) < RotationC.completeAngle;
    }

    update(delta: number) {
        this.computeTargetQuaternion(this.targetQ);

        if (this.currentQ.angleTo(this.targetQ) < RotationC.completeAngle) {
            this.target.quaternion.copy(this.targetQ);
            return;
        }

        this.target.quaternion.slerp(this.targetQ, delta * this.speed);
        this.currentQ.copy(this.target.quaternion);
    }

    private computeTargetQuaternion(out: Quaternion) {
        this.currentQ.copy(this.target.quaternion);

        this.lookAtPoint.copy(this.target.position).add(this.targetDirection);
        this.target.lookAt(this.lookAtPoint);
        out.copy(this.target.quaternion);
        this.target.quaternion.copy(this.currentQ);
    }
}
