var Request = require("./Request.js").Request;
var Response = require("./Response.js").Response;
var BinaryData = require("./../data/BinaryData.js").BinaryData;
var RequestProcessor = require("./RequestProcessor.js").RequestProcessor;
var Crypto = require("../common/Crypto.js");
var Types = require("../common/Types.js");

var http = require('http');

http.STATUS_CODES['308'] = 'Permanent Redirect';
http.STATUS_CODES['428'] = 'Precondition Required';
http.STATUS_CODES['429'] = 'Too Many Requests';
http.STATUS_CODES['431'] = 'Request Header Fields Too Large';
http.STATUS_CODES['511'] = 'Network Authentication Required';

/**
 * @class Server Main http server class. Wraps native node.js http object. Creates Request processor instance and dispatch processing.
 * @param {Class} RequestProcessorClass Subclass of RequestProcessor family.
 */
var Server = function (RequestProcessorClass) {
    this.RequestProcessorClass = RequestProcessorClass;
    this._nativeServers = [];
};

Server.prototype._reattachListeners = function (nativeServer) {
    var that = this;

    nativeServer.removeAllListeners('connection');
    nativeServer.addListener('connection', function (socket) {
        var destroy = socket.destroy;

        socket.addListener('parseError', function () {
            if (socket.writable) {
                socket.write("HTTP/1.1 400 Bad Request\r\nserver: " +  that.getServerName() + "\r\ncontent-type: text/plain\r\nconnection: close\r\n\r\n400 Bad Request");
            }
            var rp = new RequestProcessor(that);
            var req = new Request();
            var res = new Response();
            res.setStatusCode(400);
            rp.process(req, res);

            res.send();
        });

        socket.destroy = function (err) {
            if (err && err.hasOwnProperty('bytesParsed')) {
                socket.emit('parseError');
                process.nextTick(function () {
                    destroy.call(socket, err);
                });
            } else {
                destroy.call(socket, err);
            }
        }
    });
    nativeServer.addListener('connection', http._connectionListener);
};

Server.prototype.getServerName = function () {
    return "node-server";
};

Server.prototype._createNativeServer = function() {
    var that = this;
    var nativeServer = http.createServer(function (request, response) {
        that._onRequestHandler(request, response);
    });

    this._reattachListeners(nativeServer);
    this._nativeServers.push(nativeServer);

    return nativeServer;
};

Server.prototype.listen = function (port, host, callback) {
    var nativeServer = this._createNativeServer();

    nativeServer._backlog = 4096;
    nativeServer.listen(port, host, callback);
};

Server.prototype.listenFD = function (fd, protocol) {
    var nativeServer = this._createNativeServer();

    nativeServer._backlog = 4096;
    nativeServer.listenFD(fd, protocol);
};

Server.prototype.listenSocket = function (socketPath, callback) {
    var nativeServer = this._createNativeServer();

    nativeServer._backlog = 4096;
    nativeServer.listen(socketPath, callback);
};

Server.prototype.close = function () {
    for (var i = 0, l = this._nativeServers.length; i < l; i++) {
        this._nativeServers[i].close();
    }
};

/**
 * @private
 */

Server.prototype._onRequestHandler = function (httpRequest, httpResponse) {
    //czas rozpoczecia przetwarzania requestu
    httpRequest.__timeStart = Date.now();
    //console.log('\n-- request started');

    var that = this;

    var dataLength = 0, buffers = [];

    // gathering post data
    httpRequest.on('data', function (data) {
        buffers.push(data);
        dataLength += data.length;
    });

    // i'm the master of every buffer @ post data'
    httpRequest.on('end', function () {

        var joinedBuffer = new Buffer(dataLength), bufferPosition = 0;
        for(var i = 0, l = buffers.length; i < l; i++){
            buffers[i].copy(joinedBuffer, bufferPosition);
            bufferPosition += buffers[i].length;
        }
        // 2325 - 2336

        httpRequest.data = new BinaryData(
            joinedBuffer,
            BinaryData.Encoding.BINARY
        );
        // 2109 - 2283

        that._process(httpRequest, httpResponse);

        // 897 - 936
    });

};

Server.prototype._process = function (httpRequest, httpResponse) {
    var request = new Request(httpRequest);    // 824-831 - return w konstruktorze

    // 1381 - 1428
    var response = new Response(httpResponse);
    // 1246 - 1362
    //httpResponse.end('404 not found'); return;

    var requestProcessor = this.createRequestProcessor();
    requestProcessor.process(request, response);
    // 1243 - 1275

    // 900 - 932

};

Server.prototype.getServer = function () {
    return this;
};

Server.prototype.createRequestProcessor = function () {
    var requestProcessor = new this.RequestProcessorClass(this.getServer());
    if(!(requestProcessor instanceof  RequestProcessor)){
        throw Server.Exception.WRONG_PROCESSOR_CLASS;
    }
    return requestProcessor;
};

Server.prototype.getStats = function () {
    //Wsteczna kompatybilnosc do wywalenia po aktualizacji node-server
    return {};
};

/**
 * Namespace for exceptions messages.
 * @static
 * @namespace
 */
Server.Exception = {};
/**
 * @static
 * @public
 */
Server.Exception.WRONG_PROCESSOR_CLASS = "Request processor class must be inheriting RequestProcessor";
//

exports.Server = Server;
