// The wrapper around the web-socket connection to the server
(function(context, $) {

    "use strict";

    context.JK = context.JK || {};

    var logger = context.JK.logger;
    var msg_factory = context.JK.MessageFactory;

    // Let socket.io know where WebSocketMain.swf is
    context.WEB_SOCKET_SWF_LOCATION = "assets/flash/WebSocketMain.swf";

    var server = {};
    server.socket = {};
    server.signedIn = false;
    server.clientID = "";
    server.publicIP = "";
    server.dispatchTable = {};
    server.socketClosedListeners = [];
    server.connected = false;


    // handles logic if the websocket connection closes, and if it was in error then also prompt for reconnect
    function closedCleanup(in_error) {
        if(server.connected) {
            server.connected = false;

            context.JK.CurrentSessionModel.onWebsocketDisconnected(in_error);

            // notify anyone listening that the socket closed
            var len = server.socketClosedListeners.length;
            for(var i = 0; i < len; i++) {
                try {
                    server.socketClosedListeners[i](in_error);
                } catch (ex) {
                    logger.warn('exception in callback for websocket closed event:' + ex);
                }
            }
        }
    }

    server.registerOnSocketClosed = function(callback) {
        server.socketClosedListeners.push(callback);
    }

    server.registerMessageCallback = function(messageType, callback) {
        if (server.dispatchTable[messageType] === undefined) {
            server.dispatchTable[messageType] = [];
        }

        server.dispatchTable[messageType].push(callback);
    };

    server.unregisterMessageCallback = function(messageType, callback) {
        if (server.dispatchTable[messageType] !== undefined) {
            for(var i = server.dispatchTable[messageType].length; i--;) {
                if (server.dispatchTable[messageType][i] === callback)
                {
                    server.dispatchTable[messageType].splice(i, 1);
                    break;
                }
            }
        }

        if (server.dispatchTable[messageType].length === 0) {
            delete server.dispatchTable[messageType];
        }
    };

    server.connect = function() {
        logger.log("server.connect");
        var uri = context.JK.websocket_gateway_uri;  // Set in index.html.erb.
        //var uri = context.gon.websocket_gateway_uri; // Leaving here for now, as we're looking for a better solution.
        server.socket = new context.WebSocket(uri);
        server.socket.onopen = server.onOpen;
        server.socket.onmessage = server.onMessage;
        server.socket.onclose = server.onClose;
    };

    server.close = function(in_error) {
        logger.log("closing websocket");

        server.socket.close();

        closedCleanup(in_error);
    }

    server.rememberLogin = function() {
        var token, loginMessage;
        token = $.cookie("remember_token");
        loginMessage = msg_factory.login_with_token(token, null);
        server.send(loginMessage);
    };

    server.onOpen = function() {
        logger.log("server.onOpen");
        server.rememberLogin();
    };

    server.onMessage = function(e) {
        var message = JSON.parse(e.data),
            messageType = message.type.toLowerCase(),
            payload = message[messageType],
            callbacks = server.dispatchTable[message.type];

        logger.log("server.onMessage:" + messageType + " payload:" + JSON.stringify(payload));

        if (callbacks !== undefined) {
            var len = callbacks.length;
            for(var i = 0; i < len; i++) {
                try {
                    callbacks[i](message, payload);
                } catch (ex) {
                    logger.warn('exception in callback for websocket message:' + ex);
                }
            }
        }
        else {
            logger.log("Unexpected message type %s.", message.type);
        }
    };

    server.onClose = function() {
        logger.log("Socket to server closed.");

        closedCleanup(true);
    };

    server.send = function(message) {

        var jsMessage = JSON.stringify(message);

        logger.log("server.send(" + jsMessage + ")");
        if (server !== undefined && server.socket !== undefined && server.socket.send !== undefined) {
            server.socket.send(jsMessage);
        } else {
            logger.log("Dropped message because server connection is closed.");
        }
    };

    server.loginSession = function(sessionId) {
        var loginMessage;

        if (!server.signedIn) {
            logger.log("Not signed in!");
            // TODO: surface the error
            return;
        }

        loginMessage = msg_factory.login_jam_session(sessionId);
        server.send(loginMessage);
    };

    server.sendP2PMessage = function(receiver_id, message) {
        logger.log("P2P message from [" + server.clientID + "] to [" + receiver_id + "]: " + message);
        var outgoing_msg = msg_factory.client_p2p_message(server.clientID, receiver_id, message);
        server.send(outgoing_msg);
    };

    context.JK.JamServer = server;

    // Message callbacks
    server.registerMessageCallback(context.JK.MessageType.LOGIN_ACK, function(header, payload) {
        server.signedIn = true;
        logger.debug("Handling LOGIN_ACK. Updating client id to " + payload.client_id);
        server.clientID = payload.client_id;
        server.publicIP = payload.public_ip;
        server.connected = true;

        if (context.jamClient !== undefined)
        {
            logger.debug("... (handling LOGIN_ACK) Updating backend client, connected to true and clientID to " +
                         payload.client_id);
            context.jamClient.connected = true;
            context.jamClient.clientID = server.clientID;
        }
    });

    server.registerMessageCallback(context.JK.MessageType.PEER_MESSAGE, function(header, payload) {
        if (context.jamClient !== undefined)
        {
            context.jamClient.P2PMessageReceived(header.from, payload.message);
        }
    });


    // Callbacks from jamClient
    if (context.jamClient !== undefined)
    {
        context.jamClient.SendP2PMessage.connect(server.sendP2PMessage);
    }


})(window, jQuery);