(function($){
    "use strict";

    $.pixeEditHistory = function(item, settings){

        settings = $.extend({
            debounceTime: 800,
            updateSettingsPanel: true,
            onHistoryAdded: function(item, subtype, value){
                if( settings.updateSettingsPanel ){
                    $.pixeEditor.updateSettingsPanel(item, subtype, value);
                }
            },
        }, settings);

        var t = this;

        // item data
        if( !settings.type ){
            t.item = item;
            t.subtype = undefined;
            t.oldValue = $.pixeClone(item.data('settings'));
            t.settings = settings;

        // object data
        }else if( settings.type == 'object' ){
            t.item = item;
            t.subtype = undefined;
            t.oldValue = $.pixeClone(item);
            t.settings = settings;
        }
        
    };
    $.pixeEditHistory.prototype.onChange = function(subtype, displayText = '', args = undefined){
        var t = this;
        
        if( t.subtype != subtype ){
            t.subtype = subtype;

            t.debounceChange = $.pixeDebounce(function(){

                // item action
                if( !t.settings.type ){
                    var newValue = t.item.data('settings')[subtype];
                    if( $.pixeCompare(newValue, t.oldValue[subtype]) ) return; // no change
                    
                    var action = $.pixeHistory.createAction(t.item, 'edit', subtype, displayText);
                    $.pixeHistory.setActionStep(action, t.item, subtype, t.oldValue[subtype], true);
                    $.pixeHistory.setActionStep(action, t.item, subtype, newValue);
                    $.pixeHistory.addAction(action);

                    t.oldValue[subtype] = $.pixeClone(newValue);
                    t.settings.onHistoryAdded(t.item, subtype, newValue);

                // object action
                }else if( t.settings.type == 'object' ){
                    var newValue = t.item[subtype];
                    if( $.pixeCompare(newValue, t.oldValue[subtype]) ) return; // no change

                    var action = $.pixeHistory.createAction(undefined, 'object', subtype, displayText, args);
                    $.pixeHistory.setObjectActionStep(action, {
                        item: t.item, 
                        slug: subtype,
                        value: t.oldValue[subtype]
                    }, true);
                    $.pixeHistory.setObjectActionStep(action, {
                        item: t.item, 
                        slug: subtype,
                        value: newValue
                    });
                    $.pixeHistory.addAction(action);

                    t.oldValue[subtype] = $.pixeClone(newValue);
                    t.settings.onHistoryAdded(t.item, subtype, newValue);
                }
            }, t.settings.debounceTime);
            t.debounceChange();
        }else{
            t.debounceChange();
        }
    };
    $.pixeEditHistory.prototype.onChangeMultiple = function(subtypes, displayText = ''){
        var t = this;
        if( t.settings.type ) return;
        if( !Array.isArray(subtypes) ){ subtypes = [subtypes]; }

        var settings = t.item.data('settings');
        var action = $.pixeHistory.createAction(t.item, 'edit', 'multiple', displayText);
        var changes = [];

        for( var i in subtypes ){
            var subtype = subtypes[i];
            var newValue = settings[subtype];
            if( $.pixeCompare(newValue, t.oldValue[subtype]) ) continue;

            $.pixeHistory.setActionStep(action, t.item, subtype, t.oldValue[subtype], true);
            $.pixeHistory.setActionStep(action, t.item, subtype, newValue);
            changes.push({
                subtype: subtype,
                value: newValue
            });
        }

        if( !changes.length ) return;

        $.pixeHistory.addAction(action);

        for( var j in changes ){
            t.oldValue[changes[j].subtype] = $.pixeClone(changes[j].value);
        }
        t.settings.onHistoryAdded(t.item, 'multiple');
    };

    $.pixeHistory = {
        stack: [],
        maxStack: 50,
        stackPointer: 0,

        // generate action label
        getActionTitle: function(i){
            var t = this;
            var title = '';
            var item = t.stack[i].item;

            // front
            if( t.stack[i].type != 'object' ){
                if( item.hasClass('pixe-block-container') ){
                    title += 'Block - '
                }else if( item.hasClass('pixe-collection') ){
                    title += item.data('settings')._subtype + ' - ';
                }else if( item.hasClass('pixe-wrapper') ){
                    title += 'Container - ';
                }else if( item.hasClass('pixe-column') ){
                    title += 'Column - ';
                }else if( item.hasClass('pixe-item') ){
                    title += pixeData.elements[item.data('settings')._element].title + ' - ';
                }
            }

            // back
            if( t.stack[i].displayText ){
                title += t.stack[i].displayText.replaceAll('-', ' ').replaceAll('|', '-');
            }else{
                switch( t.stack[i].type ){
                    case 'add':         title += 'Added'; break; 
                    case 'remove':      title += 'Removed'; break; 
                    case 'duplicate':   title += 'Duplicated'; break; 
                    case 'move':        title += 'Moved'; break; 
                    case 'edit': 
                    case 'object': 
                        title += t.stack[i].subtype.replaceAll('-', ' ') + ' ' + 'Edited';
                        break;
                }
            }

            return title;
        },

        // data list of history
        getData: function(){
            var t = this;
            
            // get actions
            var actions = [];
            actions.push('Editing Start');
            for( var i in t.stack ){
                actions.push(t.getActionTitle(i));
            }

            // set data
            var data = {
                pointer: t.stackPointer,
                actions: actions
            }

            return data;
        },
        show: function(position){
            var t = this;
            var content = $('<div class="pixe-history-popup-content pixe-box-sizing" ></div>');
            content.append('<div class="pixe-title">History</div>');

            var actions = $('<div class="pixe-history-actions pixe-scrollbar-dark"></div>');
            content.append(actions);

            var action;
            action = $('<div class="pixe-history-action">Editing Start</div>');
            if( t.stackPointer == 0 ){
                action.addClass('pixe-active');
            }
            action.attr('data-pointer', '0');
            actions.prepend(action);

            // user action
            for( var i in t.stack ){
                
                var currentIndex = parseInt(i) + 1;
                var action = $('<div class="pixe-history-action" ></div>');
                action.append(t.getActionTitle(i));
                action.attr('data-pointer', currentIndex);
                if( currentIndex > t.stackPointer ){
                    action.addClass('pixe-inactive');
                }else if( currentIndex == t.stackPointer ){
                    action.addClass('pixe-active');
                }

                actions.prepend(action);
            }

            // history popup
            new $.pixePopup({
                style: 'pixe-history-popup pixe-dark',
                customPosition: position,
                content: content
            });

            // click event
            actions.on('click', '.pixe-history-action', function(){
                var index = $(this).attr('data-pointer');

                // undo until target
                if( t.stackPointer > index ){
                    while( t.stackPointer > index ){
                        t.undo();
                    }
                
                // redo until target
                }else if( t.stackPointer < index ){
                    while( t.stackPointer < index ){
                        t.redo();
                    } 
                }

                // set active state
                actions.find('.pixe-history-action').each(function(){
                    var action = $(this);
                    var index = action.attr('data-pointer');
                    action.removeClass('pixe-active pixe-inactive');

                    if( index > t.stackPointer ){
                        action.addClass('pixe-inactive');
                    }else if( index == t.stackPointer ){
                        action.addClass('pixe-active');
                    }
                });
            });

        },

        // create new action
        // type : remove
        createAction: function(item, type, subtype, displayText = '', args = {}){
            var t = this;
            var action = $.extend({
                item: item,
                type: type,
                subtype: subtype,
                displayText: displayText,
            }, args);
            
            switch( type ){
                // set tartget location
                case 'add':
                    action = t.setActionLocation(action, 'target');
                    if( action.subtype == 'column' ){
                        action.prevSteps = [];
                        action.steps = [];
                    }

                    // replace item for block container
                    if( item.hasClass('pixe-block-container') ){
                        item.on('convertblock removeblock', function(e){
                            if( e.type == 'convertblock' ){
                                var content = item.data('block-content');
                                if( content.length ){
                                    action.item = content;
                                }
                            }else if( e.type == 'removeblock' ){
                                for( var i in t.stack ){
                                    if( t.stack[i] == action ){
                                        t.stack.splice(i, 1);
                                        if( t.stackPointer >= i ){
                                            t.stackPointer--;
                                        }
                                        break;
                                    }
                                }
                            }
                            $(this).off(e);
                        });
                    }
                    break;
                case 'duplicate':
                    action = t.setActionLocation(action, 'target');
                    break;
                
                // set inital location
                case 'remove': 
                    action = t.setActionLocation(action);
                    break;
                case 'move': 
                    action = t.setActionLocation(action);
                    if( action.subtype == 'column' ){
                        action.prevSteps = [];
                        action.steps = [];
                    }
                    break;

                // edit action
                default:
                    action.prevSteps = [];
                    action.steps = [];
                    break;
            }

            return action; 
        },

        // source / target settings
        setActionStep: function(action, item, slug, value, isPrev = false, clone = true){
            var settings = {
                item: item,
                slug: slug
            };
            
            if( clone ){
                settings.value = $.pixeClone(value);
            }else{
                settings.value = value;
            }

            if( isPrev ){
                action.prevSteps.push(settings);
            }else{
                action.steps.push(settings); 
            }

            return action;
        },

        // settings: item, slug, value
        setObjectActionStep: function(action, settings, isPrev = false, clone = true){
            if( clone ){
                settings.value = $.pixeClone(settings.value);
            }

            if( isPrev ){
                action.prevSteps.push(settings);
            }else{
                action.steps.push(settings); 
            }

            return action;
        },

        // source / target location
        setActionLocation: function(action, area = 'source'){
            var location;
            if( action.item.prev().length ){
                location = {
                    type: 'after', 
                    item: action.item.prev()
                }
            }else{
                // first child
                location = {
                    type: 'children',
                    item: action.item.parent()
                }
            }

            if( area == 'source' ){
                action.sourceLocation = location;
            }else if( area == 'target' ){
                action.targetLocation = location;
            }

            return action;
        },

        // add action to stack
        addAction: function(action, updateNavigator = true){
            var t = this;

            // add new action
            if( t.stack.length == t.stackPointer ){
                t.stack.push(action);
                t.stackPointer++;
                
                // remove first item when exceeds max stack
                while( t.stack.length > t.maxStack ){
                    t.stack.shift();
                    t.stackPointer = t.stack.length;
                } 

            // replace & remove reverse action
            }else{
                t.stack[t.stackPointer] = action;
                t.stackPointer++;
                t.stack.length = t.stackPointer;
            }
            
            // set publish button status
            $.pixeEditor.setPublishStatus('edited');

            // content change event
            if( $.pixeEditorEvents.contentchange ){
                window.pixeEditor.trigger('contentchange');
            }
            
            // update navigator panel
            if( updateNavigator && ['add', 'remove', 'move', 'duplicate'].indexOf(action.type) >= 0 ){
                $.pixeEditor.updateNavigatorPanel();
            }
        },

        // move/add item to specified location
        moveItem: function(item, location){
            if( location.type == 'after' ){
                item.insertAfter(location.item);
            }else if( location.type == 'children' ){
                location.item.prepend(item);
            }
        },

        doAt: function(index){
            var t = this;
            
            // undo until target
            if( t.stackPointer > index ){
                while( t.stackPointer > index ){
                    t.undo();
                }
            
            // redo until target
            }else if( t.stackPointer < index ){
                while( t.stackPointer < index ){
                    t.redo();
                } 
            }

        },

        // undo
        undo: function(){
            var t = this;
            if( t.stackPointer <= 0 ) return;
            var action = t.stack[t.stackPointer - 1];

            // clear edit history object
            delete $.pixeInlineContent.editTarget;

            function doSteps(slug = true){
                if( action.prevSteps ){
                    for( var i in action.prevSteps ){
                        var step = action.prevSteps[i];

                        // update settings
                        if( typeof(step.value) == 'undefined' ){
                            delete step.item.data('settings')[step.slug];
                        }else if( slug ){
                            if( step.value instanceof jQuery ){
                                step.item.data('settings')[step.slug] = step.value;
                            }else{
                                step.item.data('settings')[step.slug] = $.pixeClone(step.value);
                            }
                        }else{
                            step.item.data('settings', $.pixeClone(step.value));
                        }
                        
                        // render
                        $.pixeEditor.rerender(step.item, step.slug);
                        
                        if( step.value instanceof jQuery ){
                            delete step.item.data('settings')[step.slug]; // remove temporary object
                        }else{
                            $.pixeEditor.updateSettingsPanel(step.item, step.slug, step.value);
                        }
                    }
                }
            }
 
            // undo action
            switch( action.type ){
                case 'add': // remove item out
                    action.item.detach();
                    doSteps();
                    $.pixeEditor.clearEditTarget();
                    break;
                case 'duplicate':
                    action.item.detach();
                    break;
                case 'move': // return item to source location
                    t.moveItem(action.item, action.sourceLocation);
                    doSteps();
                    break;
                case 'remove': // add item back to source location
                    t.moveItem(action.item, action.sourceLocation);
                    break;
                case 'edit': 
                    doSteps();
                    break;
                case 'paste-style': 
                    doSteps(false);
                    break;
                case 'object': // for plain object ( page settings )
                    for( var i in action.prevSteps ){
                        var step = action.prevSteps[i];
                        if( typeof(step.value) == 'undefined' ){
                            delete step.item[step.slug];
                        }else{
                            step.item[step.slug] = step.value;
                        }
                    }

                    if( action.onChange ){
                        action.onChange();
                    }
                    break;
            }

            $.pixeEditor.setStatus();
            t.stackPointer--;

            // set publish button status
            $.pixeEditor.setPublishStatus('edited');

            // content change event
            if( $.pixeEditorEvents.contentchange ){
                window.pixeEditor.trigger('contentchange');
            }

            // update navigator panel
            if( ['add', 'remove', 'move', 'duplicate'].indexOf(action.type) >= 0 ){
                $.pixeEditor.updateNavigatorPanel();
            }
        },

        //  redo
        redo: function(){
            var t = this;
            if( !t.stack[t.stackPointer] ) return;
            var action = t.stack[t.stackPointer];

            // clear edit history object
            delete $.pixeInlineContent.editTarget;

            function doSteps(slug = true){
                if( action.steps ){
                    for( var i in action.steps ){
                        var step = action.steps[i];

                        // update settings
                        if( typeof(step.value) == 'undefined' ){
                            delete step.item.data('settings')[step.slug];
                        }else if( slug ){
                            if( step.value instanceof jQuery ){
                                step.item.data('settings')[step.slug] = step.value;
                            }else{
                                step.item.data('settings')[step.slug] = $.pixeClone(step.value);
                            }
                        }else{
                            step.item.data('settings', $.pixeClone(step.value));
                        }
                        
                        // render
                        $.pixeEditor.rerender(step.item, step.slug);
                        
                        if( step.value instanceof jQuery ){
                            delete step.item.data('settings')[step.slug]; // remove temporary object
                        }else{
                            $.pixeEditor.updateSettingsPanel(step.item, step.slug, step.value);
                        }
                    }
                }
            }

            // redo action
            switch( action.type ){
                case 'add': // add item back to target location
                    t.moveItem(action.item, action.targetLocation);
                    doSteps();
                    break;
                case 'duplicate': 
                    t.moveItem(action.item, action.targetLocation);
                    break;
                case 'move': // move item again
                    t.moveItem(action.item, action.targetLocation);
                    doSteps();
                    break;
                case 'remove': // remove item again
                    action.item.detach();
                    $.pixeEditor.clearEditTarget();
                    break;
                case 'edit': 
                    doSteps();
                    break;
                case 'paste-style': 
                    doSteps(false);
                    break;
                case 'object': // for plain object ( page settings )
                    for( var i in action.steps ){
                        var step = action.steps[i];
                        if( typeof(step.value) == 'undefined' ){
                            delete step.item[step.slug];
                        }else{
                            step.item[step.slug] = step.value;
                        }
                    }

                    if( action.onChange ){
                        action.onChange();
                    }
                    break;
            }

            $.pixeEditor.setStatus();
            t.stackPointer++;

            // set publish button status
            $.pixeEditor.setPublishStatus('edited');

            // content change event
            if( $.pixeEditorEvents.contentchange ){
                window.pixeEditor.trigger('contentchange');
            }

            // update navigator panel
            if( ['add', 'remove', 'move', 'duplicate'].indexOf(action.type) >= 0 ){
                $.pixeEditor.updateNavigatorPanel();
            }
        },
    }

})(jQuery);	
