import { assert } from "../../../assert.js";
import { computeStringHash } from "../../../primitives/strings/computeStringHash.js";
import { ReactiveExpression } from "./ReactiveExpression.js";

/**
 * @template T,R
 */
export class ReactiveUnaryExpression extends ReactiveExpression {

    /**
     *
     * @type {ReactiveExpression<T>}
     */
    source = null;

    copy(other) {
        this.disconnect();

        if (other.source !== null) {
            this.connect(other.source.clone());
        }
    }

    update(v) {
        const result = this.transform(v);

        this.onChanged.dispatch(result);
    }

    traverse(visitor, thisArg) {
        super.traverse(visitor, thisArg);

        this.source.traverse(visitor, thisArg);
    }

    connect(source) {
        assert.equal(this.source, null, 'source is already set');

        this.source = source;

        this.source.onChanged.add(this.update, this);
    }

    disconnect() {
        if (this.source === null) {
            return;
        }

        this.source.onChanged.remove(this.update, this);

        this.source = null;
    }

    /**
     * @protected
     */
    transform(v) {
        throw new Error('ReactiveUnaryExpression.transform is not overridden');
    }

    evaluate(scope) {
        const source_value = this.source.evaluate(scope);

        return this.transform(source_value);
    }

    /**
     *
     * @param {ReactiveExpression} other
     * @returns {boolean}
     */
    equals(other) {
        return other.isUnaryExpression
            && this.source.equals(other.source)
            && super.equals(other)
            ;
    }

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

    getValue() {
        return this.transform(this.source.getValue());
    }
}

/**
 * @readonly
 * @type {boolean}
 */
ReactiveUnaryExpression.prototype.isUnaryExpression = true;
