//Object Extension
if (typeof Array.isArray === "undefined") {
    Array.isArray = function (arg) {
        return Object.prototype.toString.call(arg) === "[object Array]";
    };
}

if (typeof console == "undefined"){
    console = {};
    console.log = function(data){
       
        }
}

if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function (obj, fromIndex) {
    if (fromIndex == null) {
        fromIndex = 0;
    } else if (fromIndex < 0) {
        fromIndex = Math.max(0, this.length + fromIndex);
    }
    for (var i = fromIndex, j = this.length; i < j; i++) {
        if (this[i] === obj)
            return i;
    }
    return -1;
  };
}


function ChatEngine(sessionID, peerName){
    //Instance Variables
    this.sessionID = sessionID;
    this.peerName = peerName;
    this.status = false;
    this.commands = new Object();
}

//Instance Methods
ChatEngine.prototype.closeSessionReq = function(){
    this.status = false;
    var command = new Object();
    command.name = "closeSession";
    if(ChatEngine.sessionType == ChatEngine.USER_WINDOW){
        command.parameters = [ChatEngine.CLIENT_CLOSED];
    } else {
        command.parameters = [ChatEngine.STAFF_CLOSED];
    }
    command.parameters.push(true);
    this.cacheSessionCommand(command);
}

ChatEngine.prototype.setUserTypingReq = function(value){
    //    if(value != false)
    //        value = true;
    var command = new Object();
    command.name = "setUserTyping";
    command.parameters = new Array();
    command.parameters.push(value);
    this.cacheSessionCommand(command);
}

ChatEngine.prototype.setPeerTyping = function(param){
    ChatEngine.updateUI(this.sessionID, "typingState", param);
}

ChatEngine.prototype.sendMessageReq = function(message){
    if(this.commands['setMessage'] != undefined && this.commands['setMessage'] != null)
        message =  this.commands['setMessage'][0]+message;

    var command = new Object();
    command.name = "setMessage";
    command.parameters = new Array();
    command.parameters.push(message+"\n\r");
    this.cacheSessionCommand(command);
    
}

ChatEngine.prototype.appendMessage = function(param){
    ChatEngine.updateUI(this.sessionID, "text", param);
}

ChatEngine.prototype.getSessionRequests = function(){
    var data = new Object();
    data.sessionID = this.sessionID;
    data.commands = this.flushSessionCommands();
    return data;
}

ChatEngine.prototype.cacheSessionCommand = function(command){
    this.commands[command.name] = command.parameters;
}

ChatEngine.prototype.flushSessionCommands = function(){
    var commands = this.commands;
    this.resetSessionCommands();
    return commands;
}

ChatEngine.prototype.resetSessionCommands = function(){
    this.commands = new Object();
    if(ChatEngine.ui == null || !ChatEngine.uiReady)
        this.setUserTypingReq(false);
    else
        this.setUserTypingReq(ChatEngine.ui.getLocalTypingStatus(this.sessionID));
}

ChatEngine.prototype.evaluate = function(commands){
    var serverCommands = commands['serverCommands'];

    for(var command in serverCommands){
        switch(command){
            case "incomingMessage":
                this.appendMessage(serverCommands[command]);
                break;
            case "incomingUserTyping":
                this.setPeerTyping(serverCommands[command]);
                break;
        }
    }
}

//Constants
ChatEngine.PING_URL = window.location.protocol+"//"+window.location.hostname+"/chat/index.php";

//Static variables
ChatEngine.userID;
ChatEngine.userName;
//ChatEngine.sessionType;
ChatEngine.userIdentified = false;
ChatEngine.autoConnect = false;
ChatEngine.keepBeating = true;
ChatEngine.pingInterval = -1;
ChatEngine.defaultPingInterval = 1500;
ChatEngine.onlineStaff = false;
ChatEngine.instances =[];
ChatEngine.commands = {};
ChatEngine.inQueue = null;
ChatEngine.ui = null;
ChatEngine.uiReady = false;
ChatEngine.uiElements = {};
ChatEngine.heartID = null;
ChatEngine.alwaysCheckOnlineStaff = false;
ChatEngine.sessionDepartment = null;
ChatEngine.isStaffSessionAvailable = false;
ChatEngine.sessionType = -1;
ChatEngine.serverTime = "";
ChatEngine.timeout = -1;
ChatEngine.lastSuccessfullPing = 0;
ChatEngine.packetsCache = [];

