var RequestProcessor = require('../net/RequestProcessor.js').RequestProcessor;

var JsonRpcAbstract = require('../jsonrpc/JsonRpcAbstract.js').JsonRpcAbstract;
var JsonRpcResponse = require('../jsonrpc/JsonRpcResponse.js').JsonRpcResponse;
var JsonRpcMethod = require('../jsonrpc/JsonRpcMethod.js').JsonRpcMethod;
var JsonRpcRequest = require('../jsonrpc/JsonRpcRequest.js').JsonRpcRequest;                                                        

var Types = require('../common/Types.js');

var BinaryData = require('../data/BinaryData.js').BinaryData;

var JsonRpcRequestProcessor = exports.JsonRpcRequestProcessor = function (server) {
        this._request = null;
        this._response = null;
        this._jsonRpcRequest = null;

        RequestProcessor.call(this, server);
};
JsonRpcRequestProcessor.prototype = Object.create(RequestProcessor.prototype);

JsonRpcRequestProcessor.prototype.process = function (request, response) {
    console.time('  -- REQUEST TIME');
    RequestProcessor.prototype.process.call(this, request, response);

    var that = this, method, methodClass;

    this._request = request;
    this._response = response;

    try {
        this._jsonRpcRequest = new JsonRpcRequest(request.getBody().toBuffer().toString());
    } catch (e) {
        if (e == JsonRpcAbstract.Exceptions.INPROPER_JSON) {
            this._throwError(JsonRpcRequestProcessor.Errors.PARSE_ERROR, e);
        } else if (e == JsonRpcAbstract.Exceptions.INVALID_JSON_RPC) {
            this._throwError(JsonRpcRequestProcessor.Errors.INVALID_REQUEST, e);
        } else if (e == JsonRpcAbstract.Exceptions.INPROPER_JSON_STRING) {
            this._throwError(JsonRpcRequestProcessor.Errors.PARSE_ERROR, e);
        } else {
            this._throwError(JsonRpcRequestProcessor.Errors.INTERNAL_ERROR, e)
        }

        return;
    }

    methodClass = this.server.getMethod(this._jsonRpcRequest.getMethod());

    if (!methodClass) {
        this._throwError(JsonRpcRequestProcessor.Errors.METHOD_NOT_FOUND, new Error("Method not found"));
        return;
    }

    method = new methodClass(request, response, this.server);

    if (!Types.isInstanceOf(method, JsonRpcMethod)) {
        this._throwError(JsonRpcRequestProcessor.Errors.METHOD_NOT_FOUND, new Error("Method is not instanceOf JsonRpcMethod"));
        return;
    }

    //FIXME: tutaj trzebaby sie zastanowic jak to zrobic bo raz przekazujemy Exception a raz ErrorEvent
    method.addEventListener(JsonRpcMethod.Event.WRONG_PARAMS, this._handleError, this);
    method.addEventListener(JsonRpcMethod.Event.ERROR, this._handleError, this)
    method.addEventListener(JsonRpcMethod.Event.OK, this._success, this);
    
    console.log("  -- Executing method: " + this._jsonRpcRequest.getMethod());
    
    try {
        method.execute(this._jsonRpcRequest.getParams());
    } catch (e) {
        console.error(e, e.stack);
        this._throwError(JsonRpcRequestProcessor.Errors.INTERNAL_ERROR, e);
    }
};

JsonRpcRequestProcessor.prototype._success = function (e) {
    if (e.data) {
        var data = e.data;

        if (!Types.isInstanceOf(e.data, JsonRpcResponse)) {
            data = new JsonRpcResponse();
            data.setResult(e.data);
        }

        data.setId(this._jsonRpcRequest.getId());

        this._response.setStatusCode(200);
        this._response.setHeaders({
            'Content-Type': 'application/json; charset=utf-8',
            'Access-Control-Allow-Origin': '*'
        }, true);
        this._response.setBody(new BinaryData(data.toString(), BinaryData.Encoding.TEXT, BinaryData.CharacterEncoding.UTF8));
    }
    
    this._response.send();
    console.timeEnd('  -- REQUEST TIME');
    console.log('-- request ended');
};

JsonRpcRequestProcessor.prototype._handleError = function (e) {
    console.log('  -- Handling ERROR event...');

    var err = new Error(e.message);
    err.type = e.type;
    err.code = e.code;
    err.data = e.data;
    
    if (e.type == JsonRpcMethod.Event.WRONG_PARAMS) {
        this._throwError(JsonRpcRequestProcessor.Errors.INVALID_PARAMS, err);
    } else if (e.type == JsonRpcMethod.Event.ERROR) {
        this._throwError(null, err);
    } else {
        this._throwError(JsonRpcRequestProcessor.Errors.INTERNAL_ERROR, err);
    }
};

JsonRpcRequestProcessor.prototype._throwError = function (code, exception) {
    console.error('JSONRPC THROW EXCEPTION HANDLER: ',  code, exception.message, exception.stack);

    var jsonRpcResponse = new JsonRpcResponse(),
        errorCode = null;
    
    jsonRpcResponse.setId(this._jsonRpcRequest ? this._jsonRpcRequest.getId() : "undefined");
    
    if (code) {
        errorCode = parseInt(code.code, 10);
    } else {
        errorCode = parseInt(exception.code, 10);
    }
    
    jsonRpcResponse.setError(errorCode, exception.message, exception.data ? exception.data : null);

    this._response.setStatusCode(200);
    this._response.setHeaders({
        'Content-Type': 'application/json; charset=utf-8',
        'Access-Control-Allow-Origin': '*'
    });

    this._response.setBody(new BinaryData(jsonRpcResponse.toString(), BinaryData.Encoding.TEXT, BinaryData.CharacterEncoding.UTF8));
    this._response.send();
    console.timeEnd('  -- REQUEST TIME');
    console.log('-- request ended');
};

JsonRpcRequestProcessor.Errors = {};
JsonRpcRequestProcessor.Errors.PARSE_ERROR = { code: -32700, message: 'Parse error' };
JsonRpcRequestProcessor.Errors.INVALID_REQUEST = { code: -32600, message: 'Invalid Request' };
JsonRpcRequestProcessor.Errors.METHOD_NOT_FOUND = { code: -32601, message: 'Method not found' };
JsonRpcRequestProcessor.Errors.INVALID_PARAMS = { code: -32602, message: 'Invalid params' };
JsonRpcRequestProcessor.Errors.INTERNAL_ERROR = { code: -32603, message: 'Internal error' };
