import { assert } from "../../../../core/assert.js";
import Vector1 from "../../../../core/geom/Vector1.js";
import { PathFollowerFlags } from "./PathFollowerFlags.js";

const DEFAULT_SPEED = 1;
const DEFAULT_ROTATION_SPEED = Infinity;

const DEFAULT_FLAGS = PathFollowerFlags.Active
    | PathFollowerFlags.WritePositionX
    | PathFollowerFlags.WritePositionY
    | PathFollowerFlags.WritePositionZ
    | PathFollowerFlags.WriteRotationX
    | PathFollowerFlags.WriteRotationY
    | PathFollowerFlags.WriteRotationZ
;

const DEFAULT_MAX_MOVE_DISTANCE = 100000;

class PathFollower {

    /**
     * Movement speed along the path in world units per second
     * @type {Vector1}
     */
    speed = new Vector1(DEFAULT_SPEED);

    /**
     * Speed at which path follower can adjust rotation in Rad/s
     * @type {Vector1}
     */
    rotationSpeed = new Vector1(DEFAULT_ROTATION_SPEED);

    /**
     *
     * @type {number}
     */
    flags = DEFAULT_FLAGS;

    /**
     * Maximum distance that the follower can move along the path in a single step
     * @type {number}
     */
    maxMoveDistance = DEFAULT_MAX_MOVE_DISTANCE;

    /**
     * Absolute position along the path
     * @type {number}
     */
    position = 0;


    get rotationAlignment() {
        throw new Error(`deprecated, use relevant flag instead`);
    }

    get positionWriting() {
        throw new Error(`deprecated, use relevant flag instead`);
    }

    get active() {
        console.warn('PathFollower.active is deprecated, use flags instead');
        return this.getFlag(PathFollowerFlags.Active);
    }

    set active(v) {
        console.warn('PathFollower.active is deprecated, use flags instead');
        this.writeFlag(PathFollowerFlags.Active, v);
    }

    get lock() {
        console.warn('PathFollower.lock is deprecated, use flags instead');
        return this.getFlag(PathFollowerFlags.Locked);
    }

    set lock(v) {
        console.warn('PathFollower.lock is deprecated, use flags instead');
        this.writeFlag(PathFollowerFlags.Locked, v);
    }

    /**
     *
     * @param {number|PathFollowerFlags} flag
     * @returns {void}
     */
    setFlag(flag) {
        this.flags |= flag;
    }

    /**
     *
     * @param {number|PathFollowerFlags} flag
     * @returns {void}
     */
    clearFlag(flag) {
        this.flags &= ~flag;
    }

    /**
     *
     * @param {number|PathFollowerFlags} flag
     * @param {boolean} value
     */
    writeFlag(flag, value) {
        assert.isBoolean(value, 'value');

        if (value) {
            this.setFlag(flag);
        } else {
            this.clearFlag(flag);
        }
    }

    /**
     *
     * @param {number|PathFollowerFlags} flag
     * @returns {boolean}
     */
    getFlag(flag) {
        return (this.flags & flag) === flag;
    }

    toJSON() {
        return {
            active: this.getFlag(PathFollowerFlags.Active),
            loop: this.getFlag(PathFollowerFlags.Loop),

            writePositionX: this.getFlag(PathFollowerFlags.WritePositionX),
            writePositionY: this.getFlag(PathFollowerFlags.WritePositionY),
            writePositionZ: this.getFlag(PathFollowerFlags.WritePositionZ),

            writeRotationX: this.getFlag(PathFollowerFlags.WriteRotationX),
            writeRotationY: this.getFlag(PathFollowerFlags.WriteRotationY),
            writeRotationZ: this.getFlag(PathFollowerFlags.WriteRotationZ),

            speed: this.speed.toJSON(),
            rotationSpeed: this.rotationSpeed.toJSON(),
            position: this.position,
            maxMoveDistance: this.maxMoveDistance
        };
    }

    fromJSON(json) {
        this.flags = DEFAULT_FLAGS;

        if (json.position !== undefined) {

            assert.isNumber(json.position, 'position');
            assert.isFinite(json.position, 'position');
            assert.notNaN(json.position, 'position');

            this.position = json.position;
        } else {
            this.position = 0;
        }

        if (typeof json.active === "boolean") {
            this.writeFlag(PathFollowerFlags.Active, json.active);
        }

        if (typeof json.loop === "boolean") {
            this.writeFlag(PathFollowerFlags.Loop, json.active);
        }

        if (typeof json.speed === "number") {
            this.speed.fromJSON(json.speed);
        } else {
            this.speed.set(DEFAULT_SPEED);
        }

        if (typeof json.rotationSpeed === "number") {
            this.rotationSpeed.fromJSON(json.rotationSpeed);
        } else {
            this.rotationSpeed.set(DEFAULT_ROTATION_SPEED);
        }

        if (typeof json.maxMoveDistance === "number") {
            this.maxMoveDistance = json.maxMoveDistance;
        } else {
            this.maxMoveDistance = DEFAULT_MAX_MOVE_DISTANCE;
        }

        if (json.writePositionX !== undefined) {
            this.writeFlag(PathFollowerFlags.WritePositionX, json.writePositionX);
        }
        if (json.writePositionY !== undefined) {
            this.writeFlag(PathFollowerFlags.WritePositionY, json.writePositionY);
        }
        if (json.writePositionZ !== undefined) {
            this.writeFlag(PathFollowerFlags.WritePositionZ, json.writePositionZ);
        }

        if (json.writeRotationX !== undefined) {
            this.writeFlag(PathFollowerFlags.WriteRotationX, json.writeRotationX);
        }
        if (json.writeRotationY !== undefined) {
            this.writeFlag(PathFollowerFlags.WriteRotationY, json.writeRotationY);
        }
        if (json.writeRotationZ !== undefined) {
            this.writeFlag(PathFollowerFlags.WriteRotationZ, json.writeRotationZ);
        }

    }

    /**
     *
     * @param json
     * @returns {PathFollower}
     */
    static fromJSON(json) {
        const r = new PathFollower();

        r.fromJSON(json);

        return r;
    }
}

PathFollower.typeName = "PathFollower";

export default PathFollower;


