/*/* ===========================================================
 * trumbowyg.history.js v1.0
 * history plugin for Trumbowyg
 * http://alex-d.github.com/Trumbowyg
 * ===========================================================
 * Author : Sven Dunemann [dunemann@forelabs.eu]
 */

(function ($) {
    'use strict';
    $.extend(true, $.trumbowyg, {
        langs: {
            // jshint camelcase:false
            de: {
                history: {
                    redo: 'Wiederholen',
                    undo: 'Rückgängig'
                }
            },
            en: {
                history: {
                    redo: 'Redo',
                    undo: 'Undo'
                }
            },
            da: {
                history: {
                    redo: 'Annuller fortryd',
                    undo: 'Fortryd'
                }
            },
            fr: {
                history: {
                    redo: 'Annuler',
                    undo: 'Rétablir'
                }
            },
            zh_tw: {
               history: {
                   redo: '重做',
                   undo: '復原'
               }
            },
            pt_br: {
                history: {
                    redo: 'Refazer',
                    undo: 'Desfazer'
                }
            },
            ko: {
                history: {
                    redo: '다시 실행',
                    undo: '되돌리기'
                }
            },
            // jshint camelcase:true
        },
        plugins: {
            history: {
                init: function (t) {
                    t.o.plugins.history = $.extend(true, {
                        _stack: [],
                        _index: -1,
                        _focusEl: undefined
                    }, t.o.plugins.history || {});

                    var btnBuildDefRedo = {
                        title: t.lang.history.redo,
                        ico: 'redo',
                        key: 'Y',
                        fn: function () {
                            if (t.o.plugins.history._index < t.o.plugins.history._stack.length - 1) {
                                t.o.plugins.history._index += 1;
                                var index = t.o.plugins.history._index;
                                var newState = t.o.plugins.history._stack[index];

                                t.execCmd('html', newState);
                                // because of some semantic optimisations we have to save the state back
                                // to history
                                t.o.plugins.history._stack[index] = t.$ed.html();

                                carretToEnd();
                                toggleButtonStates();
                            }
                        }
                    };

                    var btnBuildDefUndo = {
                        title: t.lang.history.undo,
                        ico: 'undo',
                        key: 'Z',
                        fn: function () {
                            if (t.o.plugins.history._index > 0) {
                                t.o.plugins.history._index -= 1;
                                var index = t.o.plugins.history._index,
                                    newState = t.o.plugins.history._stack[index];

                                t.execCmd('html', newState);
                                // because of some semantic optimisations we have to save the state back
                                // to history
                                t.o.plugins.history._stack[index] = t.$ed.html();

                                carretToEnd();
                                toggleButtonStates();
                            }
                        }
                    };

                    var pushToHistory = function () {
                        var index = t.o.plugins.history._index,
                            stack = t.o.plugins.history._stack,
                            latestState = stack.slice(-1)[0] || '<p></p>',
                            prevState = stack[index],
                            newState = t.$ed.html(),
                            focusEl = t.doc.getSelection().focusNode,
                            focusElText = '',
                            latestStateTagsList,
                            newStateTagsList,
                            prevFocusEl = t.o.plugins.history._focusEl;

                        latestStateTagsList = $('<div>' + latestState + '</div>').find('*').map(function () {
                            return this.localName;
                        });
                        newStateTagsList = $('<div>' + newState + '</div>').find('*').map(function () {
                            return this.localName;
                        });
                        if (focusEl) {
                            t.o.plugins.history._focusEl = focusEl;
                            focusElText = focusEl.outerHTML || focusEl.textContent;
                        }

                        if (newState !== prevState) {
                            // a new stack entry is defined when current insert ends on a whitespace character
                            // or count of node elements has been changed
                            // or focused element differs from previous one
                            if (focusElText.slice(-1).match(/\s/) ||
                                !arraysAreIdentical(latestStateTagsList, newStateTagsList) ||
                                t.o.plugins.history._index <= 0 || focusEl !== prevFocusEl)
                            {
                                t.o.plugins.history._index += 1;
                                // remove newer entries in history when something new was added
                                // because timeline was changes with interaction
                                t.o.plugins.history._stack = stack.slice(
                                    0, t.o.plugins.history._index
                                );
                                // now add new state to modified history
                                t.o.plugins.history._stack.push(newState);
                            } else {
                                // modify last stack entry
                                t.o.plugins.history._stack[index] = newState;
                            }

                            toggleButtonStates();
                        }
                    };

                    var toggleButtonStates = function () {
                        var index = t.o.plugins.history._index,
                            stackSize = t.o.plugins.history._stack.length,
                            undoState = (index > 0),
                            redoState = (stackSize !== 0 && index !== stackSize - 1);

                        toggleButtonState('historyUndo', undoState);
                        toggleButtonState('historyRedo', redoState);
                    };

                    var toggleButtonState = function (btn, enable) {
                        var button = t.$box.find('.trumbowyg-' + btn + '-button');

                        if (enable) {
                            button.removeClass('trumbowyg-disable');
                        } else if (!button.hasClass('trumbowyg-disable')) {
                            button.addClass('trumbowyg-disable');
                        }
                    };

                    var arraysAreIdentical = function (a, b) {
                        if (a === b) {
                            return true;
                        }
                        if (a == null || b == null) {
                            return false;
                        }
                        if (a.length !== b.length) {
                            return false;
                        }

                        for (var i = 0; i < a.length; i += 1) {
                            if (a[i] !== b[i]) {
                                return false;
                            }
                        }
                        return true;
                    };

                    var carretToEnd = function () {
                        var node = t.doc.getSelection().focusNode,
                            range = t.doc.createRange();

                        if (node.childNodes.length > 0) {
                            range.setStartAfter(node.childNodes[node.childNodes.length - 1]);
                            range.setEndAfter(node.childNodes[node.childNodes.length - 1]);
                            t.doc.getSelection().removeAllRanges();
                            t.doc.getSelection().addRange(range);
                        }
                    };

                    t.$c.on('tbwinit tbwchange', pushToHistory);

                    t.addBtnDef('historyRedo', btnBuildDefRedo);
                    t.addBtnDef('historyUndo', btnBuildDefUndo);
                }
            }
        }
    });
})(jQuery);
