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.sink = undefined
    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) {
    if (this.sink === undefined) {
      this.sink = sink
      return 1
    }

    this.sinks = append(sink, this.sinks)
    return this.sinks.length + 1
  }

  remove (sink) {
    if (sink === this.sink) {
      this.sink = this.sinks.shift()
      return this.sink === undefined ? 0 : this.sinks.length + 1
    }

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

  event (time, value) { // eslint-disable-line complexity
    if (this.sink === undefined) {
      return
    }

    const s = this.sinks
    if (s.length === 0) {
      this.sink.event(time, value)
      return
    }

    tryEvent(time, value, this.sink)
    for (let i = 0; i < s.length; ++i) {
      tryEvent(time, value, s[i])
    }
  }

  end (time, value) {
    // Important: slice first since sink.end may change
    // the set of sinks
    const s = this.sinks.slice()
    tryEnd(time, value, this.sink)
    for (let i = 0; i < s.length; ++i) {
      tryEnd(time, value, s[i])
    }
  }

  error (time, err) {
    // Important: slice first since sink.error may change
    // the set of sinks
    const s = this.sinks.slice()
    this.sink.error(time, err)
    for (let i = 0; i < s.length; ++i) {
      s[i].error(time, err)
    }
  }
}

@@ -300,34 +300,8 @@
 rce%0A
-    this.sink = undefined%0A
     
@@ -633,14 +633,23 @@
 e = 
-void 0
+emptyDisposable
 %0A   
@@ -722,88 +722,8 @@
 ) %7B%0A
-    if (this.sink === undefined) %7B%0A      this.sink = sink%0A      return 1%0A    %7D%0A%0A
     
@@ -780,36 +780,32 @@
 his.sinks.length
- + 1
 %0A  %7D%0A%0A  remove (
@@ -820,51 +820,33 @@
     
-if (sink === this.sink) %7B%0A      this.
+const i = findIndex(
 sink
- =
+,
  thi
@@ -856,153 +856,103 @@
 inks
-.shift()%0A      return this.sink === undefined ? 0 : this.sinks.length + 1%0A    %7D%0A%0A    this.sinks = remove(findIndex(sink, this.sinks), this.sinks)
+)%0A    // istanbul ignore next%0A    if (i %3E= 0) %7B%0A      this.sinks = remove(i, this.sinks)%0A    %7D%0A
 %0A   
@@ -980,12 +980,8 @@
 ngth
- + 1
 %0A  %7D
@@ -1009,97 +1009,8 @@
 e) %7B
- // eslint-disable-line complexity%0A    if (this.sink === undefined) %7B%0A      return%0A    %7D%0A
 %0A   
@@ -1052,17 +1052,17 @@
 gth === 
-0
+1
 ) %7B%0A    
@@ -1063,25 +1063,27 @@
 %7B%0A      
-this.sink
+return s%5B0%5D
 .event(t
@@ -1102,60 +1102,9 @@
     
-  return%0A    %7D%0A%0A    tryEvent(time, value, this.sink)
+%7D
 %0A   
@@ -1216,403 +1216,159 @@
     
-// 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)
+const s = this.sinks%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
 %0A   

Simplify using just an array