//Constants
ChatEngine.STAFF_WINDOW = 2;
ChatEngine.USER_WINDOW = 3;
ChatEngine.CLIENT_CLOSED = 4;
ChatEngine.STAFF_CLOSED = 5;

//Static methods
ChatEngine.setDepartments = function(dep){
    ChatEngine.departments = dep;
}

ChatEngine.getPingInterval = function(){
    if(ChatEngine.pingInterval > 0)
        return ChatEngine.pingInterval;
    else
        return ChatEngine.defaultPingInterval;
}

ChatEngine.initiateUI = function(){
    if(ChatEngine.userIdentified){
        if(ChatEngine.sessionType == ChatEngine.USER_WINDOW){
            ChatEngine.ui = new ChatClientUI();
            ChatEngine.ui.initiateUIElements(ChatEngine.uiElements);
        }else{
            if(typeof ChatStaffUI !== undefined){
                ChatEngine.ui = new ChatStaffUI();
                ChatEngine.uiReady = true;
            } else {
                alert("Chat initialization failed... You must be logged in as a staff member...");
                ChatEngine.ui = new ChatClientUI();
                ChatEngine.uiReady = false;
            }
        }
        //if chat started append event listeners
        ChatEngine.ui.setSessionClosingEventListener(ChatEngine.sessionClosingEventListener);
        ChatEngine.ui.setNewMessageEventListener(ChatEngine.newMessageEventListener);
        ChatEngine.ui.setUIReadyEventListener(ChatEngine.UIReadyEventListener);
        ChatEngine.ui.setUIDestroyedEventListener(ChatEngine.UIDestroyedEventListener);
    }
}

ChatEngine.getRequests = function(){
    if(ChatEngine.inQueue == null){        
        ChatEngine.lastSuccessfullPing = 0;
        var data = new Object();
        data.packetID = ChatEngine.generatePacketID();
        data.sessionType = ChatEngine.sessionType;
        data.commands = ChatEngine.flushCommands();
        data.sessions = (ChatEngine.instances.length > 0) ? new Array() : "";

        for(i = 0; i < ChatEngine.instances.length; i++)
            data.sessions.push(ChatEngine.instances[i].getSessionRequests());
        ChatEngine.inQueue = data;
        return data;
    }else{
        ChatEngine.lastSuccessfullPing += 2;
        if(ChatEngine.lastSuccessfullPing >= ChatEngine.timeout){
            ChatEngine.closeAllSessions();
            ChatEngine.lastSuccessfullPing = 0;
        }
        return ChatEngine.inQueue;
    }

}

ChatEngine.cacheCommand = function(command){
    ChatEngine.commands[command.name] = command.parameters;
}

ChatEngine.flushCommands = function(){
    var commands = ChatEngine.commands;
    if(ChatEngine.userIdentified && ChatEngine.sessionType == ChatEngine.USER_WINDOW && ChatEngine.autoConnect && ChatEngine.instances.length == 0)
        ChatEngine.createSessionReq();
    ChatEngine.resetCommands();
    return commands;
}

ChatEngine.resetCommands = function(){
    ChatEngine.commands = new Object();

    ChatEngine.getPendingUsersReq();    
    if(ChatEngine.alwaysCheckOnlineStaff)
        ChatEngine.getOnlineStaffReq();
    if(!ChatEngine.userIdentified)
        ChatEngine.getUserInfoReq();
    if(ChatEngine.pingInterval == -1)
        ChatEngine.getPingIntervalReq();
    if(ChatEngine.sessionType != ChatEngine.USER_WINDOW)
        ChatEngine.setPopUpOpenReq();
    if(ChatEngine.timeout == -1)
        ChatEngine.getTimeoutReq();
}

