var loadingCounter = null;
var cometEnabled = false;

var dojoDebugEnabled = true;

dojo.require("dojo.cookie");
dojo.require("dijit.Dialog");

var toSubscribeList = new Array();
var subscribeChannelsCount = 0;
var subscriptionSuccesfull = 0;

var loadingCount = 0;

function _log(aLogString) {
}

// раскомментировать для протоколирования
//function _log(aLogString) {
//    if (dojo.byId("chatList")) {
//        dojo.byId("chatList").innerHTML += "<div class=\"chatLog\">" + aLogString + "</div>";
//    }
//    scrollAndRemoveOld();
//}

function enableComet() {
    cometEnabled = true;
    dojo.require("dojox.cometd");
    dojo.addOnLoad(initializeComet);

    _log("CometD enabled");
}

function initializeComet() {
    try {
        if (cometEnabled && $cometdGetSubscriptionList) {
            dojox.cometd.init({"data": "UserId:" + dojo.cookie("#_userId")}, "/cometd");
            dojox.cometd.subscribed = cometSubscriptionSuccessfull;

            _log("CometD initialized");

            $cometdGetSubscriptionList(subscribeToCometChannels);

            _log("CometD subscription request sent");
        }
    } catch(ex) {
        alert(dojo.toJson(ex));
    }
}

function subscribeToCometChannels(aAttributes) {
    try {
        toSubscribeList = aAttributes["subscribedTo"];

        if (toSubscribeList == null) {
            toSubscribeList = new Array();
        }
        subscribeChannelsCount = toSubscribeList.length;

        dojox.cometd.publish("/jiva/system", { action: "initialize" });
        _log(" CometD » /jiva/system » { action: 'initialize'}");

        _log("CometD subscription list: " + dojo.toJson(toSubscribeList));
        cometSubscriptionSuccessfull();
    } catch(ex) {
        alert(dojo.toJson(ex));
    }
}

function cometSubscriptionSuccessfull(aChannelName) {
    if (aChannelName && aChannelName == "/meta/subscribe") {
        _log("CometD subscription succesfull: " + aChannelName);
        return;
    }

    try {
        if (aChannelName) {
            subscriptionSuccesfull++;
            _log("CometD subscription succesfull: " + aChannelName);
        }

        if (toSubscribeList != 0) {
            var toSubscribe = toSubscribeList[0];
            toSubscribeList.splice(0, 1);
            dojox.cometd.subscribe(toSubscribe, true, cometMessageReceived);
            _log("CometD subscription: " + toSubscribe);
        }
    } catch(ex) {
        alert(dojo.toJson(ex));
    }

    if (subscriptionSuccesfull == subscribeChannelsCount && subscribeChannelsCount != 0) {
        dojox.cometd.publish("/jiva/system", { action: "history" });
        _log(" CometD » /jiva/system » { action: 'history'}");

        cometCommandProcessor({chat: "enable"});
    }
}

function sendCometMessage(aMessage) {
    try {
        if (aMessage && aMessage.length != 0) {
            dojox.cometd.publish("/jiva/messages", { message: aMessage });
        }

        _log(" CometD » /jiva/messages » " + aMessage);
    } catch(ex) {
        alert(dojo.toJson(ex));
    }
}

function cometMessageReceived(aReceivedData) {
    try {
        if (aReceivedData.channel.substring(0, "/jiva/commands".length) == "/jiva/commands") {
            if (cometCommandProcessor) {
                cometCommandProcessor(aReceivedData.data);
            }

            _log(" CometD « /jiva/commands « " + dojo.toJson(aReceivedData.data));
        } else if (aReceivedData.channel.substring(0, "/jiva/users".length) == "/jiva/users" ||
                aReceivedData.channel.substring(0, "/jiva/locations".length) == "/jiva/locations") {
            if (cometChatProcessor) {
                cometChatProcessor(aReceivedData.timestamp, aReceivedData.data);
            }

            _log(" CometD « " + aReceivedData.channel + " « " + dojo.toJson(aReceivedData.data));
        }
    } catch(ex) {
        alert(dojo.toJson(ex));
    }
}

/**
 * Показывает "Loading..." (див). Див должен быть в документе + он должен называться loadingFrame.
 * @param show
 */
function showLoading(aShowOrHide) {
    if (document.getElementById("loadingFrame")) {
        if (loadingCounter) {
            clearTimeout(loadingCounter);
        }

        if (aShowOrHide) {
            loadingCount++;
            loadingCounter = setTimeout('dojo.byId("loadingFrame").style.visibility = "visible"; dojo.fadeIn({node: "loadingFrame", duration: 200, end: 0.4}).play(50); loadingCounter = null;', 200);
            allButtonsOff(true);
        } else {
            loadingCount--;
            if (loadingCount <= 0) {
                loadingCount = 0;
                loadingCounter = setTimeout('dojo.fadeOut({node: "loadingFrame", duration: 100, onEnd: function(){dojo.byId("loadingFrame").style.visibility = "hidden";}}).play(50); loadingCounter = null;', 50);
                allButtonsOff(false);
            }
        }
    }
}

