define(function(require, module, exports) {
    main.consumes = [
        "Plugin", "menus", "ui", "settings"
    ];
    main.provides = ["editors"];
    return main;

    function main(options, imports, register) {
        var ui = imports.ui;
        var Plugin = imports.Plugin;
        var menus = imports.menus;
        var settings = imports.settings;
        
        var extname = require("path").extname;
        var basename = require("path").basename;
        
        /***** Initialization *****/
        
        var plugin = new Plugin("Ajax.org", main.consumes);
        var emit = plugin.getEmitter();
        
        var fileExtensions = {};
        var editors = {};
        var defaultEditor;
        var group;
        
        var loaded = false;
        function load() {
            if (loaded) return false;
            loaded = true;
            
            plugin.addElement(group = new ui.group());
            
            menus.addItemByPath("View/Editors/", new ui.menu({
                "onprop.visible": function(e) {
                    if (!e.value)
                        return;
                    
                    emit("menuShow", { menu: this });
                }
            }), 100, plugin);
            menus.addItemByPath("View/~", new ui.divider(), 200, plugin);
        }
        
        function findEditor(type, editor) {
            if (type)
                editor = editors[type];
            
            // if (!editor) {
            //     util.alert(
            //         "No editor is registered",
            //         "Could not find an editor to display content!",
            //         "There is something wrong with the configuration of \
            //          your IDE. No editor plugin is found.");
            //     return;
            // }
            
            return editor;
        }
        
        function editorSupportsFile(type, path) {
            var extensions = findEditor(type).fileExtensions;
            var ext = extname(path).substr(1).toLowerCase();
            var filename = basename(path).toLowerCase();
            return ~extensions.indexOf(path)
                || ~extensions.indexOf(filename)
                || ~extensions.indexOf(ext) ? true : false;
        }
        
        function findEditorByFilename(fn) {
            var ext = extname(fn).substr(1).toLowerCase();
            var filename = basename(fn).toLowerCase();
            
            // Check custom user settings first for preferred editor
            var customEditor = settings.get("user/tabs/editorTypes/@" + ext);
            if (customEditor) return customEditor;
            
            var editor = fileExtensions[fn] && fileExtensions[fn][0]
                || fileExtensions[filename] && fileExtensions[filename][0]
                || fileExtensions[ext] && fileExtensions[ext][0]
                || defaultEditor;
            
            return findEditor(null, editor);
        }
        
        /***** Methods *****/
        
        function registerPlugin(type, caption, editor, extensions) {
            editors[type] = editor;
            
            var handle = new Plugin("Ajax.org", []);
            handle[type] = editor;
            handle[caption] = caption;
            
            editor.type = type;
            editor.fileExtensions = extensions;
            
            if (extensions.length) {
                //Add a menu item to the list of editors
                menus.addItemByPath("View/Editors/" + caption,
                    new ui.item({
                        type: "radio",
                        value: type,
                        group: group,
                        onclick: function() {
                            emit("menuClick", { value: this.value });
                        }
                    }), 40000, handle);
            }
    
            extensions.forEach(function(ext) {
                // force lower-case, to account for other LowerCase checks below
                ext = ext.toLowerCase();
                (fileExtensions[ext] || (fileExtensions[ext] = [])).unshift(editor);
            });
    
            if (editor.type == options.defaultEditor)
                defaultEditor = editor;
            
            emit("register", { editor: editor });
            
            handle.on("load", function() {
                if (!editors[type])
                    registerPlugin(type, caption, editor, extensions);
            });
            handle.on("unload", function() {
                unregisterPlugin(type, editor, extensions);
            });
            return handle;
        }
        
        function unregisterPlugin(type, editor, extensions) {
            delete editors[type];
            
            extensions.forEach(function(fe) {
                if (!fileExtensions[fe]) return;
                
                var idx = fileExtensions[fe].indexOf(editor);
                fileExtensions[fe].splice(idx, 1);
                if (!fileExtensions[fe].length)
                    delete fileExtensions[fe];
            });
    
            if (defaultEditor == editor)
                defaultEditor = null;
            
            emit("unregister", { editor: editor });
        }
        
        function createEditor(type, callback) {
            if (!type) type = "default";
            
            var cancelled = false;
            
            function create() {
                if (cancelled) return;
                var constr = editors[type];
                var editor = constr ? new constr() : false;
                editor.type = type;
                callback(null, editor);
                
                emit("create", { editor: editor });
            }
            
            if (editors[type]) create();
            else {
                plugin.on("register", function wait(e) {
                    if (e.editor.type == type) {
                        plugin.off("register", wait);
                        
                        // Delay creation to make sure handle is created
                        setTimeout(create);
                    }
                });
                return function cancel() { cancelled = true; };
            }
        }
        
        /***** Lifecycle *****/
        
        plugin.on("load", function() {
            load();
        });
        plugin.on("enable", function() {
            
        });
        plugin.on("disable", function() {
            
        });
        plugin.on("unload", function() {
            loaded = false;
            defaultEditor = null;
            group = null;
        });
        
        /***** Register and define API *****/

        /**
         * Editor Manager for Cloud9. Editors are automatically registered
         * here when using the {@link Editor} base class. Use this class to
         * instantiate an editor instance for use outside of the normal tabs.
         * If you want to open a tab with an editor to edit file content use
         * {@link tabManager#method-open}.
         * 
         * @singleton
         **/
        plugin.freezePublicAPI({
            /**
             * The editor that is used when no editor is registered for the 
             * file extension in question.
             * 
             * See {@link editors#findEditorByFilename}
             * See {@link tabManager#method-open}
             * 
             * @property {String} defaultEditor
             */
            get defaultEditor() { return options.defaultEditor; },
            
            /**
             * Array of file extensions supported by the registered editors
             * @property {String[]} fileExtensions  
             */
            get fileExtensions() { return fileExtensions; },
            
            _events: [
                /**
                 * @event register Fires when an editor registers
                 * @param {Object} e
                 * @param {Editor} e.editor  The editor that is registered
                 * @readonly
                 */
                "register",
                /**
                 * @event unregister Fires when an editor unregisters
                 * @param {Object} e
                 * @param {Editor} e.editor  The editor that is unregistered
                 * @readonly
                 */
                "unregister"
            ],
            
            /**
             * Registers an editor constructor to be available for certain
             * file types
             * @param {Function} editor the editor constructor
             * @param {String[]} fileExtensions the file extensions this editor supports
             */
            register: registerPlugin,
            
            /**
             * Unregisters an editor constructor for certain file types
             * @param {Function} editor the editor constructor
             */
            unregister: unregisterPlugin,
            
            /**
             * Retrieves an editor constructor class based on it's type
             * @param {String} type  The type of the editor to retrieve
             * @return {Function}
             */
            findEditor: findEditor,
            
            /**
             * Retrieves an editor constructor class based on a filename (or path)
             * @param {String} filename the filename that determines the editor to find
             * @return {Function}
             */
            findEditorByFilename: findEditorByFilename,
            
            /**
             * 
             */
            editorSupportsFile: editorSupportsFile,
            
            /**
             * Create an editor instance based on it's type
             * @param {String} type  The type of the editor to create
             * @return {Editor}
             */
            createEditor: createEditor
        });
        
        register(null, {
            editors: plugin
        });
    }
});