ChatEngine.execute = function(){
    var requests = ChatEngine.getRequests();
    $.ajax({
        url: ChatEngine.PING_URL,
        type: "POST",
        data: requests,
        cache: false,
        success: (function ajaxSuccessClosure(dataSent) {
            return function ajaxSuccess (data){ 
                ChatEngine.inQueue = null;
                ChatEngine.response = data;
//                try{
                    if(data !== null && ChatEngine.packetsCache.indexOf(data.packetID) == -1){
                        ChatEngine.packetsCache.push(data.packetID);
                        if(ChatEngine.packetsCache.length > 70) {
                            ChatEngine.packetsCache.splice(50,20);
                        }
                        if(typeof data.timestamp !== "undefined"){
                            ChatEngine.serverTime = data.timestamp;
                        }
                        ChatEngine.dispatch(data);
                        if(data.debug !== null && typeof data.debug != "undefined" ){
                            console.log("\n");
                            for(var i=0,cnt=data.debug.length;i<cnt;i++){
                                console.log(data.debug[i]);
                            }
                            console.log("\n");
                        }
                    }
//                } catch(err){ 
//                    var error = "Error "+err.message+"\nRequest:\n"+JSON.stringify(dataSent)+"\nResponse:\n"+JSON.stringify(data)+"\nError:\n"+JSON.stringify(err);
//                    if(ChatEngine.keepBeating)
//                        alert(error);
//                    ChatEngine.keepBeating = false;
//                }
            //ChatEngine.setBusy(false);
            
            }
        }(this.data)),
        error: function(data, text, error){
            if(!ChatEngine.keepBeating)
                ChatEngine.execute();
        //ChatEngine.setBusy(false);
        },
        dataType: "json",
        timeout: Math.floor(ChatEngine.getPingInterval()*40)
    });

    if(ChatEngine.keepBeating)
        ChatEngine.heartID = setTimeout(function timeoutFunction(){
            ChatEngine.execute()
        }, ChatEngine.getPingInterval());
    else if(ChatEngine.heartID != null)
        clearTimeout(ChatEngine.heartID);
}

ChatEngine.dispatch = function(data){
    var sessions = null;
    if(data !== null) {
        ChatEngine.evaluate(data.commands);
        sessions = data.sessions;
    }
    if(Array.isArray(sessions)){
        for(var i = 0, cnt = sessions.length; i < cnt; i++){
            if(sessions[i] === null) {
                sessions.splice(i,1);
                continue;
            }
            var session = ChatEngine.getInstanceByID(sessions[i].sessionID);
            if(session != null){
                session.evaluate(sessions[i]);
            }else{
                ChatEngine.createSession(sessions[i]);
            }
        }
        ChatEngine.searchAndDestroy(sessions);
    } else {
        ChatEngine.closeAllSessions();
    }
}

ChatEngine.evaluate = function(commands){
    for(var command in commands){
        switch(command){
            case "getUserInfo":
                ChatEngine.getUserInfoRes(commands[command]);
                break;
            case "getTimeout":
                ChatEngine.getTimeoutRes(commands[command]);
                break;
            case "getOnlineStaff":
                ChatEngine.getOnlineStaffRes(commands[command]);
                break;
            case "getPingInterval":
                ChatEngine.getPingIntervalRes(commands[command]);
                break;
            case "getPendingUsers":
                if(ChatEngine.uiReady)
                    ChatEngine.getPendingUsersRes(commands[command]);
                break;
        }
    }
}

ChatEngine.setBusy = function(state){
    if(ChatEngine.ui != null && !ChatEngine.uiReady)
        ChatEngine.ui.setChatBusy(state);
}



ChatEngine.getUserInfoReq = function(){
    var command = new Object();
    command.name = "getUserInfo";
    command.parameters = new Array();
    command.parameters.push(ChatEngine.sessionType);
    ChatEngine.cacheCommand(command);
}

ChatEngine.getUserInfoRes = function(param){
    if(param.type == "error" && ChatEngine.keepBeating){
        ChatEngine.keepBeating = false;
        alert("Authentication failure.\nYou need to be logged in as a staff member to be able to chat...");
    } else if(!ChatEngine.userIdentified && param.id != null){
        ChatEngine.userID = param.id;
        ChatEngine.userName = param.name;
        ChatEngine.userIdentified = true;
        ChatEngine.initiateUI();
    }
}

ChatEngine.getTimeoutReq = function(){
    var command = new Object();
    command.name = "getTimeout";
    command.parameters = new Array();
    command.parameters.push(-1);
    ChatEngine.cacheCommand(command);
}

ChatEngine.getTimeoutRes = function(param){
    ChatEngine.timeout = param.timeout;
}

