#INCLUDES THE NECCESARY METHODS AND EVENTS TO QUERY AND INSERT OBJECTS TO SALESFORCE
#IT IS ALSO INTEGRATED WITH OTHER PLUGINS LIKE SYNCRONIZABLE, IF IT IS NOT INCLUDED THEN NO PROBLEM

Spine  = @Spine or require('spine')
$      = Spine.$
Model  = Spine.Model

Ajax =
  getURL: (object) ->
    object and object.url?() or object.url

  enabled:  true
  pending:  false
  requests: []

  disable: (callback) ->
    if @enabled
      @enabled = false
      try
        do callback
      catch e
        throw e
      finally
        @enabled = true
    else
      do callback

  requestNext: ->
    next = @requests.shift()
    if next
      @request(next)
    else
      @pending = false

  request: (callback) ->
    (do callback).complete(=> do @requestNext)

  queue: (callback) ->
    return unless @enabled
    if @pending
      @requests.push(callback)
    else
      @pending = true
      @request(callback)
    callback

class Base
  defaults:
    contentType: 'application/json'
    dataType: 'json'
    processData: false

    beforeSend: (request, settings) ->

  ajax: (params, defaults) ->
    $.ajax($.extend({}, @defaults, defaults, params))

  queue: (callback) ->
    Ajax.queue(callback)

class Collection extends Base
  constructor: (@model) ->
    Spine.queries += 1
    Spine.trigger "ajaxStart"

  find: (id, params , options) ->
    record = new @model(id: id)
    @ajax(
      params,
      type: 'GET',
      url:  Ajax.getURL(record)
    ).success(@recordsResponse(options))
     .error(@errorResponse(options))

  all: (filters,params , options) ->
    @ajax(
      params,
      type: 'GET',
      dataType: "text" ,
      url:  "/api/salesforce/sobjects?soql=#{@model.getQuery(filters)}"
    ).success(@recordsResponse(options))
     .error(@errorResponse(options))

  query: (filters , params = {}, options = {}) ->
    request = @all( filters , params , options )
    
    request.success (records) =>
      @model.destroyAll() if @model.destroyBeforeRefresh
      @model.refresh(records, options)
      @model.trigger "querySuccess"

  rest: (params,options = {}) =>
    @beforeRest
    request = @ajax(
      params,
      type:  params.restMethod,
      data:  JSON.stringify { restRoute: params.restRoute , restData: params.restData , restMethod: params.restMethod }
      url:   "/api/salesforce/rest"
    ).success(@recordsResponse(options))
     .error(@errorResponse(options))

  # Private
  recordsResponse: (options = {}) =>
    (data, status, xhr) =>
      Spine.queries -= 1
      Spine.trigger "ajaxEnd"
      
      @model.trigger('ajaxSuccess', data, status, xhr)
      options.onSuccess?(data)

  errorResponse: (options = {}) =>
    (xhr, statusText, error) =>
      Spine.queries -= 1
      Spine.trigger "ajaxEnd"
      longError = ""
      if xhr.status == 503
        longError = "Error de Login"
      else        
        errorObj = xhr.responseText
        try
          parsedErrorObj = JSON.parse errorObj
          errorObj = parsedErrorObj[0]
        catch err
          errorObj = 
            message: if xhr.responseText and xhr.responseText.length > 0 then xhr.responseText else xhr.statusText

        indexS =    errorObj.message.indexOf "Exception:"
        longError = errorObj.message.substring(indexS)

      @model.trigger('ajaxError', null, longError )
      options.onError?( longError)

class Singleton extends Base
  constructor: (@record) ->
    Spine.queries += 1
    Spine.trigger "ajaxStart"
    
    @model = @record.constructor
    @objType = "#{ @model.overrideClassName or @model.name}__c"
    @obj = 
       fields: @model.sobjectFormat(@record , false)
       id: @record.id
       objtype: @objType

  custom: (method, data, options) ->
    @queue =>
      request = @ajax(
        type: method
        data: JSON.stringify( @obj )
        url:  Ajax.getURL(@record)
      ).success(@recordResponse(options))
       .error(@errorResponse(options))


  reload: (params, options) ->
    @queue =>
      request = @ajax(
        params,
        type: 'GET'
        url:  Ajax.getURL(@record)
      ).success(@recordResponse(options))
       .error(@errorResponse(options))


  create: (params, options) ->
    @queue =>
      request = @ajax(
        params,
        type: 'POST'
        data: JSON.stringify @obj
        url:  Ajax.getURL(@model)
      ).success(@recordResponse(options))
       .error(@errorResponse(options))

  update: (params, options) ->
    @queue =>
      request = @ajax(
        params,
        type: 'PUT'
        data: JSON.stringify(@obj)
        url:  Ajax.getURL(@record)
      ).success(@recordResponse(options))
       .error(@errorResponse(options))

  destroy: (params, options) ->
    @queue =>
      request = @ajax(
        params,
        type: 'DELETE'
        url:  Ajax.getURL(@record)
        data: JSON.stringify( { objtype: @objType , id: @record.id } )
      ).success(@recordResponse(options))
       .error(@errorResponse(options))

  # Private
  recordResponse: (options = {}) =>
    (data, status, xhr) =>
      Spine.queries -= 1
      Spine.trigger "ajaxEnd"
      
      if Spine.isBlank(data)
        data = false
      else
        data = @model.fromJSON(data)

      Ajax.disable =>
        if data
          # ID change, need to do some shifting
          if data.id and @record.id isnt data.id
            @record.changeID(data.id)

          # Update with latest data
          @record.updateAttributes(data.attributes())

      @record.trigger('ajaxSuccess', data, status, xhr)
      options.onSuccess?(@record)

  errorResponse: (options = {}) =>
    (xhr, statusText, error) =>
      Spine.queries -= 1
      Spine.trigger "ajaxEnd"
      
      errorObj = xhr.responseText
      try
        parsedErrorObj = JSON.parse errorObj
        errorObj = parsedErrorObj[0]
      catch err
        errorObj = 
          message: if xhr.responseText and xhr.responseText.length > 0 then xhr.responseText else xhr.statusText

      srcError = errorObj.message
      indexS = srcError.indexOf "Exception:"
      longError = srcError.substring(indexS + 11)

      @record?.trigger('ajaxError', longError)
      options.onError?( longError)  

# Ajax endpoint
Model.host = '/api/salesforce/sobjects'

Include =
  ajax: -> new Singleton(this)

  url: (args...) ->
    url = Ajax.getURL(@constructor)
    url += '/' unless url.charAt(url.length - 1) is '/'
    url += encodeURIComponent(@id)
    args.unshift(url)
    args.join('/')

Extend =
  ajax: -> new Collection(this)


  url: (args...) ->
    args.unshift(@className.toLowerCase() + 's')
    args.unshift(Model.host)
    args.join('/')

Model.SalesforceAjax =
  extended: ->
    @change @ajaxChange

    @extend Extend
    @include Include

  # Private

  query: ->
    @ajax().query(arguments...)

  rest: ->
    @ajax().rest(arguments...)

  ajaxChange: (record, type, options = {}) ->    
    return if options.ajax is false
    record.ajax()[type](options.ajax, options)

Model.SalesforceAjax.Methods =
  extended: ->
    @extend Extend
    @include Include
    
  query: ->
    @ajax().query(arguments...)
    
  rest: ->
    @ajax().rest(arguments...)

# Globals
Ajax.defaults   = Base::defaults
Spine.SalesforceAjax      = Ajax
Spine.SalesforceAjaxUtil = new Collection()
Spine.queries = 0
module?.exports = Ajax
