function mcConnect () {
    "use strict";

    var CLIENT_TO_SERVER = 'client-to-server-cmd';
    var SERVER_TO_CLIENT = 'server-to-client-cmd';

    var CallBackFunctionPull = {};
    var sendStack = [];
    var self      = this;
    var _config   = {
        idError              : 0,
        AjaxInProgress       : false,
        _Timeout             : 20000,
        CRLF                 : "\r\n",
        CR                   : "\r",
        LF                   : "\n"
    };

    self.NoCMD          = false;
    self.CallBackNoCMD  = null;
    self.LeaveCallBacks = false;

    function check(idx, CMD) {
        CallBackFunctionPull[CMD].splice(idx, 1);

        if (CallBackFunctionPull[CMD].length === 0) {
            delete CallBackFunctionPull[CMD];
        }

        return CallBackFunctionPull.hasOwnProperty(CMD) ? CallBackFunctionPull[CMD].length > 0 : false;
    }
    
    function isObject(obj) {
        return Object.prototype.toString.call( obj ) === '[object Object]';
    }

    function requestError(XMLHttpRequest) {
        CallBackFunctionPull = {};
        sendStack = [];

        if (XMLHttpRequest >= 1 || XMLHttpRequest === undefined){
            if (self.OnError) {
                //self.OnError((XMLHttpRequest == '' || !XMLHttpRequest)? "error" : XMLHttpRequest);
                self.OnError("error");
            }

            if (mcConst.socketIO){
                mcConst.socketIO.disconnect();

                // throw new Error("Network error");
            }
        }
    }

    function __SendData(_data){
        mcConst.socketIO.emit(CLIENT_TO_SERVER, _data);
    }

    function RequestSuccess(msg){
        var CR_IDX = msg.length ? msg.indexOf(_config.CR) : -1;

        // console.log(msg);

        if (CR_IDX !== -1) {
            var cmd  = msg.slice(0, CR_IDX);
            var data = msg.slice(CR_IDX + 1, msg.length + 1);

            if (self.NoCMD) {
                self.CallBackNoCMD.apply(msg);
            } else {
                self.RunCallBackFunctionByCMD(cmd, data);
            }
        } else {
            var cmd  = "ping";

            if (self.NoCMD) {
                self.CallBackNoCMD.apply(msg);
            } else {
                self.RunCallBackFunctionByCMD(cmd, msg);
            }
        }
    }

    /**
     * @return {number}
     */
    self.SendDataToServer = function(_data){
        var sendingData = '';
        var prepareData = {};

        if ((!_data) || (_data.length === 0)) {
            if (console && console.warn) {
                console.warn('SendDataToServer: _data is empty or undefined');
            }

            return 1;
        } else {
            switch (typeof _data){
                case "string":
                    sendingData = _data;
                break;

                case "object":
                     prepareData = mcService.Marge({
                        CMD        : '',
                        Data       : '',
                        OnReceive  : null
                    }, _data || {});

                    if (prepareData.Data === ''){
                        console.error('SendDataToServer: "Data" is undefined');

                        return 2;
                    }

                    if (isObject(prepareData.Data)){
                        prepareData.Data = JSON.stringify(prepareData.Data);
                    }

                    sendingData = prepareData.CMD + _config.CR + prepareData.Data;

                    if (prepareData.CMD !== '' && prepareData.OnReceive){
                        self.SetCallBackFunctionByCMD(prepareData.CMD, prepareData.OnReceive);
                    }
                break;
            }

            __SendData(sendingData, prepareData.immediately);
        }
    };

    //=====================================================================
    self.OnError        = undefined;

    //=====================================================================
    self.SetCallBackFunctionByCMD = function(CMD, callback){
        if (callback){
            if (!CallBackFunctionPull.hasOwnProperty(CMD)) {
                CallBackFunctionPull[CMD] = [];
            }

            CallBackFunctionPull[CMD].push(callback);
        }
    };

    //=====================================================================
    self.ClearAllCallBackFunctionByCMD = function(){
        for (var i in CallBackFunctionPull){
            if (CallBackFunctionPull.hasOwnProperty(i)) {
                delete CallBackFunctionPull[i];
            }
        }
    };

    //=====================================================================
    self.RunCallBackFunctionByCMD = function(CMD, MSG){
        var next = CallBackFunctionPull.hasOwnProperty(CMD) ? CallBackFunctionPull[CMD].length > 0 : false;
        var idx  = 0;

        if (!next && CMD != 'OK') {
            console.warn("CMD: \"" + CMD + "\" not have request func");

            requestError();
        }

        while (next){
            if (CallBackFunctionPull[CMD] && CallBackFunctionPull[CMD][idx]){
                if (isObject(CallBackFunctionPull[CMD][idx])) {
                    CallBackFunctionPull[CMD][idx].cb.apply(MSG);

                    if (CallBackFunctionPull[CMD][idx].one){
                        next = check(idx, CMD);

                        continue;
                    }
                } else {
                    CallBackFunctionPull[CMD][idx].apply(MSG);
                }

                if (self.LeaveCallBacks){
                    idx++;
                } else {
                    next = check(idx, CMD);
                }
            } else {
                next = false;
            }
        }
    };

    mcConst.initWS = function (socketIO) {
        socketIO.on(SERVER_TO_CLIENT, RequestSuccess);
        socketIO.on('error',          requestError);
        socketIO.on('connect_failed', requestError);
        socketIO.on('disconnected',   requestError);
        socketIO.on('reconnecting',   requestError);
    }
}