ChatEngine.createSessionReq = function(){
    var command = new Object();
    command.name = "createSession";
    command.parameters = new Array();
    command.parameters.push(ChatEngine.sessionDepartment);
    ChatEngine.cacheCommand(command);
    if(ChatEngine.ui != null && ChatEngine.sessionType == ChatEngine.USER_WINDOW)
        ChatEngine.ui.reset();
}

ChatEngine.createSession = function(param){
    if(ChatEngine.ui != null){        
        var inst = new ChatEngine(param.sessionID, param.peerName);
        ChatEngine.instances.push(inst);
        if(ChatEngine.sessionType == ChatEngine.USER_WINDOW){
            ChatEngine.updateUI(-1, "reset", null);
            ChatEngine.updateUI(-1, "addSession", param);
        }else
            ChatEngine.updateUI(-1, "addSession", param);
        ChatEngine.sessionsCreated = true;
    }
}

ChatEngine.closeSession = function(sessionID){
    var index = ChatEngine.getInstanceIndex(sessionID);
    ChatEngine.instances.splice(index, 1);
    ChatEngine.ui.closeSession(sessionID);
    ChatEngine.autoConnect = false;
}

ChatEngine.setPopUpOpenReq = function(){
    var command = new Object();
    command.name = "setPopupOpened";
    command.parameters = new Array();
    command.parameters.push(true);
    ChatEngine.cacheCommand(command);
}

ChatEngine.getOnlineStaffReq = function(){
    var command = new Object();
    command.name = "getOnlineStaff";
    command.parameters = new Array();
    command.parameters.push(ChatEngine.departments);
    ChatEngine.cacheCommand(command);
}


ChatEngine.getOnlineStaffRes = function(param){
    ChatEngine.onlineStaff = param.result;
    ChatEngine.updateClientChatButtons();
}


ChatEngine.getPendingUsersReq = function(){
    var command = new Object();
    command.name = "getPendingUsers";
    command.parameters = new Array();
    command.parameters.push(true);
    ChatEngine.cacheCommand(command);
}

ChatEngine.goOfflineReq = function(){
    var command = new Object();
    command.name = "goOffline";
    command.parameters = new Array();
    ChatEngine.cacheCommand(command);
}

ChatEngine.getPendingUsersRes = function(param){
    ChatEngine.updateUI(-1, "pendingUsers", param);
}


ChatEngine.getPingIntervalReq = function(){
    var command = new Object();
    command.name = "getPingInterval";
    command.parameters = new Array();
    command.parameters.push(true);
    ChatEngine.cacheCommand(command);
}

ChatEngine.getPingIntervalRes = function(param){
    if(param.interval != null && param.interval != undefined && param.interval > 0){
        ChatEngine.pingInterval = param.interval*1000;
    }
}

ChatEngine.searchAndDestroy = function(sessions){
    for(i = 0; i < ChatEngine.instances.length; i++){
        var found = false;
        for(j = 0; j < sessions.length; j++){
            if(sessions[j].sessionID == ChatEngine.instances[i].sessionID){
                found = true;
                break;
            }
        }
        if(!found){
            ChatEngine.closeSession(ChatEngine.instances[i].sessionID);
        }
    }
}

ChatEngine.closeAllSessions = function() {
    for(i = 0; i < ChatEngine.instances.length; i++){
        ChatEngine.closeSession(ChatEngine.instances[i].sessionID);
    }
}

ChatEngine.getInstanceByID = function(id){
    for(i = 0; i < ChatEngine.instances.length; i++){
        if(ChatEngine.instances[i].sessionID == id) {
            return ChatEngine.instances[i];
        }
    }
    return null;
}

ChatEngine.getInstanceIndex = function(id){
    for(i = 0; i < ChatEngine.instances.length; i++){
        if(ChatEngine.instances[i].sessionID == id) {
            return i;
        }
    }
    return null;
}

ChatEngine.updateUI = function(sessionID, uiElement, data){
    if(ChatEngine.uiReady){
        switch(uiElement){
            case "addSession":
                if(ChatEngine.sessionType == ChatEngine.USER_WINDOW)
                    ChatEngine.ui.addSession(data.sessionID, data.peerName, data.peerIP);
                else
                    ChatEngine.ui.addSession(data.sessionID, data.peerName, data.peerIP, data.peerID, data.domain);
                break;
            case "reset":
                ChatEngine.ui.reset();
                break;
            case "text":
                var messages = data.text.split("\n");
                for(var i=0,cnt=messages.length-1;i<cnt;i++)
                    ChatEngine.ui.setMessage(sessionID, messages[i],data.timestamp);
                break;
            case "typingState":
                ChatEngine.ui.setPeerTypingStatus(sessionID, data.status);
                break;
            case "pendingUsers":
                ChatEngine.ui.setPendingUsers(data.result);
                break;
        }
    }else{
        setTimeout(function(){
            ChatEngine.updateUI(sessionID, uiElement, data);
        }, 10);
    }
}

