import MulticastDisposable from './MulticastDisposable'
import { tryEvent, tryEnd } from './tryEvent'
import { dispose, emptyDisposable } from './dispose'
import { append, remove, findIndex } from '@most/prelude'

export default class MulticastSource {
  constructor (source) {
    this.source = source
    this.sinks = []
    this._disposable = emptyDisposable
  }

  run (sink, scheduler) {
    const n = this.add(sink)
    if (n === 1) {
      this._disposable = this.source.run(this, scheduler)
    }
    return new MulticastDisposable(this, sink)
  }

  _dispose () {
    const disposable = this._disposable
    this._disposable = void 0
    return Promise.resolve(disposable).then(dispose)
  }

  add (sink) {
    this.sinks = append(sink, this.sinks)
    return this.sinks.length
  }

  remove (sink) {
    this.sinks = remove(findIndex(sink, this.sinks), this.sinks)
    return this.sinks.length
  }

  event (time, value) {
    const s = this.sinks
    if (s.length === 1) {
      s[0].event(time, value)
      return
    }
    for (let i = 0; i < s.length; ++i) {
      tryEvent(time, value, s[i])
    }
  }

  end (time, value) {
    const s = this.sinks
    if (s.length === 1) {
      s[0].end(time, value)
      return
    }
    for (let i = 0; i < s.length; ++i) {
      tryEnd(time, value, s[i])
    }
  }

  error (time, err) {
    const s = this.sinks
    for (let i = 0; i < s.length; ++i) {
      s[i].error(time, err)
    }
  }
}

@@ -296,16 +296,42 @@
  source%0A
+    this.sink = undefined%0A
     this
@@ -727,32 +727,112 @@
 %0A  add (sink) %7B%0A
+    if (this.sink === undefined) %7B%0A      this.sink = sink%0A      return 1%0A    %7D%0A%0A
     this.sinks =
@@ -877,32 +877,36 @@
 his.sinks.length
+ + 1
 %0A  %7D%0A%0A  remove (
@@ -909,24 +909,163 @@
 ve (sink) %7B%0A
+    if (sink === this.sink) %7B%0A      this.sink = this.sinks.shift()%0A      return this.sink === undefined ? 0 : this.sinks.length + 1%0A    %7D%0A%0A
     this.sin
@@ -1145,16 +1145,20 @@
 s.length
+ + 1
 %0A  %7D%0A%0A  
@@ -1217,33 +1217,33 @@
 f (s.length === 
-1
+0
 ) %7B%0A      s%5B0%5D.e
@@ -1236,20 +1236,25 @@
 %7B%0A      
-s%5B0%5D
+this.sink
 .event(t
@@ -1265,39 +1265,74 @@
  value)%0A    
-  return%0A    %7D%0A
+%7D else %7B%0A      tryEvent(time, value, this.sink)%0A  
     for (let
@@ -1358,32 +1358,34 @@
 h; ++i) %7B%0A      
+  
 tryEvent(time, v
@@ -1388,38 +1388,46 @@
 e, value, s%5Bi%5D)%0A
+  
     %7D%0A
+    %7D%0A
   %7D%0A%0A  end (time
@@ -1445,232 +1445,403 @@
     
-const s = this.sinks%0A    if (s.length === 1) %7B%0A      s%5B0%5D.end(time, value)%0A      return%0A    %7D%0A    for (let i = 0; i %3C s.length; ++i) %7B%0A      tryEnd(time, value, s%5Bi%5D)%0A    %7D%0A  %7D%0A%0A  error (time, err) %7B%0A    const s = this.sinks
+// Important: slice first since sink.end may change%0A    // the set of sinks%0A    const s = this.sinks.slice()%0A    tryEnd(time, value, this.sink)%0A    for (let i = 0; i %3C s.length; ++i) %7B%0A      tryEnd(time, value, s%5Bi%5D)%0A    %7D%0A  %7D%0A%0A  error (time, err) %7B%0A    // Important: slice first since sink.error may change%0A    // the set of sinks%0A    const s = this.sinks.slice()%0A    this.sink.error(time, err)
 %0A   

Use a fast property for first observer