/**
 * Webservice client.  Extend this class to communicate with services implemented
 * using the webservice module.
 * 
 * @see http://redmine.cityofcairns.com/projects/coc-framework-2/wiki/WebService_Module
 */
var WebServiceClient = new Class({
  baseUrl: null,
  lastRequestID: 0,
  callParamsByRequestID: new Hash(),
  
  initialize: function(baseUrl, options)
  {
	  this.baseUrl = baseUrl;
	
    this.request = new Request.JSON(
      $extend({
        method: 'post',
        link: 'chain',
        onSuccess: this.onRequestSuccess.bind(this),
        onFailure: this.onRequestFailure.bind(this)
      }, options));
  }, 
  
  call: function(callback, options) 
  {
    if ($type(callback) != 'function')
      throw 'invalid callback function';
    
    var callParams = $extend({
        url: this.makeUrl(options),
        requestID: ++this.lastRequestID,
        callback: callback,
        id: false,
        action: false,
        data: false,
        onFailure: this.onFailure.bind(this),
        auth: false
      },
      options);
    
    if (this.beforeRequest)
      this.beforeRequest(callParams.requestID);
    
    this.callParamsByRequestID.set(callParams.requestID, callParams);
    this.request.send({
      url: callParams.url,
      data: JSON.encode({
        requestID: callParams.requestID,
        data: callParams.data,
        auth: callParams.auth
      })
    });
    
    return callParams.requestID;
  },
  
  makeUrl: function(options)
  {
    url = this.baseUrl;
    
    if (options)
    {
      // append numeric id: /<id>
      if (options.id)
        url += '/' + options.id;
      
      // append action: .<action>
      if (options.action)
        url += '.' + options.action;
    }
    
    return url;
  },
  
  onRequestSuccess: function(response, responseText)
  {
    var requestID = null;
    if (response == null)
    {
      this.fail(null, 
                'ajax request succeeded, but '
                  + (responseText.length ==  0 ? 
                     'no data returned by server' 
                   : 'unrecognized data returned by server: ' + responseText));
    }
    else
    {
      requestID = response.requestID;
    	var callParams = this.callParamsByRequestID.get(requestID);
    	if (callParams)
    		callParams.callback(response.data);
    	else
    	  this.fail(callParams, 'bad request ID');
    }

    if (this.afterRequest)
      this.afterRequest(requestID);
  },

  onRequestFailure: function(xhr)
  {
    switch (xhr.status)
    {
      case 500:
        var response = JSON.decode(xhr.responseText);
        
        if (response == null)
        {
          this.fail(null, 
                    'ajax request failed, '
                      + (xhr.responseText.length ==  0 ? 
                         'no data returned by server' 
                       : 'unrecognized data returned by server: ' + xhr.responseText));
        }
        else 
        {
          var callParams = this.callParamsByRequestID.get(response.requestID);
          this.fail(callParams, response)
        }
        break;
        
      default:
        // if request-failure but status 200, it means we didn't get a valid JSON string back from the server.
        if (xhr.status == 200) {
          this.fail(null, 
                    'ajax request failed, '
                      + (xhr.responseText.length ==  0 ? 
                         'no data returned by server' 
                       : 'unrecognized data returned by server: ' + xhr.responseText));
          break;
        }

        var message = 'Server error: ';
        try
        {
          message += xhr.status + " " + xhr.statusText;
        }
        catch(e)
        {
          message += '(xhr status not available)';
        }
        
        this.fail(null, message);
    }
    
    if (this.afterRequest)
      this.afterRequest();
   },
  
  fail: function(callParams, error)
  {
    if ($type(error) == 'string')
    {
      error = {
        id: null,
        message: error 
      }
    }
    
    if (callParams)
    {
      if (callParams.onFailure)
        callParams.onFailure(callParams, error);
    }
    else
      this.onFailure(null, error);

//    This is causing duplicate afterRequest calls (without the applicable ID) in every case of a failure.
//    if (this.afterRequest)
//      this.afterRequest();
  },
  
  /**
   * Called on failure for any reason. 
   */
  onFailure: function(callParams, error)
  {
    try
    {
      if (error.exception)
        console.error('WebService server exception:\n', error.exception);
      else
        console.error('WebService call failed:\n', error.message);
    }
    catch (e)
    {
      // ignore
    }
  }
  
});