/**
 * Выключает все кнопки, пока выполняется запрос. Это просто правильно.
 * @param needOff
 */
function allButtonsOff(aNeedOff) {
    try {
        var buttons = document.getElementsByTagName("INPUT");
        if (buttons) {
            for (var i = 0; i < buttons.length; i++) {
                if (buttons[i].type.toLowerCase() == "button" || buttons[i].type.toLowerCase() == "submit") {
                    buttons[i].disabled = aNeedOff ? "1" : null;
                }
            }
        }
    } catch(ex) {
        alert(ex);
    }
}

function jivaSend(aAction, aCallback, aAttributes, aObjects) {
//    document.body.innerHTML += "Call: " + aAction + "; " +
//                               "<div style=\"margin-left: 20px;\">" +
//                               dojo.json.serialize(aAttributes).replace(/\\n/g, "<br/>").replace(/},{/g, "},<br/>{").replace(/\[{/g, "[<br/>{").replace(/}\]/g, "}<br/>]") + "</div><br/>";
    showLoading(true);

    sendRequest({
        _action : aAction,
        _attributes : aAttributes,
        _objects : aObjects,
        _callback : aCallback
    });
}

function sendRequest(aObject) {
    var requestText = serialize(aObject._action, aObject._attributes, aObject._objects);

    var deferred = dojo.rawXhrPost({
        url : "/",
        error : function (aResponse, aIOArgs) {
                    processError(aResponse, aIOArgs);
                },
//        sync: true,
        contentType : "text/plain",
        postData : requestText,
        preventCache : true
    });

    deferred.addCallback(function(aResponse) {
//        alert(aObject._action);
        processResult(aResponse, aObject._callback);
        return aResponse;
    });
}

// For the load, error and handle functions, the ioArgs object will contain the following properties:
//  args: the original object argument to the IO call.
//  xhr: For XMLHttpRequest calls only, the XMLHttpRequest object that was used for the request.
//  url: The final URL used for the call. Many times it will be different than the original args.url value.
//  query: For non-GET requests, the name1=value1&name2=value2 parameters sent up in the request.
//  handleAs: The final indicator on how the response will be handled.
//  id: For dojo.io.script calls only, the internal script ID used for the request.
//  canDelete: For dojo.io.script calls only, indicates whether the script tag that represents the request can be deleted after
//             callbacks have been called. Used internally to know when cleanup can happen on JSONP-type requests.
//  json: For dojo.io.script calls only: holds the JSON response for JSONP-type requests. Used internally to hold on to
//        the JSON responses. You should not need to access it directly -- the same object should be passed to the success callbacks directly.

function processError(aResponse, aIOArgs) {
    //ToDo: вставить тут процессинг ошибок, и так далее
    showLoading(false);

    if (dojoDebugEnabled) {
        console.error(aResponse);
    }
}

