/*global define, _ */
define(function(require, exports, module) {
    var eventbus = require("./lib/eventbus");
    var config = require("./config");
    var command = require("./command");
    var InlineAnnotation = require("./lib/inline_annotation");
    var editor = require("./editor");

    /**
     * This parts handles mode handlers, e.g. "change", "preview" etc.
     */

    var handlerFns = {};

    function runSessionHandler(session, handlerName, debounceTimeout, callback) {
        var path = session.filename;
        var mode = session.mode;
        var commandNames = [];

        var editors = editor.getEditors();
        var edit = null;
        
        _.each(editors, function(edit_) {
            if (edit_.getSession() === session) {
                edit = edit_;
            }
        });

        if (!edit) {
            // Session is not currently visible, won't run commands
            return;
        }

        if (mode && mode.handlers[handlerName]) {
            commandNames = commandNames.concat(mode.handlers[handlerName]);
        }
        if (config.getHandlers()[handlerName]) {
            commandNames = commandNames.concat(config.getHandlers()[handlerName]);
        }

        function runCommands() {
            var waitingFor = commandNames.length;
            var results = [];
            var edit = editor.getActiveEditor();
            commandNames.forEach(function(commandName) {
                command.exec(commandName, edit, session, function(err, results_) {
                    if (err) {
                        return callback(err);
                    }
                    results = results.concat(results_);
                    waitingFor--;
                    if (waitingFor === 0) {
                        _.isFunction(callback) && callback(null, results);
                    }
                });
            });
            if (waitingFor === 0) {
                _.isFunction(callback) && callback(null, results);
            }
        }

        if (commandNames.length > 0) {
            if (debounceTimeout) {
                var id = path + ':' + handlerName;
                if (!handlerFns[id]) {
                    handlerFns[id] = _.debounce(runCommands, debounceTimeout);
                }
                handlerFns[id]();
            } else {
                runCommands();
            }
            return true;
        } else {
            _.isFunction(callback) && callback();
            return false;
        }
    }
    
    function runHandler(handlerName, callback) {
        var commandNames = [];
        if (config.getHandlers()[handlerName]) {
            commandNames = commandNames.concat(config.getHandlers()[handlerName]);
        }
        
        var waitingFor = commandNames.length;
        var results = [];
        var edit = editor.getActiveEditor();
        var session = edit.getSession();
        commandNames.forEach(function(commandName) {
            command.exec(commandName, edit, session, function(err, results_) {
                if (err) {
                    return callback(err);
                }
                results = results.concat(results_);
                waitingFor--;
                if (waitingFor === 0) {
                    _.isFunction(callback) && callback(null, results);
                }
            });
        });
        if (waitingFor === 0) {
            _.isFunction(callback) && callback(null, results);
        }
    }

    exports.runSessionHandler = runSessionHandler;

    function setAnnotations(session, annos) {
        annos = annos || [];
        (session.annotations || []).forEach(function(anno) {
            anno.remove();
        });
        session.annotations = [];
        for (var i = 0; i < annos.length; i++) {
            var anno = annos[i];
            // If no endColum, no inline marker is required
            if (anno.endColumn) {
                session.annotations.push(new InlineAnnotation(session, anno));
            }
        }
        session.setAnnotations(annos);
    }

    exports.hook = function() {
        eventbus.once("inited", function() {
            runHandler("init");
        });
        eventbus.on("configchanged", function() {
            runHandler("configchanged");
        });
        eventbus.on("sessionchanged", function(session) {
            analyze(session);
        });
        eventbus.on("modeset", function(session) {
            analyze(session);
        });
        eventbus.on("switchsession", function(edit, session) {
            analyze(session, true);
        });

        function analyze(session, instant) {
            runSessionHandler(session, "change");
            runSessionHandler(session, "check", instant ? null : 1000, function(err, annos) {
                setAnnotations(session, annos);
            });
        }

        eventbus.on("preview", function(session) {
            var didPreview = runSessionHandler(session, "preview");
            if (!didPreview) {
                require(["./preview"], function(preview) {
                    preview.showPreview("Not supported.");
                    eventbus.emit("sessionactivityfailed", session, "No preview available");
                });
            }
        });
    };
});
@@ -651,25 +651,17 @@
 = null;%0A
-        
 %0A
+
         
@@ -2491,21 +2491,17 @@
 %7D%0A    %7D%0A
-    
 %0A
+
     func
@@ -2718,17 +2718,9 @@
   %7D%0A
-        
 %0A
+
     
@@ -4245,32 +4245,156 @@
 %22);%0A        %7D);%0A
+        eventbus.on(%22sessionbeforesave%22, function(session) %7B%0A            runSessionHandler(session, %22presave%22);%0A        %7D);%0A
         eventbus
@@ -5360,11 +5360,12 @@
 %0A    %7D;%0A
-
 %7D);
+%0A

Create a presave handler for the sandbox.