class Firehose.LongPoll extends Firehose.Transport

messageSequenceHeader: 'Pragma'
name: -> 'LongPoll'

# CORS is kinda supported in IE8+ except that its implementation cannot
# access "simple request" response headers. This means we don't yet have a
# plan to support IE<10 (when it gets a real XHR2 implementation). Sucks...
# $.browser.msie and parseInt($.browser.version) >= 8 # DEPRECATED
@ieSupported: -> (document.documentMode || 10) >= 8

@supported: ->
  # IE 8+, FF 3.5+, Chrome 4+, Safari 4+, Opera 12+, iOS 3.2+, Android 2.1+
  if xhr = $.ajaxSettings.xhr()
    "withCredentials" of xhr || Firehose.LongPoll.ieSupported()

constructor: (args) ->
  super args

  @config.ssl ?= false

  # Configrations specifically for long polling
  @config.longPoll         ||= {}
  @config.longPoll.url     ||= "#{@_protocol()}:#{@config.uri}"
  # How many ms should we wait before timing out the AJAX connection?
  @config.longPoll.timeout ||= 25000
  # TODO - What is @_lagTime for? Can't we just use the @_timeout value?
  # We use the lag time to make the client live longer than the server.
  @_lagTime                  = 5000
  @_timeout                  = @config.longPoll.timeout + @_lagTime
  @_okInterval               = @config.okInterval || 0
  @_stopRequestLoop          = false
  @_lastMessageSequence      = 0

# Protocol schema we should use for talking to firehose server.
_protocol: =>
  if @config.ssl then "https" else "http"

_request: =>
  return if @_stopRequestLoop
  # Set the Last Message Sequence in a query string.
  # Ideally we'd use an HTTP header, but android devices don't let us
  # set any HTTP headers for CORS requests.
  data = @_requestParams()
  data.last_message_sequence = @_lastMessageSequence
  # TODO: Some of these options will be deprecated in jQuery 1.8
  #       See: http://api.jquery.com/jQuery.ajax/#jqXHR
  @_lastRequest = $.ajax
    url:          @config.longPoll.url
    firehose:     true
    crossDomain:  true
    data:         data
    timeout:      @_timeout
    success:      @_success
    error:        @_error
    cache:        false

_requestParams: =>
  @config.params

stop: =>
  @_stopRequestLoop = true
  if @_lastRequest?
    try @_lastRequest.abort() catch e
    delete @_lastRequest
  if @_lastPingRequest?
    try @_lastPingRequest.abort() catch e
    delete @_lastPingRequest

_success: (data, status, jqXhr) =>
  if @_needToNotifyOfReconnect or not @_succeeded
    @_needToNotifyOfReconnect = false
    @_open data
  return if @_stopRequestLoop
  if jqXhr.status is 200
    # Of course, IE's XDomainRequest doesn't support non-200 success codes.
    try
      {message, last_sequence} = JSON.parse jqXhr.responseText
      @_lastMessageSequence    = last_sequence || 0
      @config.message @config.parse message
    catch e
  @connect @_okInterval

_ping: =>
  # Ping long poll server to verify internet connectivity
  # jQuery CORS doesn't support timeouts and there is no way to access xhr2 object
  # directly so we can't manually set a timeout.
  @_lastPingRequest = $.ajax
    url:          @config.uri
    method:       'HEAD'
    crossDomain:  true
    firehose:     true
    data:         @_requestParams()
    success:      =>
      if @_needToNotifyOfReconnect
        @_needToNotifyOfReconnect = false
        @config.connected @

# We need this custom handler to have the connection status
# properly displayed
_error: (jqXhr, status, error) =>
  unless @_needToNotifyOfReconnect or @_stopRequestLoop
    @_needToNotifyOfReconnect = true
    @config.disconnected()
  unless @_stopRequestLoop
    # Ping the server to make sure this isn't a network connectivity error
    setTimeout @_ping, @_retryDelay + @_lagTime
    # Reconnect with delay
    setTimeout @_request, @_retryDelay

# Let’s try to hack in support for IE8-9 via the XDomainRequest object! # This was adapted from code shamelessly stolen from: # github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js if $?.browser?.msie and parseInt($.browser.version, 10) in [8, 9]

jQuery.ajaxTransport (s) ->
  if s.crossDomain and s.async and s.firehose
    if s.timeout
      s.xdrTimeout = s.timeout
      delete s.timeout
    xdr = undefined
    return {
      send: (_, complete) ->
        callback = (status, statusText, responses, responseHeaders) ->
          xdr.onload = xdr.onerror = xdr.ontimeout = jQuery.noop
          xdr = undefined
          complete status, statusText, responses, responseHeaders

        xdr = new XDomainRequest()

        xdr.open s.type, s.url

        xdr.onload = ->
          headers = "Content-Type: #{xdr.contentType}"
          callback 200, "OK", {text: xdr.responseText}, headers

        xdr.onerror = -> callback 404, "Not Found"

        # This is critical for long poll to work in IE9.
        # Without it, the initial request will work but
        # subsequent requests will fail silently.
        # http://social.msdn.microsoft.com/Forums/ie/en-US/30ef3add-767c-4436-b8a9-f1ca19b4812e/ie9-rtm-xdomainrequest-issued-requests-may-abort-if-all-event-handlers-not-specified?forum=iewebdevelopment
        xdr.onprogress = -> {}

        if s.xdrTimeout?
          xdr.ontimeout = -> callback 0, "timeout"
          xdr.timeout   = s.xdrTimeout

        xdr.send (s.hasContent and s.data) or null

      abort: ->
        if xdr?
          xdr.onerror = jQuery.noop()
          xdr.abort()
    }

class Firehose.MultiplexedLongPoll extends Firehose.LongPoll

constructor: (args) ->
  super args
  @_lastMessageSequence = {}

subscribe: (channel, opts) =>
  # nothing to be done

unsubscribe: (channelNames...) =>
  # same here

_request: =>
  return if @_stopRequestLoop
  data = @_subscriptions()

  @_lastRequest = $.ajax
    url:          @config.uri
    firehose:     true
    crossDomain:  true
    method:       "POST"
    data:         data
    dataType:     "json"
    timeout:      @_timeout
    success:      @_success
    error:        @_error
    cache:        false

_updateLastMessageSequences: =>
  for channel, opts of @config.channels
    if seq = @_lastMessageSequence[channel]
      opts.last_sequence = seq
    else
      unless opts.last_sequence
        opts.last_sequence = 0

_subscriptions: =>
  @_updateLastMessageSequences()
  subs = {}
  for channel, opts of @config.channels
    subs[channel] = opts.last_sequence || 0
  JSON.stringify(subs)

_success: (data, status, jqXhr) =>
  if @_needToNotifyOfReconnect or not @_succeeded
    @_needToNotifyOfReconnect = false
    @_open data
  return if @_stopRequestLoop
  if jqXhr.status is 200
    # Of course, IE's XDomainRequest doesn't support non-200 success codes.
    try
      message = JSON.parse jqXhr.responseText
      @_lastMessageSequence ||= {}
      @_lastMessageSequence[message.channel] = message.last_sequence
      @config.message message
    catch e
  @connect @_okInterval