if (!Object.assign) { throw “StreamingGraphQLClient requires Object.assign” }

var StreamingGraphQLClient = {

// Send a GraphQL query to the server,
// then receive patches from `\n\n`-delimited chunks.
// For each patch, call `onResponse` with the updated response (data and errors)
// @example Send a GraphQL query and handle the response
//   var queryString = "{ items @stream { name } }"
//   var queryVariables = {}
//
//   // Response handler checks for errors
//   var handleResponse = function(response) {
//     if (response.errors) {
//       alert("Uh oh, something went wrong!\n\n" + JSON.stringify(response.errors))
//     } else {
//       _this.setState({items: response.items})
//     }
//   }
//
//   StreamingGraphQL.fetch("/graphql", queryString, queryVariables, handleResponse)
//
fetch: function(endpointPath, queryString, variables, onResponse) {
  var xhr = new XMLHttpRequest()
  xhr.open("POST", endpointPath)
  xhr.setRequestHeader('Content-Type', 'application/json')
  var csrfToken = $('meta[name="csrf-token"]').attr('content')
  xhr.setRequestHeader('X-CSRF-Token', csrfToken)

  // This will collect patches to publish to `onResponse`
  var responseData = {}

  // It seems like `onprogress` was called once with the first _two_ patches.
  // Track the index to make sure we don't miss any double-patches.
  var nextPatchIdx = 0

  var _this = this
  xhr.onprogress = function () {
    // responseText grows; we only care about the most recent patch
    // TODO: it's a waste to split the text over and over, can
    // we maintain a indicator to the last-read patch?
    var patchStrings = xhr.responseText.split("\n\n")

    while (patchStrings.length > nextPatchIdx) {
      var nextPatchString = patchStrings[nextPatchIdx]
      var nextPatch = JSON.parse(nextPatchString)
      _this._mergePatch(responseData, nextPatch)
      nextPatchIdx += 1
    }

    onResponse(responseData)
  }

  xhr.send(JSON.stringify({
    query: queryString,
    variables: variables,
  }))
},

// merge `patch` into `responseData` (destructive)
_mergePatch: function(responseData, patch) {
  if (patch.path.length === 0) {
    Object.assign(responseData, patch.value)
  } else {
    var targetHash = responseData
    var steps = patch.path.slice(0, patch.path.length - 1)
    var lastKey = patch.path[patch.path.length - 1]
    steps.forEach(function(step) {
      var nextStep = targetHash[step]
      if (nextStep == null) {
        nextStep = targetHash[step] = {}
      }
      targetHash = nextStep
    })
    targetHash[lastKey] = patch.value
  }
}

}