function processResult(aResponse, aCallback) {
    showLoading(false);

    var attributes = new Array();
    var objects = new Array();

    if (!aResponse) {
        aResponse = "";
    }

    var responseLines = aResponse.split(/\s*\n\s*/g);
    var inObject = false;
    var currentObjectName = null;
    var currentObject = null;

    var resultType = "OK";
    var resultDescription = "No description";

    for (var lineIndex = 0; lineIndex < responseLines.length; lineIndex++) {
        try {
            var responseLine = responseLines[lineIndex];
            var name = null;
            var index = null;
            var value = null;
            if (lineIndex == 0) {
                resultType = responseLine;
                continue;
            } else if (lineIndex == 1) {
                resultDescription = responseLine;

                if (resultType == "ERROR" || resultType == "EXCEPTION") {
                    break;
                } else {
                    continue;
                }
            }
            if (responseLine.charAt(0) == "$") {
                inObject = true;
                var nameIndexRevision = responseLine.split(/[\\=\\.\s]/g);
                currentObjectName = nameIndexRevision[0].substring(1);

                currentObject = new Object();
                currentObject._id = new Number(nameIndexRevision[1]);
                currentObject._revision = new Number(nameIndexRevision[2]);
                currentObject._attributes = new Array();
            } else if (responseLine.charAt(0) == "}") {
                inObject = false;
                objects[currentObjectName] = currentObject;
                currentObject = null;
                currentObjectName = null;
            } else {
                var equalsPosition = responseLine.indexOf("=");
                var typeValue = responseLine.substring(equalsPosition + 1);
                name = responseLine.substring(0, equalsPosition);
                value = "";

                var spacePosition;

                var typeSubstring2 = typeValue.substring(0, 2);
                var typeSubstring3 = typeValue.substring(0, 3);

                if (typeSubstring2 == "s ") {
                    value = typeValue.substring(2).replace(/%n/g, "\n").replace(/%%/g, "%");
                } else if (typeSubstring2 == "l " || typeSubstring2 == "d ") {
                    value = new Number(typeValue.substring(2));
                } else if (typeSubstring2 == "L.") {
                    spacePosition = typeValue.indexOf(" ");
                    index = typeValue.substring(2, spacePosition);
                    value = typeValue.substring(spacePosition + 1).replace(/%n/g, "\n").replace(/%%/g, "%");
                } else if (typeSubstring3 == "M.\"") {
                    //ToDo: отфильтровать те случаи, когда внутри ключа - "
                    spacePosition = typeValue.indexOf("\" ");
                    index = typeValue.substring(3, spacePosition);
                    value = typeValue.substring(spacePosition + 2).replace(/%n/g, "\n").replace(/%%/g, "%");
                }

                var editAttributes = attributes;
                if (inObject) {
                    editAttributes = currentObject._attributes;
                }

                if (index == null) {
                    editAttributes[name] = value;
                } else {
                    var array = editAttributes[name];
                    
                    if (!array) {
                        array = new Array();
                        editAttributes[name] = array;
                    }

                    array[index] = value;
                }
            }
        } catch(ex) {
            alert(dojo.toJson(ex));
        }
    }

//    document.body.innerHTML += "Response: " +
//                               "<div style=\"margin-left: 20px;\">" +
//                               aResponse.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/\n/g, "<br/>").replace(/%n/g, "<br/>") +
//                               "</div><br/>" +
//                               "<pre>" +
//                               dojo.toJsonIndentStr(objects) +
//                               "</pre><br/>";

    if (aCallback) {
        aCallback(attributes, objects, resultType, resultDescription);
    }
}

function $a(aName, aType, aValue) {
    return {
        _name : aName,
        _type : aType,
        _value : aValue
    }
}

function serialize(aAction, aAttributes, aObjects) {
    var result = "%" + aAction + "\n";

    var attributeName;
    var attribute;

    var valueIndex;

    for (attributeName in aAttributes) {
        attribute = aAttributes[attributeName];

        if (attribute != null && attribute != undefined && attribute._value != null && attribute._value != undefined) {
            if (attribute._type == "L") {
                for (valueIndex in attribute._value) {
                    result += attribute._name + "=" + attribute._type + "." + valueIndex + " " + replacePercents(new String(attribute._value[valueIndex])) + "\n";
                }
            } else if (attribute._type == "M") {
                for (valueIndex in attribute._value) {
                    result += attribute._name + "=" + attribute._type + ".\"" + valueIndex + "\" " + replacePercents(new String(attribute._value[valueIndex])) + "\n";
                }

                result += attribute._name + "=" + attribute._type + " " + replacePercents(new String(attribute._value)) + "\n";
            } else {
                result += attribute._name + "=" + attribute._type + " " + replacePercents(new String(attribute._value)) + "\n";
            }
        }
    }

    for (var objectName in aObjects) {
        var object = aObjects[objectName];

        result += "$" + object._name;

        if (object._id) {
            result += "=" + object._id + ".";
        }

        if (object._revision) {
            result += object._revision;
        }

        result += " {\n";

        for (attributeName in object._attributes) {
            attribute = object._attributes[attributeName];

            if (attribute != null && attribute != undefined && attribute._value != null && attribute._value != undefined) {
                if (attribute._type == "L") {
                    for (valueIndex in attribute._value) {
                        result += attribute._name + "=" + attribute._type + "." + valueIndex + " " + replacePercents(new String(attribute._value[valueIndex])) + "\n";
                    }
                } else if (attribute._type == "M") {
                    for (valueIndex in attribute._value) {
                        result += attribute._name + "=" + attribute._type + ".\"" + valueIndex + "\" " + replacePercents(new String(attribute._value[valueIndex])) + "\n";
                    }

                    result += attribute._name + "=" + attribute._type + " " + replacePercents(new String(attribute._value)) + "\n";
                } else {
                    result += attribute._name + "=" + attribute._type + " " + replacePercents(new String(attribute._value)) + "\n";
                }
            }
        }

        result += "}\n";
    }

    return result;
}

function replacePercents(aString) {
    if (aString) {
        return new String(aString).replace(/%/g, "%%").replace(/\n/g, "%n");
    } else {
        return "";
    }
}
