"use strict";
const Behavior_1 = require("./Behavior");
class Stream {
    constructor() {
        this.eventListeners = [];
    }
    publish(a) {
        const list = this.eventListeners;
        const l = this.eventListeners.length;
        if (l === 1) {
            list[0].push(a);
        }
        else {
            for (let i = 0; i < l; ++i) {
                list[i].push(a);
            }
        }
    }
    ;
    set def(stream) {
        stream.eventListeners.push(...this.eventListeners);
        this.eventListeners = stream.eventListeners;
    }
    subscribe(fn) {
        this.eventListeners.push({ push: fn });
    }
    map(fn) {
        const e = new MapStream(fn);
        this.eventListeners.push(e);
        return e;
    }
    mapTo(val) {
        const s = new MapToStream(val);
        this.eventListeners.push(s);
        return s;
    }
    merge(otherStream) {
        const e = new SinkStream();
        this.eventListeners.push(e);
        otherStream.eventListeners.push(e);
        return e;
    }
    filter(fn) {
        const e = new FilterStream(fn);
        this.eventListeners.push(e);
        return e;
    }
    scanS(fn, startingValue) {
        return Behavior_1.fromFunction(() => new ScanStream(fn, startingValue, this));
    }
    scan(fn, init) {
        return Behavior_1.scan(fn, init, this);
    }
    unlisten(listener) {
        const l = this.eventListeners;
        const idx = l.indexOf(listener);
        if (idx !== -1) {
            if (idx !== l.length - 1) {
                l[idx] = l[l.length - 1];
            }
            l.length--; // remove the last element of the list
        }
    }
}
exports.Stream = Stream;
class SinkStream extends Stream {
    push(a) {
        this.publish(a);
    }
}
exports.SinkStream = SinkStream;
class MapStream extends Stream {
    constructor(fn) {
        super();
        this.fn = fn;
    }
    push(a) {
        this.publish(this.fn(a));
    }
}
class MapToStream extends Stream {
    constructor(val) {
        super();
        this.val = val;
    }
    push(a) {
        this.publish(this.val);
    }
}
class FilterStream extends Stream {
    constructor(fn) {
        super();
        this.fn = fn;
    }
    push(a) {
        if (this.fn(a)) {
            this.publish(a);
        }
    }
}
class ScanStream extends Stream {
    constructor(fn, last, source) {
        super();
        this.fn = fn;
        this.last = last;
        source.eventListeners.push(this);
    }
    push(a) {
        const val = this.last = this.fn(a, this.last);
        this.publish(val);
    }
}
function scanS(fn, startingValue, stream) {
    return Behavior_1.fromFunction(() => new ScanStream(fn, startingValue, stream));
}
exports.scanS = scanS;
class SnapshotStream extends Stream {
    constructor(behavior, stream) {
        super();
        this.behavior = behavior;
        stream.eventListeners.push(this);
    }
    push(a) {
        this.publish([a, Behavior_1.at(this.behavior)]);
    }
}
function snapshot(behavior, stream) {
    return new SnapshotStream(behavior, stream);
}
exports.snapshot = snapshot;
class SnapshotWithStream extends Stream {
    constructor(fn, behavior, stream) {
        super();
        this.fn = fn;
        this.behavior = behavior;
        stream.eventListeners.push(this);
    }
    push(a) {
        this.publish(this.fn(a, Behavior_1.at(this.behavior)));
    }
}
function snapshotWith(fn, behavior, stream) {
    return new SnapshotWithStream(fn, behavior, stream);
}
exports.snapshotWith = snapshotWith;
class SwitchBehaviorStream extends Stream {
    constructor(b) {
        super();
        this.b = b;
        b.addListener(this);
        const cur = this.currentSource = Behavior_1.at(b);
        cur.eventListeners.push(this);
    }
    push(a, changer) {
        if (changer === this.b) {
            this.doSwitch(a);
        }
        else {
            this.publish(a);
        }
    }
    doSwitch(newStream) {
        this.currentSource.unlisten(this);
        newStream.eventListeners.push(this);
        this.currentSource = newStream;
    }
}
/**
 * Takes a stream valued behavior and returns at stream that emits
 * values from the current stream at the behavior.
 */
function switchStream(b) {
    return new SwitchBehaviorStream(b);
}
exports.switchStream = switchStream;
function mergeList(ss) {
    return ss.reduce((s1, s2) => s1.merge(s2), empty());
}
exports.mergeList = mergeList;
function empty() {
    return new SinkStream();
}
exports.empty = empty;
function subscribe(fn, stream) {
    stream.subscribe(fn);
}
exports.subscribe = subscribe;
function publish(a, stream) {
    stream.publish(a);
}
exports.publish = publish;
function merge(a, b) {
    return a.merge(b);
}
exports.merge = merge;
function map(fn, stream) {
    return stream.map(fn);
}
exports.map = map;
function filter(fn, stream) {
    return stream.filter(fn);
}
exports.filter = filter;
function isStream(obj) {
    return (obj instanceof Stream);
}
exports.isStream = isStream;
//# sourceMappingURL=Stream.js.map