ChatEngine.sessionClosingEventListener = function(event){
    if(event.disableAutoConnect == true)
        ChatEngine.autoConnect = false;
    var instance = ChatEngine.getInstanceByID(event.sessionID);
    if(instance != null){
        instance.closeSessionReq();
    }
}

ChatEngine.newMessageEventListener = function(event){
    var instance = ChatEngine.getInstanceByID(event.sessionID);
    if(instance != null){
        instance.sendMessageReq(event.text);
    }
}

ChatEngine.UIReadyEventListener = function(event){
    ChatEngine.uiReady = true;
    if(ChatEngine.sessionType == ChatEngine.USER_WINDOW)
        ChatEngine.disactivateClientChatButtons();
}

ChatEngine.UIDestroyedEventListener = function(event){
    ChatEngine.uiReady = false;
    if(ChatEngine.sessionType == ChatEngine.STAFF_WINDOW && ChatEngine.keepBeating == true){
        ChatEngine.goOfflineReq();
        ChatEngine.keepBeating = false;
        ChatEngine.uiElements.buttons[1].click();
    } else if (ChatEngine.sessionType == ChatEngine.USER_WINDOW) {
        ChatEngine.autoConnect = false;
        ChatEngine.activateClientChatButtons();
    }
}

ChatEngine.generatePacketID = function(){
    var chars = "0123456789qwertyuiopasdfghjklzxcvbnm";
    var string_length = 8;
    var randomstring = '';
    for (var i=0; i<string_length; i++) {
        var rnum = Math.floor(Math.random() * chars.length);
        randomstring += chars.substring(rnum,rnum+1);
    }
    return "p"+randomstring;
}

ChatEngine.initChat = function(obj){
    var buttonsArray = new Array();
    var departments = new Array();
    var dep, i=0 ;
    ChatEngine.sessionType = ChatEngine.USER_WINDOW;
    ChatEngine.alwaysCheckOnlineStaff = true;    
    
    for(dep in obj.buttons){
        buttonsArray[i] = $(obj.buttons[dep]);
        buttonsArray[i].addClass("chat-offline");
        departments.push(dep);
        i++;
    }
    
    ChatEngine.setDepartments(departments);
    ChatEngine.debug = (obj.hasOwnProperty("debug") && obj.debug == 1)? 1 : 0;
    ChatEngine.uiElements.dialogTitle = obj.dialogTitle;
    ChatEngine.bindClientUIElements(buttonsArray);
    //ChatEngine.resetCommands();
    ChatEngine.execute();
}

ChatEngine.bindClientUIElements = function(chatButtons){
    ChatEngine.uiElements.chatButtons = chatButtons;
    ChatEngine.activateClientChatButtons();
}

ChatEngine.activateClientChatButtons = function(){
    for(var i=0;i<ChatEngine.uiElements.chatButtons.length;i++){
        ChatEngine.uiElements.chatButtons[i].click(function(department){
            return function(){
                ChatEngine.autoConnect = true;
                ChatEngine.sessionDepartment = department;
                ChatEngine.ui.openChatDialog();
            }
        }(ChatEngine.departments[i]));
    }
}

ChatEngine.disactivateClientChatButtons = function() {
    for(var i=0;i<ChatEngine.uiElements.chatButtons.length;i++){
        ChatEngine.uiElements.chatButtons[i].unbind("click");
    }
}

ChatEngine.updateClientChatButtons = function() {
    for(var i=0;i<ChatEngine.uiElements.chatButtons.length;i++){
        if(ChatEngine.onlineStaff[i])
            ChatEngine.uiElements.chatButtons[i]
                        .removeClass("chat-offline")
                        .addClass("chat-online");
        else
            ChatEngine.uiElements.chatButtons[i]
                        .removeClass("chat-online")
                        .addClass("chat-offline");
    }
}
