let undoDigests = [];
let redoDigests = [];

(function($) {

   let debug = window.oxy_toolbox_undo_options['debugger'];
   let max = window.oxy_toolbox_undo_options['max'] && parseInt(window.oxy_toolbox_undo_options['max']) ? parseInt(window.oxy_toolbox_undo_options['max']) : 12 ;
   let autosave = window.oxy_toolbox_undo_options['autosave'] && parseInt(window.oxy_toolbox_undo_options['autosave']) ? parseInt(window.oxy_toolbox_undo_options['autosave']) : false ;
   let smartAutoSave = window.oxy_toolbox_undo_options['smartautosave'] && parseInt(window.oxy_toolbox_undo_options['smartautosave']) ? parseInt(window.oxy_toolbox_undo_options['smartautosave']) : false ;
   let disablekeyboard = window.oxy_toolbox_undo_options['disablekeyboard'] && parseInt(window.oxy_toolbox_undo_options['disablekeyboard']) ? parseInt(window.oxy_toolbox_undo_options['disablekeyboard']) : false ;

   function log(val1) {
      if(debug) {
         console.log('oxy-undo', val1);
      }
   }

   $('document').ready(function() {     

      //let undoDigests = [];

      // let redoDigests = [];
      
      let timeout = 1000;
      let firing = false;
      let processingDOM = false;
      let fireUndo, fireRedo;
      let contentOldVal = false;
      let autosaveTimeout = false;
      
      let alltimeout;
      let loggingChanges = false;

      let autoSaveDelay = false;

      let keyPressed = false;
      let keyPressedTimeout = false;

      let mouseMoved = false;
      let mouseMovedTimeout = false



      function fireAutoSave() {
         
         if(firing === false && processingDOM === false && loggingChanges === false && (!smartAutoSave || (keyPressed === false && mouseMoved === false))) {
            iframeScope.savePage();
            console.log('auto Save', (new Date()));
            setAutoSaveTimeout();
         } else {
            autoSaveDelay = setTimeout(() => {
               clearTimeout(autoSaveDelay);
               fireAutoSave();
            }, 3000);
         }

      }

      function setAutoSaveTimeout() {

         autosaveTimeout = setTimeout(() => {
            
            clearTimeout(autosaveTimeout);

            fireAutoSave();
         
         }, autosave*60000)
 
      }
      
      function captureMouse(e) {
         mouseMoved = true;
         
         if(mouseMovedTimeout) {
            clearTimeout(mouseMovedTimeout);
         }

         mouseMovedTimeout = setTimeout(() => {
            clearTimeout(mouseMovedTimeout);
            mouseMoved = false;
            
         }, 2000)
      }

      // add an undo button
      let container = $('.oxygen-toolbar-panels > div:first-child', parent.document);
      let position = 0;
      if(container.length < 1) {
         container = $('#eeui-top-bar--add-components', parent.document);
         position = 1;
      }

      let undoButton = $('<div class="oxy-undo-control"><svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="undo" class="svg-inline--fa fa-undo fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M12 8h27.711c6.739 0 12.157 5.548 11.997 12.286l-2.347 98.568C93.925 51.834 170.212 7.73 256.793 8.001 393.18 8.428 504.213 120.009 504 256.396 503.786 393.181 392.835 504 256 504c-63.926 0-122.202-24.187-166.178-63.908-5.113-4.618-5.354-12.561-.482-17.433l19.738-19.738c4.498-4.498 11.753-4.785 16.501-.552C160.213 433.246 205.895 452 256 452c108.322 0 196-87.662 196-196 0-108.322-87.662-196-196-196-79.545 0-147.941 47.282-178.675 115.302l126.389-3.009c6.737-.16 12.286 5.257 12.286 11.997V212c0 6.627-5.373 12-12 12H12c-6.627 0-12-5.373-12-12V20C0 13.373 5.373 8 12 8z"></path></svg><span>Undo</span></div>');
      let redoButton = $('<div class="oxy-undo-control"><svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="redo" class="svg-inline--fa fa-redo fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M500 8h-27.711c-6.739 0-12.157 5.548-11.997 12.286l2.347 98.568C418.075 51.834 341.788 7.73 255.207 8.001 118.82 8.428 7.787 120.009 8 256.396 8.214 393.181 119.165 504 256 504c63.926 0 122.202-24.187 166.178-63.908 5.113-4.618 5.354-12.561.482-17.433l-19.738-19.738c-4.498-4.498-11.753-4.785-16.501-.552C351.787 433.246 306.105 452 256 452c-108.322 0-196-87.662-196-196 0-108.322 87.662-196 196-196 79.545 0 147.941 47.282 178.675 115.302l-126.389-3.009c-6.737-.16-12.286 5.257-12.286 11.997V212c0 6.627 5.373 12 12 12h192c6.627 0 12-5.373 12-12V20c0-6.627-5.373-12-12-12z"></path></svg><span>Redo</span></div>');

      if(position === 0) {
         container.prepend(redoButton).prepend(undoButton);
      } else {
         redoButton.insertAfter(container);
         undoButton.insertAfter(container);
      }

      let applyComponentDefaultOptions = iframeScope.applyComponentDefaultOptions;

      iframeScope.applyComponentDefaultOptions = function(id, componentName, component) {
         
         if(processingDOM) {

            iframeScope.component.options[id] = {};
        
            iframeScope.component.options[id]['original'] = {};
        
            iframeScope.component.options[id]['id'] = {};
         }
         
         applyComponentDefaultOptions(id, componentName, component);

      }
      
      let codeMirrorLoaded = false;

      $('body', parent.document).on('focus', '.CodeMirror', () => { 
         
         if(codeMirrorLoaded === false) {
            codeMirrorLoaded = true;
         } else {
            return;
         }

         let codeMirror = parent.document.querySelector('.CodeMirror').CodeMirror;

         log('enabling CodeMirror editing');
         
         let prop = false;
         
         if(iframeScope.isEditing('id')) {
            prop = 'componentsTree';
         } 
         else if(iframeScope.isEditing('class')) {
            prop = 'classes';
         }
         else if(iframeScope.isEditing('style-sheet')) {
            prop = 'styleSheets';
         }
         else if(iframeScope.selectedNodeType === 'selector') {
            prop = 'customSelectors';
         }
         if(prop === false) {
            codeMirrorLoaded = false;
            return;
         }
         contentOldVal = JSON.stringify(iframeScope[prop]);

         codeMirror.on('blur', function(e) { 
            if(contentOldVal && contentOldVal !== JSON.stringify(iframeScope[prop])) {
               recordChange(prop, contentOldVal, true);
               contentOldVal = false;
            }
            codeMirror.off('blur');
            codeMirrorLoaded = false;
         })
         
      });



      let enableContentEdit = iframeScope.parentScope.enableContentEdit;

      iframeScope.parentScope.enableContentEdit = function(prop) {
         // if say a span or bold being edited inside a text
         if(contentOldVal !== false && contentOldVal !== JSON.stringify(iframeScope.componentsTree)) {
            recordChange('componentsTree', contentOldVal, true);
            contentOldVal = false;
         }
         log('enabling content editing');
         contentOldVal = JSON.stringify(iframeScope.componentsTree);
         enableContentEdit(prop);
      }

      let disableContentEdit = iframeScope.parentScope.disableContentEdit;
      
      iframeScope.parentScope.disableContentEdit = function() {
         if ( !iframeScope.parentScope.actionTabs["contentEditing"] ) {
            return false;
         }
         
         disableContentEdit();

         if(contentOldVal !== false && contentOldVal !== JSON.stringify(iframeScope.componentsTree)) {
            recordChange('componentsTree', contentOldVal, true);
            contentOldVal = false;
         }
      }

      function makeChangesToLayout(target) {
            
            processingDOM = true;
            iframeScope.componentsClasses = [];
            let activeID = iframeScope.component.active.id;
            let activeName = iframeScope.component.active.name;
            try {
               iframeScope.activateComponent();
            } catch(e) {
               // so what if it erred 
            }
            iframeScope.rebuildDOM();
            iframeScope.updateDOMTreeNavigator();

            if(!iframeScope.componentsTree['children'] || iframeScope.componentsTree['children'].length < 1) {
               $('div#ct-builder').html('');
            }

            if(activeID && activeName) {
               try {
                  iframeScope.activateComponent(activeID, activeName);
               } catch(e) {
                  iframeScope.component.active.id = 0;
                  iframeScope.component.active.name = 'root';
               }
            } else {
               iframeScope.component.active.id = 0;
               iframeScope.component.active.name = 'root';
            }

            processingDOM = false;
            

         
      }

      function loadButtons() {
         setTimeout(() => {
         
            if(undoDigests.length > 0) {
              loadUndoButton()
               
            }

            if(redoDigests.length > 0) {
               loadRedoButton()
            }

         
            firing = false;
         }, 500)
            
      }

      fireUndo = function() {

         undoButton.off('click');

         if(!undoButton.hasClass('loaded')) {
            return;
         }
         
         undoButton.removeClass('loaded');

         if(undoDigests.length < 1) {
            return;
         }

         firing = true;
         
         let digest = undoDigests.pop();
         
         

         redoDigests.push([]);

         digest.reverse().forEach((item) => {
            
            redoDigests[redoDigests.length-1].push({
               target: item.target,
               payload: JSON.stringify(iframeScope[item.target])
            });

            if(item.target === 'classes') {
               iframeScope.currentClass = false;
            }

            iframeScope[item.target] = JSON.parse(item.payload);   
            log('un did' + item.target);
         })
         
         
         
         

         makeChangesToLayout();
         loadButtons();
      }

      fireRedo = function() {

         redoButton.off('click');
         if(!redoButton.hasClass('loaded')) {
            return;
         }

         redoButton.removeClass('loaded');

         if(redoDigests.length < 1) {
            return;
         }

         firing = true;
         

         let digest = redoDigests.pop();

         undoDigests.push([]);

         digest.reverse().forEach((item) => {
            
            undoDigests[undoDigests.length-1].push({
               target: item.target,
               payload: JSON.stringify(iframeScope[item.target])
            });

            iframeScope[item.target] = JSON.parse(item.payload);   
            log('re did' + item.target);
         })
         
         makeChangesToLayout();
         loadButtons();

      }

      function loadUndoButton() {
         undoButton.off('click');
         undoButton.addClass('loaded');
         undoButton.on('click', () => {
            fireUndo();
         }); 
      }

      function loadRedoButton() {
         redoButton.off('click');
         redoButton.addClass('loaded');
         redoButton.on('click', () => {
            fireRedo();
         }); 
      }

      function recordChange(prop, oldVal, explicit) {
         redoDigests = [];
         redoButton.off('click');
         redoButton.removeClass('loaded');

         if(loggingChanges === false) {
            log('logging on');
            loggingChanges = true;


            if(undoDigests.length > 0) {

               if(undoDigests[undoDigests.length-1].length > 0) {
                  if(undoDigests.length >= max) {
                     log('reached max limit, slicing of from bottom');
                     undoDigests.splice(0, 1);
                  }
                  undoDigests.push([]);
               }
            } else {
               undoDigests.push([]);
            }
         }

         log('changed '+prop);

         undoDigests[undoDigests.length-1].push(
            {
               payload: oldVal,
               target: prop
            }
         )
         if(explicit) {
            loggingChanges = false;
            log('logging off');
         }
         loadUndoButton();
      }

      

      function setUndoRedo(prop) {
         let ztimeout;
         let oldVal;

         function putWatch() {
            let de = iframeScope.$watch(prop, 

               function() { 


                  if(ztimeout) {
                     clearTimeout(ztimeout)  
                  }

                  
                  
                  

                  ztimeout = setTimeout(() => {


                        // de();

                        if(oldVal && oldVal !== JSON.stringify(iframeScope[prop])) {
                           let doNotRecord = false
                           if(codeMirrorLoaded === true) {
                              log('skipping recording change as inside codeMirror');
                              doNotRecord = true;
                           }

                           let id = iframeScope.component.active.id;

                           if(id) {
                              let component = iframeScope.getComponentById(id);

                              if(component && component.attr && component.attr('contenteditable') === 'true') {
                                 log('skipping recording change as content is being edited');
                                 doNotRecord = true;
                              }
                           }

                           if(!firing && doNotRecord === false) {
                              recordChange(prop, oldVal);
                           }
                        

                           if(alltimeout) {
                              clearTimeout;
                           }

                           alltimeout = setTimeout(() => {
                              loggingChanges = false;
                              log('logging off');
                              //firing = false;

                           }, 1100);

                        }

                        oldVal = JSON.stringify(iframeScope[prop])
                        putWatch();
                        

                  }, 200);

               }
            );

            oldVal = JSON.stringify(iframeScope[prop])
         }
         log('setting undo for '+prop);
         putWatch();
      }


      let props = [
         'componentsTree',
         'classes',
         'pageSettings',
         'pageSettingsMeta',
         'globalSettings',
         'styleSets',
         'styleFolders',
         'customSelectors',
         'styleSheets',
         'easyPostsCustomTemplates',
         'commentsListCustomTemplates',
         'typeKitFonts',
         'globalColorSets',
         'elementPresets'
      ];

      function captureKeys(event, whichFrame) {

         if(disablekeyboard) {
            return true;
         }
         
         keyPressed = true;

         if(keyPressedTimeout) {
            clearTimeout(keyPressedTimeout);
         }

         keyPressedTimeout = setTimeout(() => {
            clearTimeout(keyPressedTimeout);
            keyPressed = false;
         }, 2000)
         
         if((event.ctrlKey || event.metaKey) && (event.which===90 || event.which===89)) {

            if(codeMirrorLoaded === true) {
               log('skipping key capture as inside codeMirror');
               return;
            }

            let id = iframeScope.component.active.id;

            if(id) {
               let component = iframeScope.getComponentById(id);

               if(component && component.attr && component.attr('contenteditable') === 'true') {
                  log('skipping key capture as the content is being edited');
                  return;
               }
            }

            let tagName = false;
            if($(':focus').length > 0) {
               tagName = $(':focus').prop('tagName');
            } else if($(':focus', parent.document).length > 0) {
               tagName = $(':focus', parent.document).prop('tagName');
            }
            
            if(tagName === 'INPUT' || tagName === 'TEXTAREA') {
               log('skipping key capture as an input or a textarea is being edited')
               return;
            }

            log('keys captures in ' +whichFrame);

            // Save Function
            if(event.shiftKey || event.which === 89) {
                if(redoButton.hasClass('loaded')) {
                  fireRedo()
                }
            } else {
                if(undoButton.hasClass('loaded')) {
                  fireUndo()
                }
            }
            event.preventDefault();
            return false;
         }

      }

      function action() {
         
         props.forEach(setUndoRedo);
         
         $('body', parent.document).on('keydown', (e) => {captureKeys(e, 'outerFrame')});
         $('body').on('keydown', (e) => {captureKeys(e, 'innerFrame')});

         $(parent.document).on('mousemove', (e) => {captureMouse(e, 'outerFrame')});
         $(document).on('mousemove', (e) => {captureMouse(e, 'innerFrame')});

         if(autosave) {
            setAutoSaveTimeout();
         }
      }

      let buildComponentsFromTree = iframeScope.buildComponentsFromTree;

      let initTimeOut;
      let planBTimeout;
      
      iframeScope.buildComponentsFromTree = function(componentsTree, id, reorder, domNode, primaryIndex, forceZeroIndex) {
         
         buildComponentsFromTree(componentsTree, id, reorder, domNode, primaryIndex, forceZeroIndex)
         if(planBTimeout) {
            clearTimeout(planBTimeout);
         }
         if(initTimeOut) {
            clearTimeout(initTimeOut);
         }
         
         
         initTimeOut = setTimeout(() => {
         
            iframeScope.buildComponentsFromTree = buildComponentsFromTree;
            action();
         }, 1000)

      }

      planBTimeout = setTimeout(() => {
         
         iframeScope.buildComponentsFromTree = buildComponentsFromTree;
         action();
      }, 3000)


   });

})(jQuery);