/**
 * @ignore
 * undo,redo manager for kissy editor
 * @author yiminghe@gmail.com
 */
KISSY.add("editor/plugin/undo/cmd", function (S, Editor) {
    var UA = S.UA,
        LIMIT = 30;

    /**
     * current editor status(including html and cursor position)
     * @param editor
     * @class KISSY.Editor.Undo.Snapshot
     * @private
     */
    function Snapshot(editor) {
        var contents = editor.get("document")[0].body.innerHTML,
            self = this,
            selection;
        if (contents) {
            selection = editor.getSelection();
        }
        //内容html
        self.contents = contents;
        //选择区域书签标志
        self.bookmarks = selection && selection.createBookmarks2(true);
    }

    S.augment(Snapshot, {
        equals: function (otherImage) {
            var self = this,
                thisContents = self.contents,
                otherContents = otherImage.contents;
            // 不比较书签!
            // source mode -> wysiwyg mode 光标不保持
            return thisContents == otherContents;
        }
    });

    /**
     * manager history of editor content
     * @param editor
     * @class KISSY.Editor.UndoManager
     * @private
     */
    function UndoManager(editor) {
        // redo undo history stack
        /**
         * 编辑器状态历史保存
         */
        var self = this;
        self.history = [];
        //当前所处状态对应的历史栈内下标
        self.index = -1;
        self.editor = editor;
        //键盘输入做延迟处理
        self.bufferRunner = S.buffer(self.save, 500, self);
        self._init();
    }

    var //editingKeyCodes = { /*Backspace*/ 8:1, /*Delete*/ 46:1 },
        modifierKeyCodes = { /*Shift*/ 16: 1, /*Ctrl*/ 17: 1, /*Alt*/ 18: 1 },
    // Arrows: L, T, R, B
        navigationKeyCodes = { 37: 1, 38: 1, 39: 1, 40: 1, 33: 1, 34: 1 },
        zKeyCode = 90,
        yKeyCode = 89;

    S.augment(UndoManager, {
        _keyMonitor: function () {
            var self = this,
                editor = self.editor;

            editor.docReady(function () {
                editor.get("document").on("keydown", function (ev) {
                    var keyCode = ev.keyCode;
                    if (keyCode in navigationKeyCodes
                        || keyCode in modifierKeyCodes) {
                        return;
                    }
                    // ctrl+z,撤销
                    if (keyCode === zKeyCode && (ev.ctrlKey || ev.metaKey)) {
                        if (false !== editor.fire("beforeRedo")) {
                            self.restore(-1);
                        }
                        ev.halt();
                        return;
                    }
                    // ctrl+y,重做
                    if (keyCode === yKeyCode && (ev.ctrlKey || ev.metaKey)) {
                        if (false !== editor.fire("beforeUndo")) {
                            self.restore(1);
                        }
                        ev.halt();
                        return;
                    }
                    if (editor.fire("beforeSave", {buffer: 1}) !== false) {
                        self.save(1);
                    }
                });
            });
        },

        _init: function () {
            var self = this,
                editor = self.editor;
            self._keyMonitor();
            setTimeout(function () {
                // 只初始化保存一次,切换模式不保存
                if (editor.get('mode') == Editor.Mode.WYSIWYG_MODE) {
                    if (editor.isDocReady()) {
                        self.save();
                    } else {
                        editor.on('docReady', function () {
                            self.save();
                            editor.detach('docReady', arguments.callee);
                        });
                    }
                }
            }, 0);
        },

        /**
         * save to history
         */
        save: function (buffer) {

            var editor = this.editor;

            // 代码模式下不和可视模式下混在一起
            if (editor.get("mode") != Editor.Mode.WYSIWYG_MODE) {
                return;
            }

            if (!editor.get("document")) {
                return;
            }

            if (buffer) {
                this.bufferRunner();
                return;
            }

            var self = this,
                history = self.history,
                l = history.length,
                index = self.index;

            //前面的历史抛弃
            l = Math.min(l, index + 1);

            var last = history[l - 1],
                current = new Snapshot(editor);

            if (!last || !last.equals(current)) {
                history.length = l;
                if (l === LIMIT) {
                    history.shift();
                    l--;
                }
                history.push(current);
                self.index = index = l;
                editor.fire("afterSave", {history: history, index: index});
            }
        },

        /**
         * restore from history
         */
        restore: function (d) {

            // 代码模式下不和可视模式下混在一起
            if (this.editor.get("mode") != Editor.Mode.WYSIWYG_MODE) {
                return undefined;
            }

            var self = this,
                history = self.history,
                editor = self.editor,
                editorDomBody = editor.get("document")[0].body,
                snapshot = history[self.index + d];

            if (snapshot) {
                editorDomBody.innerHTML = snapshot.contents;
                if (snapshot.bookmarks) {
                    editor.getSelection().selectBookmarks(snapshot.bookmarks);
                } else if (UA['ie']) {
                    // IE BUG: If I don't set the selection to *somewhere* after setting
                    // document contents, then IE would create an empty paragraph at the bottom
                    // the next time the document is modified.
                    var $range = editorDomBody.createTextRange();
                    $range.collapse(true);
                    $range.select();
                }
                var selection = editor.getSelection();
                // 将当前光标,选择区域滚动到可视区域
                if (selection) {
                    selection.scrollIntoView();
                }
                self.index += d;
                editor.fire(d < 0 ? "afterUndo" : "afterRedo", {
                    history: history,
                    index: self.index
                });
                editor.notifySelectionChange();
            }

            return snapshot;
        }
    });

    return {
        init: function (editor) {
            if (!editor.hasCommand("save")) {
                var undoRedo = new UndoManager(editor);
                editor.addCommand("save", {
                    exec: function (_, buffer) {
                        editor.focus();
                        undoRedo.save(buffer);
                    }
                });
                editor.addCommand("undo", {
                    exec: function () {
                        editor.focus();
                        undoRedo.restore(-1);
                    }
                });
                editor.addCommand("redo", {
                    exec: function () {
                        editor.focus();
                        undoRedo.restore(1);
                    }
                });
            }
        }
    };
}, {
    requires: ['editor']
});