class Turbolinks.HttpRequest

@NETWORK_FAILURE = 0
@TIMEOUT_FAILURE = -1

@timeout = 60

constructor: (@delegate, location, referrer) ->
  @url = Turbolinks.Location.wrap(location).requestURL
  @referrer = Turbolinks.Location.wrap(referrer).absoluteURL
  @createXHR()

send: ->
  if @xhr and not @sent
    @notifyApplicationBeforeRequestStart()
    @setProgress(0)
    @xhr.send()
    @sent = true
    @delegate.requestStarted?()

cancel: ->
  if @xhr and @sent
    @xhr.abort()

# XMLHttpRequest events

requestProgressed: (event) =>
  if event.lengthComputable
    @setProgress(event.loaded / event.total)

requestLoaded: =>
  @endRequest =>
    if 200 <= @xhr.status < 300
      @delegate.requestCompletedWithResponse(@xhr.responseText, @xhr.getResponseHeader("Turbolinks-Location"))
    else
      @failed = true
      @delegate.requestFailedWithStatusCode(@xhr.status, @xhr.responseText)

requestFailed: =>
  @endRequest =>
    @failed = true
    @delegate.requestFailedWithStatusCode(@constructor.NETWORK_FAILURE)

requestTimedOut: =>
  @endRequest =>
    @failed = true
    @delegate.requestFailedWithStatusCode(@constructor.TIMEOUT_FAILURE)

requestCanceled: =>
  @endRequest()

# Application events

notifyApplicationBeforeRequestStart: ->
  Turbolinks.dispatch("turbolinks:request-start", data: { url: @url, xhr: @xhr })

notifyApplicationAfterRequestEnd: ->
  Turbolinks.dispatch("turbolinks:request-end", data: { url: @url, xhr: @xhr })

# Private

createXHR: ->
  @xhr = new XMLHttpRequest
  @xhr.open("GET", @url, true)
  @xhr.timeout = @constructor.timeout * 1000
  @xhr.setRequestHeader("Accept", "text/html, application/xhtml+xml")
  @xhr.setRequestHeader("Turbolinks-Referrer", @referrer)
  @xhr.onprogress = @requestProgressed
  @xhr.onload = @requestLoaded
  @xhr.onerror = @requestFailed
  @xhr.ontimeout = @requestTimedOut
  @xhr.onabort = @requestCanceled

endRequest: (callback) ->
  if @xhr
    @notifyApplicationAfterRequestEnd()
    callback?.call(this)
    @destroy()

setProgress: (progress) ->
  @progress = progress
  @delegate.requestProgressed?(@progress)

destroy: ->
  @setProgress(1)
  @delegate.requestFinished?()
  @delegate = null
  @xhr = null