import { assert } from "../../../../core/assert.js";
import Quaternion from "../../../../core/geom/Quaternion.js";
import Vector3 from "../../../../core/geom/Vector3.js";
import { lerp } from "../../../../core/math/lerp.js";
import { Transform } from "../../../ecs/transform/Transform.js";
import { BehaviorStatus } from "../BehaviorStatus.js";
import { EntityBehavior } from "./EntityBehavior.js";

export class OrbitingBehavior extends EntityBehavior {

    /**
     * Radians per second.
     * Can be negative for counter-clockwise rotation.
     * @type {number}
     */
    rate = Math.PI;

    /**
     *
     * @type {number}
     */
    radius = 1;

    /**
     * Initial offset in radians
     * @type {number}
     */
    phase = 0;

    /**
     *
     * @type {number}
     */
    angle = 0;

    /**
     * Offset from the origin
     * @type {Vector3}
     */
    offset = new Vector3();

    /**
     *
     * @type {number}
     */
    parent = -1;

    /**
     *
     * @type {number}
     */
    easing = 1;

    orientation = new Quaternion();

    /**
     *
     * @type {Vector3}
     */
    center = new Vector3();


    /**
     *
     * @type {Quaternion}
     */
    lookRotation = new Quaternion();

    /**
     *
     * @param {Vector3} [center]
     * @param {number} [radius]
     * @param {number} [rate]
     * @returns {OrbitingBehavior}
     */
    static from({
                    center = Vector3.zero,
                    radius = 1,
                    rate = 1
                }) {

        assert.defined(center, 'center')
        assert.isNumber(radius, 'radius')
        assert.isNumber(rate, 'rate')

        const r = new OrbitingBehavior();

        r.offset.copy(center);
        r.radius = radius;
        r.rate = rate;

        return r;
    }

    initialize(context) {
        super.initialize(context);

        const easing = this.easing;

        this.easing = 1;

        this.tick(0);

        this.easing = easing;
    }

    tick(timeDelta) {

        const angleDelta = timeDelta * this.rate;

        this.angle += angleDelta;

        const entity = this.context.entity;
        const ecd = this.context.ecd;

        /**
         *
         * @type {Transform}
         */
        const transform = ecd.getComponent(entity, Transform);

        if (transform === undefined) {
            return BehaviorStatus.Failed;
        }

        const a = this.angle + this.phase;

        const cos = Math.cos(a);
        const sin = Math.sin(a);

        let center_x = this.offset.x;
        let center_y = this.offset.y;
        let center_z = this.offset.z;

        const r = this.radius;


        let x = r * cos;
        let y = 0;
        let z = r * sin;

        const q = this.orientation;

        //transform point into quaternion
        const qx = q.x, qy = q.y, qz = q.z, qw = q.w;

        // calculate quat * vector

        var ix = qw * x + qy * z;
        var iy = qz * x - qx * z;
        var iz = qw * z - qy * x;
        var iw = -qx * x - qz * z;

        // calculate result * inverse quat

        x = ix * qw + iw * -qx + iy * -qz - iz * -qy;
        y = iy * qw + iw * -qy + iz * -qx - ix * -qz;
        z = iz * qw + iw * -qz + ix * -qy - iy * -qx;


        if (this.parent !== -1 && ecd.entityExists(this.parent)) {

            const parentTransform = ecd.getComponent(this.parent, Transform);

            center_x += parentTransform.position.x;
            center_y += parentTransform.position.y;
            center_z += parentTransform.position.z;


            const parentScape = parentTransform.scale;

            x *= parentScape.x;
            y *= parentScape.y;
            z *= parentScape.z;
        }

        center_x = lerp(this.center.x, center_x, this.easing);
        center_y = lerp(this.center.y, center_y, this.easing);
        center_z = lerp(this.center.z, center_z, this.easing);

        this.center.set(center_x, center_y, center_z);

        const _x = center_x + x;
        const _y = center_y + y;
        const _z = center_z + z;

        const p = transform.position;

        const old_x = p.x;
        const old_y = p.y;
        const old_z = p.z;

        if (old_x !== _x || old_y !== _y || old_z !== _z) {

            transform.rotation._lookRotation(_x - old_x, _y - old_y, _z - old_z, 0, 1, 0);
            transform.rotation.multiplyQuaternions(transform.rotation, this.lookRotation);

        }

        p.set(_x, _y, _z);

        return BehaviorStatus.Running;
    }
}
