"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const log_1 = require("../log/log");
class Chainable {
    constructor() {
        // IMPLEMENTATION
        this.downstreamChainables = [];
    }
    chain(chainable) {
        this.downstreamChainables.push(chainable);
        return chainable;
    }
    branch(branchFn) {
        branchFn(this.me());
        return this.me();
    }
    shouldLoadNewItems() {
        return this.downstreamChainables.length > 0;
    }
    give(error, item) {
        if (this.downstreamChainables.length == 0)
            return false;
        // slice copies object references into the new array. Both the original and new array refer to the same object.
        const frozenDownstream = this.downstreamChainables.slice();
        for (let i = 0; i < frozenDownstream.length; i++) {
            const downstreamChainable = frozenDownstream[i];
            const result = downstreamChainable.doTake(error, item);
            if (!result) {
                const consumerIndex = this.downstreamChainables.indexOf(downstreamChainable);
                if (consumerIndex !== -1)
                    this.downstreamChainables.splice(consumerIndex, 1);
            }
        }
        return this.shouldLoadNewItems();
    }
    doTake(error, item) {
        if (typeof (item) === "undefined" && !error)
            throw new Error("Got undefined item");
        try {
            return this.take(error, item);
        }
        catch (error) {
            this.give(error, undefined);
            return false;
        }
    }
}
exports.Chainable = Chainable;
/**
 * Generic filter that can be chained.
 */
class ChainableFilter extends Chainable {
}
exports.ChainableFilter = ChainableFilter;
/**
 * Generic transformer that can be chained.
 */
class ChainableTransformer extends Chainable {
}
exports.ChainableTransformer = ChainableTransformer;
/**
 * Supplier that does not accept take.
 */
class ChainableSupplier extends Chainable {
    take(error, item) {
        throw new Error("Supplier cannot take.");
    }
}
exports.ChainableSupplier = ChainableSupplier;
/**
 * Taker that cannot be branched or chained.
 */
class ChainableTaker extends Chainable {
    chain(chainable) {
        throw new Error("Cannot chain from Taker.");
    }
    branch(branchFn) {
        throw new Error("Cannot branch from Taker.");
    }
    give(error, item) {
        throw new Error("Taker has no downstream. Cannot call his give");
    }
}
exports.ChainableTaker = ChainableTaker;
class SimpleTaker extends ChainableTaker {
    constructor(callback) {
        super();
        this.onErrorCallback = (error) => { return false; };
        this.callback = callback;
    }
    catch(fn) {
        this.onErrorCallback = fn;
    }
    me() {
        return this;
    }
    onErrorCought(error) {
        this.onErrorCallback(error);
    }
    take(error, item) {
        if (error) {
            return this.onErrorCallback(error);
        }
        else
            return this.callback(item);
    }
    doTake(error, item) {
        if (typeof (item) === "undefined" && !error)
            throw new Error("Got undefined item");
        try {
            return this.take(error, item);
        }
        catch (error) {
            this.onErrorCallback(error);
            log_1.Log.log().exception(log_1.Log.level.error, error);
            return false;
        }
    }
}
exports.SimpleTaker = SimpleTaker;
//# sourceMappingURL=Chainable.js.map