var generic = generic || {};

generic.jsonRPC = Class.create({
    id: 0, 
    url: generic.env.domain + "/rpc/jsonrpc.tmpl",
    errorCodes: {
        101: "The data type of this method is not supported.",
        102: "The data type of the request parameters is not supported.",
        103: "Your request did not return any results.",
        104: "Response is not in the expected format."
    },
    initialize: function () {
    },

    /** args is a hash, required key: args.method 
        optional keys: args.params, args.onBoth func, args.onSuccess func and args.onFailure func **/
    fetch: function(/* Object*/args) {
        var self = this;
        this.id++;

        var options = {method:'post'};

        if (args.onBoth) {
            options.onSuccess = args.onBoth;
            options.onFailure = args.onBoth;
        } else {
            options.onSuccess = args.onSuccess || function (response) {
                console.log('JSON-RPC success');
                console.log(Object.toJSON(response.getValue()));
            };
            options.onFailure = args.onFailure || function (response) {
                console.log('JSON-RPC failure');
                console.log(Object.toJSON(response.getMessages()));
            };
        }

        options.onSuccess = options.onSuccess.wrap(
            function(proceed, response) {
                if (!response||!response.responseText) { // empty response
                    errorHandler(this.createErrorResponse(103));
                    return;
                }
                // Analytics general event for RPC..
                generic.events.fire({event:'RPC:RESULT',msg:response});

                var responseArray = response.responseText.evalJSON(true);

                if (Object.isArray(responseArray)) {
                    var resultObj = responseArray[0];
                    if (resultObj) {
                        var jsonRpcResponse = self.response(resultObj);
                        if (resultObj.error) { // server returns an error
                            errorHandler(jsonRpcResponse);
                        } else if (resultObj.result) { // successful response in expected format
                            //console.log("generic.jsonrpc.onSuccess");
                            proceed(jsonRpcResponse);
                        }
                    } else { // top-level response array is empty
                        errorHandler(self.createErrorResponse(103));
                    }
                } else { // response is not in expected format (array) 
                    errorHandler(self.createErrorResponse(104));
                }
            });

        options.onFailure = options.onFailure.wrap(
            function(proceed, response) {
                var resp = response;

                //server returned failure, i.e. onFailure was not triggered by this class
                if (typeof response.responseText != "undefined") {
                    //console.log("generic.jsonRPC onFailure: server error");
                    try { //server returns an error in json
                        var responseArray = response.responseText.evalJSON(true);
                        var resultObj = responseArray[0];
                        resp = self.response(resultObj);
                    } catch(e) { //server response is not json
                        //console.log("generic.jsonRPC onFailure: server error, result is not json");
                        resp = self.createErrorResponse(response.status,response.responseText);
                    }
                }

                proceed(resp);
            });

        var errorHandler = options.onFailure;

        var method = args.method;

        var params = args.params || [];
	 	
        var postObj = {
            method: method,
            id: self.id
        };

        // make sure a method was passed
        if ( !Object.isString(method) || method.length <= 0 ) {
            errorHandler(self.createErrorResponse(101));
            return null;
        }

        //make sure that the params type is an obj
        if (typeof params === 'string') {
            postObj.params = params.evalJSON();
        } else if (typeof params === 'object') {
            postObj.params = params;
        } else {
            errorHandler(self.createErrorResponse(102));
            return null;
        }

        var postString = "[" + Object.toJSON(postObj) + "]";

        options.parameters = $H({JSONRPC:[postString]}).toQueryString();
        var url = this.url + '?dbgmethod=' + method;
        new Ajax.Request( url, options );
        return this.id;
    },

    createErrorResponse: function(errorCode, errorMsg) {
        errorMsg = errorMsg || this.errorCodes[errorCode];
        var errorObj = new this.response({
            "error" : {
                "code": errorCode,
                "data": {
                "messages" : [{
                    "text" : errorMsg,
                    "display_locations" : [],
                    "severity" : "MESSAGE",
                    "tags" : [],
                    "key" : ""
                }]
                }
            },
            "id" : this.id
        });
        return errorObj;
    },

     /**
    * A JsonRpcResponse object is instantiated and returned to the onSuccess and onError
    * callback functions that are passed to the fetch() method. It exposes the contents
    * of the response through its getData, getError, and getMessages methods.
    */
    response: function (resultObj) {
        var jsonRpcResponseObj = {};
        var rawResponse = resultObj;
        jsonRpcResponseObj.getId = function() {
            if (rawResponse) {
                return rawResponse.id;
            }
            return null;
        };
        jsonRpcResponseObj.getError = function() {
            if (rawResponse &&
                rawResponse.error) {
                return rawResponse.error;
            }
            return null;
        };
        jsonRpcResponseObj.getData = function() {
            if (rawResponse &&
                rawResponse.result &&
                rawResponse.result.data) {
                return rawResponse.result.data;
            }
            return null;
        };
        jsonRpcResponseObj.getValue = function() {
            if (rawResponse &&
                rawResponse.result &&
                typeof rawResponse.result.value != "undefined") {
                return rawResponse.result.value;
            }
            return null;
        };
        /**
         * This method returns the contents of the response's error property.
         * It first checks the result property, then checks the error property.
         */        
        jsonRpcResponseObj.getMessages = function() {
            if (rawResponse) {
                if (rawResponse.result &&
                    rawResponse.result.data &&
                    rawResponse.result.data.messages) {
                    return rawResponse.result.data.messages;
                } else if (rawResponse.error &&
                           rawResponse.error.data &&
                           rawResponse.error.data.messages) {
                    return rawResponse.error.data.messages;
                }
            }
            return null;
        };
        jsonRpcResponseObj.getCartResults = function() {
            if (rawResponse &&
                rawResponse.result &&
                rawResponse.result.data &&
                rawResponse.result.data.ac_results &&
                Object.isArray(rawResponse.result.data.ac_results) ) {
                rawResponse.result.data.ac_results.each(function (resultObj) {
                    if (resultObj.result) {
                        for (var i in resultObj.result) {
                            resultObj[i] = resultObj.result[i];
                        }
                    }
                });
                return rawResponse.result.data.ac_results;
            }
            return null;
        };
        return jsonRpcResponseObj;
    }
});
generic.jsonrpc = new generic.jsonRPC();
