/** @license MIT License (c) copyright 2010-2015 original author or authors */
/** @author Brian Cavalier */
/** @author John Hann */

var Stream = require('../Stream');
var ChainDisposable = require('../disposable/ChainDisposable');
var EmptyDisposable = require('../disposable/EmptyDisposable');

exports.switch = switchLatest;

/**
 * Given a stream of streams, return a new stream that adopts the behavior
 * of the most recent inner stream.
 * @param {Stream} stream of streams on which to switch
 * @returns {Stream} switching stream
 */
function switchLatest(stream) {
	return new Stream(new Switch(stream.source));
}

function Switch(source) {
	this.source = source;
}

Switch.prototype.run = function(sink, scheduler) {
	var switchSink = new SwitchSink(sink, scheduler);
	return new ChainDisposable(switchSink, this.source.run(switchSink, scheduler));
};

function SwitchSink(sink, scheduler) {
	this.sink = sink;
	this.scheduler = scheduler;
	this.current = null;
	this.ended = false;
}

SwitchSink.prototype.event = function(t, stream) {
	this._disposeCurrent(t); // TODO: capture the result of this dispose
	this.current = new Segment(t, Infinity, this, this.sink);
	this.current.disposable = stream.source.run(this.current, this.scheduler);
};

SwitchSink.prototype.end = function(t, x) {
	this.ended = true;
	this._checkEnd(t, x);
};

SwitchSink.prototype.error = function(t, e) {
	this.ended = true;
	this.sink.error(t, e);
};

SwitchSink.prototype.dispose = function() {
	return this._disposeCurrent(0);
};

SwitchSink.prototype._disposeCurrent = function(t) {
	if(this.current !== null) {
		return this.current._dispose(t);
	}
};

SwitchSink.prototype._disposeInner = function(t, inner) {
	inner._dispose(t); // TODO: capture the result of this dispose
	if(inner === this.current) {
		this.current = null;
	}
};

SwitchSink.prototype._checkEnd = function(t, x) {
	if(this.ended && this.current === null) {
		this.sink.end(t, x);
	}
};

SwitchSink.prototype._endInner = function(t, x, inner) {
	this._disposeInner(t, inner);
	this._checkEnd(t, x);
};

SwitchSink.prototype._errorInner = function(t, e, inner) {
	this._disposeInner(t, inner);
	this.sink.error(t, e);
};

function Segment(min, max, outer, sink) {
	this.min = min;
	this.max = max;
	this.outer = outer;
	this.sink = sink;
	this.disposable = new EmptyDisposable();
}

Segment.prototype.event = function(t, x) {
	if(t < this.max) {
		this.sink.event(Math.max(t, this.min), x);
	}
};

Segment.prototype.end = function(t, x) {
	this.outer._endInner(Math.max(t, this.min), x, this);
};

Segment.prototype.error = function(t, e) {
	this.outer._errorInner(Math.max(t, this.min), e, this);
};

Segment.prototype._dispose = function(t) {
	this.max = t;
	return this.disposable.dispose();
};

@@ -167,30 +167,30 @@
 ');%0Avar 
-ChainDisposabl
+MulticastSourc
 e = requ
@@ -201,100 +201,155 @@
 '../
-disposable/ChainDisposable');%0Avar EmptyDisposable = require('../disposable/EmptyDisposable')
+source/MulticastSource');%0Avar until = require('./timeslice').takeUntil;%0Avar concurrentMergeMap = require('./concurrentMergeMap').concurrentMergeMap
 ;%0A%0Ae
@@ -629,2189 +629,180 @@
  %7B%0A%09
-return new Stream(new Switch(stream.source));%0A%7D%0A%0Afunction Switch(source) %7B%0A%09this.source = source;%0A%7D%0A%0ASwitch.prototype.run = function(sink, scheduler) %7B%0A%09var switchSink = new SwitchSink(sink, scheduler);%0A%09return new ChainDisposable(switchSink, this.source.run(switchSink, scheduler));%0A%7D;%0A%0Afunction SwitchSink(sink, scheduler) %7B%0A%09this.sink = sink;%0A%09this.scheduler = scheduler;%0A%09this.current = null;%0A%09this.ended = false;%0A%7D%0A%0ASwitchSink.prototype.event = function(t, stream) %7B%0A%09this._disposeCurrent(t); // TODO: capture the result of this dispose%0A%09this.current = new Segment(t, Infinity, this, this.sink);%0A%09this.current.disposable = stream.source.run(this.current, this.scheduler);%0A%7D;%0A%0ASwitchSink.prototype.end = function(t, x) %7B%0A%09this.ended = true;%0A%09this._checkEnd(t, x);%0A%7D;%0A%0ASwitchSink.prototype.error = function(t, e) %7B%0A%09this.ended = true;%0A%09this.sink.error(t, e);%0A%7D;%0A%0ASwitchSink.prototype.dispose = function() %7B%0A%09return this._disposeCurrent(0);%0A%7D;%0A%0ASwitchSink.prototype._disposeCurrent = function(t) %7B%0A%09if(this.current !== null) %7B%0A%09%09return this.current._dispose(t);%0A%09%7D%0A%7D;%0A%0ASwitchSink.prototype._disposeInner = function(t, inner) %7B%0A%09inner._dispose(t); // TODO: capture the result of this dispose%0A%09if(inner === this.current) %7B%0A%09%09this.current = null;%0A%09%7D%0A%7D;%0A%0ASwitchSink.prototype._checkEnd = function(t, x) %7B%0A%09if(this.ended && this.current === null) %7B%0A%09%09this.sink.end(t, x);%0A%09%7D%0A%7D;%0A%0ASwitchSink.prototype._endInner = function(t, x, inner) %7B%0A%09this._disposeInner(t, inner);%0A%09this._checkEnd(t, x);%0A%7D;%0A%0ASwitchSink.prototype._errorInner = function(t, e, inner) %7B%0A%09this._disposeInner(t, inner);%0A%09this.sink.error(t, e);%0A%7D;%0A%0Af
+var upstream = new Stream(new MulticastSource(stream.source));%0A%0A%09return concurrentMergeMap(untilNext, 1, upstream);%0A%0A%09function 
 un
-c
 ti
-on Segment(min, max, outer, sink) %7B%0A%09this.min = min;%0A%09this.max = max;%0A%09this.outer = outer;%0A%09this.sink = sink;%0A%09this.disposable = new EmptyDisposable();%0A%7D%0A%0ASegment.prototype.event = function(t, x) %7B%0A%09if(t %3C this.max) %7B%0A%09%09this.sink.event(Math.max(t, this.min), x);%0A%09%7D%0A%7D;%0A%0ASegment.prototype.end = f
+lNext(s) %7B%0A%09%09return 
 un
-c
 ti
-on(t, x) %7B%0A%09this.outer._endInner(Math.max(t, this.min), x, this);%0A%7D;%0A%0ASegment.prototype.error = function(t, e) %7B%0A%09this.outer._errorInner(Math.max(t, this.min), e, this);%0A%7D;%0A%0ASegment.prototype._dispose = function(t) %7B%0A%09this.max = t;%0A%09return this.disposable.dispose();
+l(upstream, s);%0A%09%7D
 %0A%7D
-;
 %0A

Simplify switch() by using until()