/*
 *
 * Evennia Webclient GUI component
 *
 * This is used in conjunction with the main evennia.js library, which
 * handles all the communication with the Server.
 *
 * The job of this code is to coordinate between listeners subscribed to
 * evennia messages and any registered plugins that want to process those
 * messages and send data back to Evennia
 *
 * This is done via Evennia.emitter.on(cmdname, listener) and calling
 * each plugin's init() function to give each plugin a chance to register
 * input handlers or other events on startup.
 *
 * Once a plugin has determined it wants to send a message back to the
 * server, it generates an onSend() function event which allows all
 * other plugins a chance to modify the event and then uses
 * Evennia.msg(cmdname, args, kwargs, [callback]) to finally send the data.
 *
 */

//
// Global Plugins system
//

var options = {}; // Global "settings" object that all plugins can use to
// save/pass data to each other and the server.
// format should match:
//  { 'plugin_name': { 'option_key': value, ... }, ... }

var plugins = {}; // Global plugin objects by name.
// Each must have an init() function.

//
// Global plugin_handler
//
var plugin_handler = (function () {
    "use strict"

    var ordered_plugins = new Array; // plugins in <html> loaded order

    //
    // Plugin Support Functions
    //

    // Add a new plugin
    var add = function (name, plugin) {
        plugins[name] = plugin;
        ordered_plugins.push(plugin);
    }


    //
    // GUI Event Handlers
    //

    // catch all keyboard input, handle special chars
    var onKeydown = function (event) {
        // cycle through each plugin's keydown
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            // does this plugin handle keydown events?
            if ('onKeydown' in plugin) {
                // yes, does this plugin claim this event exclusively?
                if (plugin.onKeydown(event)) {
                    // 'true' claims this event has been handled
                    return;
                }
            }
        }
        console.log('NO plugin handled this Keydown');
    }


    // Ask if user really wants to exit session when closing
    // the tab or reloading the page. Note: the message is not shown
    // in Firefox, there it's a standard error.
    var onBeforeUnload = function () {
        // cycle through each plugin to look for unload handlers
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('onBeforeUnload' in plugin) {
                plugin.onBeforeUnload();
            }
        }
    }


    //
    // Evennia Public Event Handlers
    //

    // Handle onLoggedIn from the server
    var onLoggedIn = function (args, kwargs) {
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('onLoggedIn' in plugin) {
                plugin.onLoggedIn(args, kwargs);
            }
        }
    }


    // Handle onGotOptions from the server
    var onGotOptions = function (args, kwargs) {
        // does any plugin handle Options?
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('onGotOptions' in plugin) {
                plugin.onGotOptions(args, kwargs);
            }
        }
    }


    // Handle text coming from the server
    var onText = function (args, kwargs) {
        // does this plugin handle this onText event?
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('onText' in plugin) {
                if (plugin.onText(args, kwargs)) {
                    // True  -- means this plugin claims this Text exclusively.
                    return;
                }
            }
        }
        console.log('NO plugin handled this Text');
    }


    // Handle prompt output from the server
    var onPrompt = function (args, kwargs) {
        // does this plugin handle this onPrompt event?
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('onPrompt' in plugin) {
                if (plugin.onPrompt(args, kwargs)) {
                    // True -- means this plugin claims this Prompt exclusively.
                    return;
                }
            }
        }
        console.log('NO plugin handled this Prompt');
    }


    // Handle unrecognized commands from server
    var onDefault = function (cmdname, args, kwargs) {
        // does this plugin handle this UnknownCmd?
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('onUnknownCmd' in plugin) {
                if (plugin.onUnknownCmd(cmdname, args, kwargs)) {
                    // True -- means this plugin claims this UnknownCmd exclusively.
                    return;
                }
            }
        }
        console.log('NO plugin handled this Unknown Evennia Command');
    }


    // Handle the server connection closing
    var onConnectionClose = function (args, kwargs) {
        // give every plugin a chance to do stuff onConnectionClose
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('onConnectionClose' in plugin) {
                plugin.onConnectionClose(args, kwargs);
            }
        }

        onText(["The connection was closed or lost."], { 'cls': 'err' });
    }


    // Silences events we don't do anything with.
    var onSilence = function (cmdname, args, kwargs) { }


    //
    // Global onSend() function to iterate through all plugins before sending text to the server.
    // This can be called by other plugins for "Triggers", <enter>, and other automated sends
    //
    var onSend = function (line) {
        if (!Evennia.isConnected()) {
            var reconnect = confirm("Not currently connected. Reconnect?");
            if (reconnect) {
                onText(["Attempting to reconnnect..."], { cls: "sys" });
                Evennia.connect();
            }
            // Don't try to send anything until the connection is back.
            return;
        }

        // default output command
        var cmd = {
            command: "text",
            args: [line],
            kwargs: {}
        };

        // Give each plugin a chance to use/modify the outgoing command for aliases/history/etc
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('onSend' in plugin) {
                var outCmd = plugin.onSend(line);
                if (outCmd) {
                    cmd = outCmd;
                }
            }
        }

        // console.log('sending: ' + cmd.command + ', [' + cmd.args[0].toString() + '], ' + cmd.kwargs.toString() );
        Evennia.msg(cmd.command, cmd.args, cmd.kwargs);
    }


    //
    // call each plugins' init function (the only required function)
    //
    var init = function () {
        for (let n = 0; n < ordered_plugins.length; n++) {
            ordered_plugins[n].init();
        }
    }


    //
    // normally init() is all that is needed, but some cases may require a second
    // pass to avoid chicken/egg dependencies between two plugins.
    var postInit = function () {
        // does this plugin need postInit() to be called?
        for (let n = 0; n < ordered_plugins.length; n++) {
            let plugin = ordered_plugins[n];
            if ('postInit' in plugin) {
                plugin.postInit();
            }
        }
    }


    return {
        add: add,
        onKeydown: onKeydown,
        onBeforeUnload: onBeforeUnload,
        onLoggedIn: onLoggedIn,
        onText: onText,
        onGotOptions: onGotOptions,
        onPrompt: onPrompt,
        onDefault: onDefault,
        onSilence: onSilence,
        onConnectionClose: onConnectionClose,
        onSend: onSend,
        init: init,
        postInit: postInit,
    }
})();


