// JSON Encoding
JSON=function(){function f(n){return n<10?'0'+n:n;}
Date.prototype.toJSON=function(key){return this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z';};var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapeable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapeable.lastIndex=0;return escapeable.test(string)?'"'+string.replace(escapeable,function(a){var c=meta[a];if(typeof c==='string'){return c;}
return'\\u'+('0000'+
(+(a.charCodeAt(0))).toString(16)).slice(-4);})+'"':'"'+string+'"';}
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
if(typeof rep==='function'){value=rep.call(holder,key,value);}
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
gap+=indent;partial=[];if(typeof value.length==='number'&&!(value.propertyIsEnumerable('length'))){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
v=partial.length===0?'[]':gap?'[\n'+gap+
partial.join(',\n'+gap)+'\n'+
mind+']':'['+partial.join(',')+']';gap=mind;return v;}
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value,rep);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
return{stringify:function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
return str('',{'':value});},parse:function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
return reviver.call(holder,key,value);}
cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+
(+(a.charCodeAt(0))).toString(16)).slice(-4);});}
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
throw new SyntaxError('JSON.parse');}};}();

var Resource = function(m, o, c){
  
  // example options {changes: objectOrJson, through: {modelName: id}, id: number, action: name, params: {authenticity_token: 'string'}}
  
  var model = m.underscore();
  var table = m.tableize();
  var options = o || {};
  
  // handle ID passed as option
  if (o.constructor == Number || o.constructor == String){
    options = {id: o};
  }
  
  if (c){
    options['success'] = c;
  }
  
  var method = 'GET';
  
  // handle filter plugins
  for (var i = 0; i < Resource.filters.length; i++){
    var result = Resource.filters[i](model, options);
    if (result){
      return result;
    }
  }
  
  var data = null;
  var request_url = "";
  
  // Option: data
  if (options['data']){
    if (options['data'].constructor == String){
      data = options['data'];
    }
    else{
      data = JSON.stringify(options['data']);
    }
  }
  
  // Option: changes
  if (options['changes']){
    if (options['changes'].id){
      if (!options['id']) options['id'] = options['changes'].id;
    }
    data = Resource.getSubmission(model, options['changes']);
  }
  
  // Option: parent
  if (options['parent'] && options['parent'].type){
    request_url += '/' + options['parent'].type.tableize() + '/' + options['parent'].id;
  }
  
  // Option: through
  if (options['through']){
    if (options['through'].constructor == String){
      request_url += '/' + options['through'];
    }
    else{
      for (var key in options['through']){
        if (options['through'].hasOwnProperty(key)){
          request_url += '/' + key.tableize() + '/' + options['through'][key];
        }
      }
    }
  }
  
  // Model / table
  request_url += '/' + table
  
  // Option: id
  if (options['id']){
    request_url += '/' + options['id'];
  }
  
  // Option: action
  if (options['action']){
    request_url += '/' + options['action'];
    method = 'POST';
  }
  
  if (data){
    if (options['id'] || options['url']) {
      method = 'PUT';
    }
    else{
      method = 'POST';
    }
  }

  // Option: method override
  if (options['method']){
    method = options['method'];
  }
  
  // Format
  request_url += '.' + 'json';
  
  // Option: url override
  if (options['url']){
    request_url = options['url'];
  }
  
  // Option: params
  if (options['params']){
    for (var key in options['params']){
       if (options['params'].hasOwnProperty(key)){
         request_url += (/\?/.test(request_url)) ? '&' : '?';
         request_url += key + '=' + encodeURIComponent(options['params'][key]);
       }
     }    
  }
  
  if (Resource.defaultParams){
    for (var key in Resource.defaultParams){
       if (Resource.defaultParams.hasOwnProperty(key)){
         request_url += (/\?/.test(request_url)) ? '&' : '?';
         request_url += key + '=' + encodeURIComponent(Resource.defaultParams[key]);
       }
     }    
  }
  
  // cache killer
  if (options['auth'] === false && method != 'GET'){
    request_url += (/\?/.test(request_url)) ? '&' : '?';
    request_url += 'authenticity_token=' + encodeURIComponent(options['auth']);
  }
  // cache killer
  if (options['cache'] === false && method == 'GET'){
    request_url += (/\?/.test(request_url)) ? '&' : '?';
    request_url += 'nocache=' + encodeURIComponent(Math.round(Math.random() * 10000), 0);
  }
  
  // Begin request
  var request = Resource.getHttpRequest(model, options['success'] || options['callback'], options['fail']);
  request.open(method, request_url, true);
  request.setRequestHeader("Content-Type", "application/json;"); 
  request.send(data);
  
  return request;
};
Resource.destroy = function(model, options, callback){
  if (!options) options = {};
  if (options.constructor == Number || options.constructor == String){
    options = {id: options};
  }
  options.method = "DELETE";
  return Resource(model, options, callback);
};

Resource.filters = [];

Resource.addFilter = function(action){
  Resource.filters.push(action);
};

Resource.getHttpRequest = function(m, s, f) {
    var obj;
    var onsuccess = s;
    var onfail = f;
    var model = m;
    
    try {
         obj = new XMLHttpRequest();
    }
    catch (e) {
        obj = new ActiveXObject("Msxml2.XMLHTTP");
    }
    obj.onreadystatechange = function() {
        if (obj.readyState==4) {
            if (obj.status >= 200 && obj.status < 300) {
                if (onsuccess) {
                    onsuccess(Resource.getObject(model, obj.responseText), obj.status);
                }
            }
            else {
                if (onfail) {
                    var responseObject = null;
                    try {
                      responseObject = eval('(' + obj.responseText + ')');
                      if (responseObject[context.model]){
                        responseObject = responseObject[context.model];
                      } 
                    }
                    catch (e) {
                      responseObject = responseObject || {};
                    }
                    responseObject.statusCode = this.status;
                    responseObject.statusText = this.statusText;
                    onfail(responseObject, obj.status);
                }
            }                
        }
    };
    return obj;
};

Resource.getObject = function(model, response){
  var table = model.tableize();
  var responseObject = null;
  if ($.trim(response)) {
    responseObject = eval('(' + response + ')');
    if (responseObject[model]) responseObject = responseObject[model];
    else if (responseObject[table]) responseObject = responseObject[table];
  }
  return responseObject;
};

Resource.getSubmission = function(model, object) {

  if (object.toLowerCase){
    return object;
  }
  else{
    var obj = {};
    $.extend(obj, object);

    var submission = {};
    submission[(obj.constructor == Array) ? model.tableize() : model] = obj;
    if (obj['local_id']) delete obj['local_id'];
    if (obj['_parent']) delete obj['_parent'];
    
    return JSON.stringify(submission)
  }
};
