_ = require 'underscore'

# Public: To make specs easier to test, we make all asynchronous behavior
# actually synchronous. We do this by overriding all global timeout and
# Promise functions.
#
# You must now manually call `advanceClock()` in order to move the "clock"
# forward.
class TimeOverride

  @advanceClock = (delta=1) =>
    @now += delta
    callbacks = []

    @timeouts ?= []
    @timeouts = @timeouts.filter ([id, strikeTime, callback]) =>
      if strikeTime <= @now
        callbacks.push(callback)
        false
      else
        true

    callback() for callback in callbacks

  @resetTime = =>
    @now = 0
    @timeoutCount = 0
    @intervalCount = 0
    @timeouts = []
    @intervalTimeouts = {}
    @originalPromiseScheduler = null

  @enableSpies = =>
    window.advanceClock = @advanceClock

    window.originalSetTimeout = window.setTimeout
    window.originalSetInterval = window.setInterval
    spyOn(window, "setTimeout").andCallFake @_fakeSetTimeout
    spyOn(window, "clearTimeout").andCallFake @_fakeClearTimeout
    spyOn(window, "setInterval").andCallFake @_fakeSetInterval
    spyOn(window, "clearInterval").andCallFake @_fakeClearInterval
    spyOn(_._, "now").andCallFake => @now

    # spyOn(Date, "now").andCallFake => @now
    # spyOn(Date.prototype, "getTime").andCallFake => @now

  @disableSpies = =>
    window.advanceClock = null

    jasmine.unspy(window, 'setTimeout')
    jasmine.unspy(window, 'clearTimeout')
    jasmine.unspy(window, 'setInterval')
    jasmine.unspy(window, 'clearInterval')

    jasmine.unspy(_._, "now")

  @resetSpyData = ->
    window.setTimeout.reset?()
    window.clearTimeout.reset?()
    window.setInterval.reset?()
    window.clearInterval.reset?()
    Date.now.reset?()
    Date.prototype.getTime.reset?()

  @_fakeSetTimeout = (callback, ms) =>
    id = ++@timeoutCount
    @timeouts.push([id, @now + ms, callback])
    id

  @_fakeClearTimeout = (idToClear) =>
    @timeouts ?= []
    @timeouts = @timeouts.filter ([id]) -> id != idToClear

  @_fakeSetInterval = (callback, ms) =>
    id = ++@intervalCount
    action = =>
      callback()
      @intervalTimeouts[id] = @_fakeSetTimeout(action, ms)
    @intervalTimeouts[id] = @_fakeSetTimeout(action, ms)
    id

  @_fakeClearInterval = (idToClear) =>
    @_fakeClearTimeout(@intervalTimeouts[idToClear])

module.exports = TimeOverride
