import Signal from "../../../events/signal/Signal.js";
import DataType from "../../../parser/simple/DataType.js";
import { computeStringHash } from "../../../primitives/strings/computeStringHash.js";

/**
 * @template T
 */
export class ReactiveExpression {

    /**
     * @readonly
     * @type {Signal<T,T>}
     */
    onChanged = new Signal();


    copy(other) {
        throw new Error('Not Implemented');
    }

    clone() {
        throw new Error('Not Implemented');
    }

    /**
     *
     * @param {ReactiveExpression} other
     * @returns {boolean}
     */
    equals(other) {
        if (this.dataType !== other.dataType) {
            return false;
        }

        return true;
    }

    /**
     *
     * @returns {string}
     */
    toCode() {
        return "";
    }

    /**
     *
     * @returns {number}
     */
    hash() {
        return computeStringHash(this.dataType);
    }

    /**
     * @returns {T|boolean|number|string}
     */
    getValue() {
        throw new Error('ReactiveExpression.getValue not overridden');
    }

    /**
     *
     * @param {function(node:ReactiveExpression)} visitor
     * @param {*} [thisArg]
     */
    traverse(visitor, thisArg) {
        visitor.call(thisArg, this);
    }

    /**
     *
     * @return {number}
     */
    computeTreeSize() {
        let result = 0;

        this.traverse(() => result++);

        return result;
    }

    /**
     *
     * @param {function(T,T)} handler
     * @param {*} [thisArg]
     * @returns {this}
     */
    process(handler, thisArg) {
        this.onChanged.add(handler, thisArg);

        const v = this.getValue();

        handler.call(thisArg, v, v);

        return this;
    }

    /**
     *
     * @param {Object} scope
     * @returns {boolean|number|string}
     */
    evaluate(scope) {
        return 0;
    }
}

ReactiveExpression.prototype.dataType = DataType.Any;

/**
 * @readonly
 * @type {boolean}
 */
ReactiveExpression.prototype.isReactiveExpression = true;
