import { assert } from "../../assert.js";
import { computeHashFloat } from "../../primitives/numbers/computeHashFloat.js";


/**
 * Linear polynomial modifier in form of : a*x + b
 * @example if x is 5, and modifier a=3 and b=7 then we get 3*5 + 7 = 23
 */
class LinearModifier {
    /**
     * @param {number} [a=1] gradient (slope)
     * @param {number} [b=0] constant (intercept)
     * @constructor
     */
    constructor(a = 1, b = 0) {
        assert.isNumber(a, 'a');
        assert.isNumber(b, 'b');

        /**
         * gradient (slope)
         * @readonly
         * @type {number}
         */
        this.a = a;

        /**
         * constant (intercept)
         * @readonly
         * @type {number}
         */
        this.b = b;

        /**
         *
         * @type {number}
         */
        this.source = 0;

        /**
         * Whenever this modifier is grated by another persistent effect
         * @type {boolean}
         */
        this.transient = false;
    }

    /**
     *
     * @param {LinearModifier} other
     */
    copy(other) {
        this.a = other.a;
        this.b = other.b;
        this.source = other.source;
        this.transient = other.transient;
    }

    /**
     *
     * @return {LinearModifier}
     */
    clone() {
        const r = new LinearModifier();

        r.copy(this);

        return r;
    }

    /**
     * Combines other modifier onto this one.
     * @param {LinearModifier} other
     */
    add(other) {
        this.a += (other.a - 1);
        this.b += other.b;
    }

    /**
     *
     * @param {LinearModifier} other
     * @returns {boolean}
     */
    equals(other) {
        return this.a === other.a
            && this.b === other.b
            && this.source === other.source
            && this.transient === other.transient;
    }

    /**
     *
     * @return {number}
     */
    hash() {
        return computeHashFloat(this.a)
            ^ computeHashFloat(this.b)
            ^ this.source
            ^ (this.transient ? 0 : 1);
    }

    toString() {
        return `LinearModifier{ a:${this.a}, b:${this.b} }`;
    }

    toJSON() {
        return {
            a: this.a,
            b: this.b,
            source: this.source,
            transient: this.transient
        };
    }

    fromJSON({
                 a = 1,
                 b = 0,
                 source = 0,
                 transient = false
             }) {
        this.a = a;
        this.b = b;
        this.source = source;
        this.transient = transient;
    }
}

/**
 * @readonly
 * @type {LinearModifier}
 */
LinearModifier.CONSTANT_ONE = Object.freeze(new LinearModifier(0, 1));
/**
 * @readonly
 * @type {LinearModifier}
 */
LinearModifier.CONSTANT_ZERO = Object.freeze(new LinearModifier(0, 0));

export default LinearModifier;
