import List from "./List.js";

/**
 * @template T
 */
export class ListForwarder {
    /**
     * Where the data ends up
     * @type {List<T>}
     * @private
     */
    #destination = new List();

    /**
     * Where to take data from
     * @type {List<T>[]}
     * @private
     */
    #sources = [];

    /**
     *
     * @type {boolean}
     * @private
     */
    #is_linked = false;

    /**
     *
     * @param {List<T>} v
     */
    set destination(v) {
        if (this.#is_linked) {
            // already linked
            throw new Error('Invalid state: linked');
        }

        this.#destination = v;
    }

    /**
     *
     * @return {List<T>}
     */
    get destination() {
        return this.#destination;
    }

    /**
     *
     * @param {List<T>} source
     * @return {boolean}
     */
    add(source) {
        this.#sources.push(source);

        if (this.#is_linked) {
            this.#link_one(source);
        }

        return true;
    }

    /**
     *
     * @param {List<T>} source
     * @private
     */
    #link_one(source) {
        source.on.added.add(this.#destination.add, this.#destination);
        source.on.removed.add(this.#destination.removeOneOf, this.#destination);

        // move data to destination
        source.forEach(this.#destination.add, this.#destination);
    }

    /**
     *
     * @param {List<T>} source
     * @private
     */
    #unlink_one(source) {

        source.on.added.remove(this.#destination.add, this.#destination);
        source.on.removed.remove(this.#destination.removeOneOf, this.#destination);

        // remove all data from destination
        source.forEach(this.#destination.removeOneOf, this.#destination);
    }

    /**
     *
     * @param {List<T>} source
     * @return {boolean}
     */
    remove(source) {
        const i = this.#sources.indexOf(source);

        if (i === -1) {
            return false;
        }

        if (this.#is_linked) {
            this.#unlink_one(source);
        }

        this.#sources.splice(i, 1);

        return true;
    }

    link() {
        if (this.#is_linked) {
            return;
        }

        if (this.#destination === null) {
            throw new Error('Illegal state: destination not set');
        }

        this.#is_linked = true;

        const sources = this.#sources;
        const n = sources.length;
        for (let i = 0; i < n; i++) {
            const source = sources[i];

            this.#link_one(source);
        }
    }

    unlink() {
        if (!this.#is_linked) {
            return;
        }

        this.#is_linked = false;

        const sources = this.#sources;
        const n = sources.length;

        for (let i = 0; i < n; i++) {
            const source = sources[i];

            this.#unlink_one(source);
        }
    }

    /**
     *
     * @return {boolean}
     */
    isLinked() {
        return this.#is_linked;
    }
}
