/**
 * Copyright 2018 Avaya Inc. All Rights Reserved.
 * 
 * Usage of this source is bound to the terms described in licences/License.txt
 * 
 * Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or
 * Avaya Policy
 * 
 * Defines the low-level WebSocket connections and interactions with it
 */

var chatSocket = {

    // set to true if the user closes manually
    manualClose : false,

    // authentication token for customer's firewall
    authToken : '',

    reconnecting : false,

    /**
     * Open the socket.
     */
    openSocket : function () {
        'use strict';
        console.trace('WebChat: Opening the WebSocket');

        // Ensures only one connection is open at a time
        if (webSocket !== undefined && (webSocket.readyState !== WebSocket.CLOSED)) {
            console.warn('WebChat: WebSocket is already opened');
            return;
        }

        clearTimeout(chatConfig.reconnectionTimeout);

        // Create a new instance of the WebSocket using the specified url
        webSocket = new WebSocket(links.getWebChatUrl());

        // attach event handlers
        webSocket.onopen = chatSocket.handleOpen;
        webSocket.onmessage = chatSocket.handleMessage;
        webSocket.onerror = chatSocket.handleError;
        webSocket.onclose = function (event) {

            console.debug("WebChat: Websocket closed with code " + event.code);

            // disable the controls upon close
            chatUI.disableControls(true);

            // If it is an expected/graceful close, do not attempt to reconnect.
            // Don't attempt reconnect if we haven't connected successfully
            // before
            if (!chatConfig.previouslyConnected || chatConfig.dontRetryConnection || event.code === 1000 || event.code === 1005) {
                avayaGlobal.clearSessionStorage();
                chatSocket.handleClose(event);
            } else if (event.code === 1001) {
                chatSocket.setupRefresh();
            } else {
                chatSocket.reconnect();
            }
        };

    },

    /**
     * Close the websocket. Disable the controls, clear the users, and let the
     * user know that the chat is over.
     */
    handleClose : function (event) {
        'use strict';

        if (!event.wasClean) {
            console
                    .warn(
                            'WebChat: WebSocket closed abnormally. This may be caused by the user exiting before the chat starts or the agent closing the chat (in which case, ignore this), a certificate issue (e.g. your browser considers the certificate to be invalid), or an incorrect URL (e.g. not using a secure connection to a cluster that enforces secure connections). The URL is: ',
                            links.getWebChatUrl());
        } else {
            console.info('WebChat: Closing the WebSocket.');
        }

        chatUI.disableControls(true);
        webChat.initCalled = false;
        webChat.updateUsers(); // called here to hide the users
        var timer = chatConfig.resetTimer;

        // if the customer hasn't closed it manually, let them know.
        // otherwise, ignore the timer
        if (!chatSocket.manualClose) {
            var timerText = chatConfig.panelCloseText.replace("{0}",
                    timer / 1000);
            webChat.writeResponse(chatConfig.connectionClosedText,
                    chatConfig.writeResponseClassSystem);
            webChat.writeResponse(timerText,
                    chatConfig.writeResponseClassSystem);
        } else {
            timer = 0;
            webChat.initCalled = false;
        }
        chatSocket.closeTimer = setTimeout(webChat.resetChat, timer);

    },

    /**
     * Open the WebSocket. The user's controls will be disabled if there are no
     * agents, and the ping timer will start.
     * 
     * @param event
     */
    handleOpen : function (event) {
        'use strict';

        chatSocket.manualClose = false;

        // set up the ping mechanism here.
        webChat.pingInterval = setInterval(function () {
            webChat.sendPing();
        }, chatConfig.pingTimer);
        webChat.timeouts.push(webChat.pingInterval);
        webChat.chatLogin();

        // if there are agents in the chat, enable the controls
        if (Object.keys(webChat.users).length <= 0) {
            console.debug('WebChat: No users in room, disabling controls');
            chatUI.disableControls(true);
        } else {
            console.debug('WebChat: Agents already in chat, enabling controls');
            chatUI.disableControls(false);
        }
    },

    handleMessage : function (event) {
        'use strict';
        var msg = JSON.parse(event.data), body = msg.body;

        // Handle the message according to the type and method.
        // Notifications are in their own method to reduce complexity.
        if (msg.type === chatConfig.messageTypeNotification) {
            webChat.handleNotification(msg);
        } else if (msg.type === chatConfig.messageTypeError) {
            // parse the error message
            chatSocket.parseErrorMessage(body);
        } else if (msg.type === chatConfig.messageTypeAck) {
            // Nothing to do for acks
        } else {
            throw new TypeError('Unknown message type:\n' + msg);
        }
    },

    /**
     * Log errors to the console and alert the user that an error has occurred.
     * 
     * @param event
     */
    handleError : function (event) {
        'use strict';
        console.error("WebChat: WebSocket error", event);
        webChat.writeResponse(chatConfig.connectionErrorText,
                chatConfig.writeResponseClassSystem);
    },

    /**
     * Parse the error message
     */
    parseErrorMessage : function (error) {
        'use strict';
        console.log(error);
        var code = error.code;
        var message = error.errorMessage;

        console.warn('WebChat: An error with status', code,
                'occurred. Error message:', message);

        // HTTP 503 means "service unavailable" - which is a perfect description
        // for shutting down
        if (code === 503) {
            webChat.writeResponse(chatConfig.closedForMaintenanceText,
                    chatConfig.writeResponseClassSystem);
        } else {
            var errorMsg = chatConfig.errorOccurredText.replace("{0}",
                    error.code).replace("{1}", message);
            webChat
                    .writeResponse(errorMsg,
                            chatConfig.writeResponseClassSystem);
        }

        // allow the user to clear the page if not error code 1
        // this stands for "invalid message"
        if (code !== 1) {

            chatConfig.dontRetryConnection = true;
            chatSocket.clearRefresh();
            if (webSocket !== undefined) {
                webSocket.close();
            }
        }

    },

    /**
     * Attempt to reconnect the webSocket.
     */
    reconnect : function () {
        'use strict';

        if (chatConfig.dontRetryConnection) {
            console.warn("Attempting to reconnect when we shouldn't!");
            return;
        }

        if (webSocket.readyState !== WebSocket.OPEN) {
            webChat.custDetails.isContinuingAfterRefresh = false;
            chatConfig.reconnectionTimeout = setTimeout(function() {
                if (chatConfig.totalNumberOfRetries <= chatConfig.maxNumberOfRetries) {
                    webChat.writeResponse(chatConfig.attemptingToReconnectText, chatConfig.writeResponseClassSystem);
                    clearTimeout(chatConfig.reconnectionTimeout);
                    chatConfig.totalNumberOfRetries++;
                    chatSocket.openSocket();
                } else {
                    webChat.writeResponse(chatConfig.unableToReconnectText, chatConfig.writeResponseClassSystem);
                }
            }, chatConfig.retryInterval);
        }
    },

    /**
     * Reset the number of connection attempts after a successful login.
     */
    resetConnectionAttempts : function () {
        'use strict';
        chatConfig.totalNumberOfRetries = 0;
        clearTimeout(chatConfig.reconnectionTimeout);
    },

    /**
     * Reset the WebSocket.
     */
    resetWebSocket : function () {
        'use strict';
        webChat.initCalled = false;
        chatConfig.previouslyConnected = false;
        chatConfig.totalNumberOfRetries = 0;
        webSocket = undefined;
    },

    /**
     * Send a message over the WebSocket. May throw an InvalidStateError if
     * connection has failed; this can be ignored.
     * 
     * @param outMsg
     *            as a JSON object
     */
    sendMessage : function (outMsg) {
        'use strict';

        // prepend the authToken here
        var newMsg = $.extend({}, {
            "authToken" : chatSocket.authToken
        }, outMsg);

        if (webSocket !== null) {
            webSocket.send(JSON.stringify(newMsg));
        }
    },

    reloadAfterRefresh : function () {
        'use strict';
        var ak = avayaGlobal.getSessionStorage("ak");
        var guid = parseInt(avayaGlobal.getSessionStorage("guid"));
        var reloadTimestamp = parseInt(avayaGlobal
                .getSessionStorage("reloadTimestamp"));
        if (isNaN(reloadTimestamp)) {
            console
                    .warn("WebChat: Reload timestamp is not a valid UTC timestamp!");
            chatSocket.clearRefresh();
            return;
        }

        webChat.users = JSON.parse(avayaGlobal.getSessionStorage("users"));

        var currentTimestamp = Date.now();
        var expired = (currentTimestamp - reloadTimestamp) >= (chatConfig.refreshTimeoutSeconds * 1000);
        console.debug("Current and closing timestamps are", currentTimestamp,
                reloadTimestamp);
        console.debug(ak, guid, expired);

        if (expired) {
            console.warn("WebChat: session has probably expired");
            chatSocket.clearRefresh();
            return;
        }

        if (ak !== null && guid !== null && !isNaN(guid)) {
            console.debug("WebChat: reloading the chat");
            chatConfig.previouslyConnected = true;
            webChat.guid = guid;
            webChat.ak = ak;
            chatUI.reloadChatPanel();
            
            var custDetails = JSON.parse(avayaGlobal
                    .getSessionStorage("custDetails"));
            custDetails.isContinuingAfterRefresh = true;
            webChat.writeResponse(chatConfig.reloadingPageText,
                    chatConfig.writeResponseClassSystem);
            webChat.initChat(false, custDetails);

        }
    },

    setupRefresh : function () {
        'use strict';
        console.debug("WebChat: Refreshing page");
        avayaGlobal.setSessionStorage("guid", webChat.guid);
        avayaGlobal.setSessionStorage("ak", webChat.ak);
        avayaGlobal.setSessionStorage("custDetails", JSON
                .stringify(webChat.custDetails));
        avayaGlobal.setSessionStorage("reloadTimestamp", Date.now());
    },

    clearRefresh : function () {
        'use strict';
        console.debug("WebChat: clearing refresh");
        avayaGlobal.clearSessionStorage();
        webChat.initCalled = false;
    }
};