//
// Webclient Initialization
//

// Event when client finishes loading
$(document).ready(function () {
    // This is safe to call, it will always only
    // initialize once.
    Evennia.init();

    // register listeners
    Evennia.emitter.on("logged_in", plugin_handler.onLoggedIn);
    Evennia.emitter.on("text", plugin_handler.onText);
    Evennia.emitter.on("webclient_options", plugin_handler.onGotOptions);
    Evennia.emitter.on("prompt", plugin_handler.onPrompt);
    Evennia.emitter.on("default", plugin_handler.onDefault);
    Evennia.emitter.on("connection_close", plugin_handler.onConnectionClose);

    // silence currently unused events
    Evennia.emitter.on("connection_open", plugin_handler.onSilence);
    Evennia.emitter.on("connection_error", plugin_handler.onSilence);

    // Event when closing window (have to have Evennia initialized)
    $(window).bind("beforeunload", plugin_handler.onBeforeUnload);

    // Event when any key is pressed
    $(document).keydown(plugin_handler.onKeydown)

    // set an idle timer to send idle every 3 minutes,
    // to avoid proxy servers timing out on us
    setInterval(function () { // Connect to server
        Evennia.msg("text", ["idle"], {});
    },
        60000 * 3
    );

    // Initialize all plugins
    plugin_handler.init();

    // Finish Initializing any plugins that need a second stage
    plugin_handler.postInit();

    console.log("Completed Webclient setup");
});
