define(function(require, exports, module) {
    main.consumes = [
        "Plugin", "ui", "ace", "tabbehavior", "menus", "tabManager"
    ];
    main.provides = ["ace.split"];
    return main;
    
    function main(options, imports, register) {
        var Plugin = imports.Plugin;
        var ui = imports.ui;
        var ace = imports.ace;
        
        var event = require("ace/lib/event");
        var Editor = require("ace/editor").Editor;
        var Renderer = require("ace/virtual_renderer").VirtualRenderer;
        
        /***** Initialization *****/
        
        var plugin = new Plugin("Ajax.org", main.consumes);
        var emit = plugin.getEmitter();
        
        var editors = [], splits = {};
        
        var loaded = false;
        function load() {
            if (loaded) return false;
            loaded = true;
            
            ace.on("create", function(e) {
                if (e.editor.type != "ace")
                    return;
                
                draw();
                
                var editor = e.editor;
                var grabber;
                
                editor.once("draw", function() {
                    grabber = createGrabber(editor);
                }, plugin);
                
                editor.on("documentActivate", function(e) {
                    var doc = e.doc;
                    var session = doc.getSession();
                    var split = session.split;
                    var splitInfo = splits[doc.editor.name];
                    
                    // If we are not in split mode and the editor is not split
                    // lets do nothing.
                    if (!split && !splitInfo)
                        return;
                    
                    if (split) {
                        // Make sure we have a split inited for this editor
                        if (!splitInfo)
                            splitInfo = initSplit(editor, split.height);
                            
                        var editor2 = splitInfo.editor2;
                        
                        // Set Session
                        editor2.setSession(split.session2);
                        
                        // Set Height
                        splitInfo.topPane.setHeight(split.height);
                        
                        // Show bottom pane
                        splitInfo.topPane.show();
                        
                        // Hide Grabber
                        grabber.style.display = "none";
                    }
                    else {
                        // Hide bottom pane
                        splitInfo.topPane.hide();
                        
                        // Show Grabber
                        grabber.style.display = "block";
                    }
                });
                
                editor.on("getState", function(e) {
                    var session = e.doc.getSession();
                    var state = e.state;
                    
                    if (e.filter || !session.split) 
                        return;
                    
                    var session2 = session.split.session2;
                    
                    state.split = {
                        height: session.split.height,
                        
                        // Scroll state
                        scrolltop: session2.getScrollTop(),
                        scrollleft: session2.getScrollLeft(),
                        
                        // Selection
                        selection: session2.selection.toJSON()
                    };
                });
                
                editor.on("setState", function(e) {
                    var state = e.state.split;
                    var session = e.doc.getSession();
                    
                    if (!state)
                        return;
                    
                    var splitInfo = initSplit(editor, state.height);
                    var session2 = ace.cloneSession(session.session);
                    
                    session.split = {
                        height: state.height,
                        session2: session2
                    };
                    
                    // Set 2nd Session
                    splitInfo.editor2.setSession(session2);
                    
                    // Set selection
                    if (state.selection)
                        session2.selection.fromJSON(state.selection);
                    
                    // Set scroll state
                    if (state.scrolltop)
                        session2.setScrollTop(state.scrolltop);
                    if (state.scrollleft)
                        session2.setScrollLeft(state.scrollleft);
                    
                    if (state.options)
                        session2.setOptions(state.options);
                    
                    var grabber = editor.aml.$int.querySelector(".splitgrabber");
                    grabber.style.display = "none";
                });
                
                editor.on("resize", function(e) {
                    var splitInfo = splits[editor.name];
                    if (!splitInfo || !splitInfo.topPane.visible) return;
                    splitInfo.editor2.resize(true); // @Harutyun
                });
                
                editor.on("unload", function(e) {
                    delete splits[editor.name];
                });
            });
        }
        
        var drawn = false;
        function draw() {
            if (drawn) return;
            drawn = true;
            
            // Insert CSS
            ui.insertCss(require("text!./style.css"), plugin);
        
            emit("draw");
        }
        
        function createGrabber(editor) {
            var htmlNode = editor.ace.container.parentNode;
            var grabber = document.createElement("div");
            grabber.className = "splitgrabber";
            grabber.textContent = "=";
            htmlNode.appendChild(grabber);
            
            grabber.addEventListener("mousedown", function(e) {
                startSplit(e, grabber, editor);
            });
            
            plugin.addOther(function() {
                grabber.parentNode.removeChild(grabber);
            });
            
            return grabber;
        }
        
        /***** Methods *****/
        
        function startSplit(e, grabber, editor) {
            var container = grabber;
            var drag = grabber;
            
            // Set Top
            drag.style.zIndex = 1000000;
            
            var offsetY = e.clientY - (parseInt(container.style.top, 10) || 0);
            var moved = false;
            var startY = e.clientY - offsetY;
            var offset = e.offsetY;
            
            var session = editor.activeDocument.getSession();
            
            event.capture(container, function(e) {
                var y = e.clientY - offsetY;
                
                if (!moved) {
                    if (Math.abs(y - startY) > 3) {
                        moved = true;
                        var percentage = ((y - startY) / grabber.parentNode.offsetHeight) * 100;
                        session.split = {
                            height: percentage + "%",
                            session2: ace.cloneSession(session.session)
                        };
                        var splitInfo = initSplit(editor, percentage);
                        
                        // Set 2nd Session
                        splitInfo.editor2.setSession(session.split.session2);
                        
                        // Start splitter
                        splitInfo.splitbox.$handle.$ext.onmousedown({ 
                            clientY: e.clientY, 
                            offsetY: -7 + offset
                        });
                        
                        // Hide Grabber
                        grabber.style.display = "none";
                    }
                    else return;
                }
            }, function() {
                if (moved)
                    setFinalState(editor, session);
            });
            
            event.stopEvent(e);
        }
        
        function initSplit(editor, percentage) {
            if (splits[editor.name]) {
                var splitInfo = splits[editor.name];
                splitInfo.topPane.show();
                return splitInfo;
            }
            
            var container = editor.aml.$int;
            var amlNode = container.host;
            // @todo detect if this already happened
            
            var splitbox = amlNode.appendChild(new ui.vsplitbox({ 
                "class": "ace_split",
                padding: 7,
                edge: "7 0 0 0",
                splitter: true 
            }));
            
            var topPane = splitbox.appendChild(new ui.bar({ 
                height: percentage + "%" 
            }));
            var bottomPane = splitbox.appendChild(new ui.bar());
            
            // Original Editor
            bottomPane.$int.appendChild(editor.ace.container);
            editor.ace.container.style.top = "0px";
            
            // New Editor
            var editor2 = new Editor(new Renderer(topPane.$int, ace.theme));
            editors.push(editor2);
            
            splitbox.$handle.on("dragmove", function() {
                editor.resize();
                editor2.resize();
            });
            splitbox.$handle.on("dragdrop", function() {
                editor.resize();
                editor2.resize();
                
                var session = editor.activeDocument.getSession();
                setFinalState(editor, session);
            });
            ace.on("settingsUpdate", function(e) {
                editor2.setOptions(e.options);
            }, editor);
            
            function setTheme() {
                var theme = ace.theme;
                editor2.setTheme(theme.path);
                
                var node = splitbox.$ext.parentNode;
                if (theme.isDark)
                    ui.setStyleClass(node, "dark");
                else
                    ui.setStyleClass(node, "", ["dark"]);
            }
            ace.on("themeChange", setTheme, editor);
            setTheme();
            
            var lastFocused = editor2;
            editor2.on("focus", function() {
                lastFocused = editor2;
            });
            
            editor.ace.on("focus", function() {
                lastFocused = null;
            });
            
            editor.on("getAce", function() {
                if (lastFocused)
                    return editor2;
            });
            
            editor.addEditor(editor2);
            
            splits[editor.name] = {
                splitbox: splitbox,
                topPane: topPane,
                bottomPane: bottomPane,
                editor: editor,
                editor2: editor2
            };
            
            return splits[editor.name];
        }
        
        function setFinalState(editor, session) {
            var splitInfo = splits[editor.name];
            var pixelHeight = splitInfo.topPane.getHeight();
            
            var grabber = editor.aml.$int.querySelector(".splitgrabber");
            
            if (pixelHeight < 3) {
                // Remove the split
                splitInfo.topPane.hide();
                delete session.split;
                
                // Show Grabber
                grabber.style.display = "block";
            }
            else {
                // Record the height
                session.split.height = splitInfo.topPane.height;
                
                // Hide Grabber
                grabber.style.display = "none";
            }
        }
        
        /***** Lifecycle *****/
        
        plugin.on("load", function() {
            load();
        });
        plugin.on("enable", function() {
            
        });
        plugin.on("disable", function() {
            
        });
        plugin.on("unload", function() {
            loaded = false;
            drawn = false;
        });
        
        /***** Register and define API *****/
        
        /**
         * 
         **/
        plugin.freezePublicAPI({
            _events: [
                /**
                 * @event draw
                 */
                "draw"
            ]
        });
        
        register(null, {
            "ace.split": plugin
        });
    }
});