/* eslint-disable jsdoc/require-description-complete-sentence */ "use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get PLUGIN_KEY () {
        return PLUGIN_KEY;
    },
    get PLUGIN_PRIORITY () {
        return PLUGIN_PRIORITY;
    },
    get UndoRedo () {
        return UndoRedo;
    }
});
const _base = require("../base");
const _hooks = require("../../core/hooks");
const _object = require("../../helpers/object");
const _actions = require("./actions");
function _check_private_redeclaration(obj, privateCollection) {
    if (privateCollection.has(obj)) {
        throw new TypeError("Cannot initialize the same private elements twice on an object");
    }
}
function _class_private_method_get(receiver, privateSet, fn) {
    if (!privateSet.has(receiver)) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    return fn;
}
function _class_private_method_init(obj, privateSet) {
    _check_private_redeclaration(obj, privateSet);
    privateSet.add(obj);
}
const SHORTCUTS_GROUP = 'undoRedo';
const PLUGIN_KEY = 'undoRedo';
const PLUGIN_PRIORITY = 1000;
_hooks.Hooks.getSingleton().register('beforeUndo');
_hooks.Hooks.getSingleton().register('afterUndo');
_hooks.Hooks.getSingleton().register('beforeRedo');
_hooks.Hooks.getSingleton().register('afterRedo');
var /**
   * Listens to the data change and if the source is `loadData` then clears the undo and redo history.
   *
   * @param {Array} changes The data changes.
   * @param {string} source The source of the change.
   */ _onAfterChange = /*#__PURE__*/ new WeakSet();
class UndoRedo extends _base.BasePlugin {
    static get PLUGIN_KEY() {
        return PLUGIN_KEY;
    }
    static get PLUGIN_PRIORITY() {
        return PLUGIN_PRIORITY;
    }
    static get SETTING_KEYS() {
        return true;
    }
    /**
   * Checks if the plugin is enabled in the handsontable settings. This method is executed in {@link Hooks#beforeInit}
   * hook and if it returns `true` then the {@link UndoRedo#enablePlugin} method is called.
   *
   * @returns {boolean}
   */ isEnabled() {
        return !!this.hot.getSettings().undo;
    }
    /**
   * Enables the plugin functionality for this Handsontable instance.
   */ enablePlugin() {
        if (this.enabled) {
            return;
        }
        this.addHook('afterChange', (...args)=>_class_private_method_get(this, _onAfterChange, onAfterChange).call(this, ...args));
        this.registerShortcuts();
        super.enablePlugin();
    }
    /**
   * Disables the plugin functionality for this Handsontable instance.
   */ disablePlugin() {
        super.disablePlugin();
        this.clear();
        this.unregisterShortcuts();
    }
    /**
   * Registers shortcuts responsible for performing undo/redo.
   *
   * @private
   */ registerShortcuts() {
        const shortcutManager = this.hot.getShortcutManager();
        const gridContext = shortcutManager.getContext('grid');
        const runOnlyIf = (event)=>{
            return !event.altKey; // right ALT in some systems triggers ALT+CTR
        };
        const config = {
            runOnlyIf,
            group: SHORTCUTS_GROUP
        };
        gridContext.addShortcuts([
            {
                keys: [
                    [
                        'Control/Meta',
                        'z'
                    ]
                ],
                callback: ()=>{
                    this.undo();
                }
            },
            {
                keys: [
                    [
                        'Control/Meta',
                        'y'
                    ],
                    [
                        'Control/Meta',
                        'Shift',
                        'z'
                    ]
                ],
                callback: ()=>{
                    this.redo();
                }
            }
        ], config);
    }
    /**
   * Unregister shortcuts responsible for performing undo/redo.
   *
   * @private
   */ unregisterShortcuts() {
        const shortcutManager = this.hot.getShortcutManager();
        const gridContext = shortcutManager.getContext('grid');
        gridContext.removeShortcutsByGroup(SHORTCUTS_GROUP);
    }
    /**
   * Stash information about performed actions.
   *
   * @fires Hooks#beforeUndoStackChange
   * @fires Hooks#afterUndoStackChange
   * @fires Hooks#beforeRedoStackChange
   * @fires Hooks#afterRedoStackChange
   * @param {Function} wrappedAction The action descriptor wrapped in a closure.
   * @param {string} [source] Source of the action. It is defined just for more general actions (not related to plugins).
   */ done(wrappedAction, source) {
        if (this.ignoreNewActions) {
            return;
        }
        const isBlockedByDefault = source === 'UndoRedo.undo' || source === 'UndoRedo.redo' || source === 'auto';
        if (isBlockedByDefault) {
            return;
        }
        const doneActionsCopy = this.doneActions.slice();
        const continueAction = this.hot.runHooks('beforeUndoStackChange', doneActionsCopy, source);
        if (continueAction === false) {
            return;
        }
        const newAction = wrappedAction();
        const undoneActionsCopy = this.undoneActions.slice();
        if (newAction !== null) {
            this.doneActions.push(newAction);
        }
        this.hot.runHooks('afterUndoStackChange', doneActionsCopy, this.doneActions.slice());
        this.hot.runHooks('beforeRedoStackChange', undoneActionsCopy);
        this.undoneActions.length = 0;
        this.hot.runHooks('afterRedoStackChange', undoneActionsCopy, this.undoneActions.slice());
    }
    /**
   * Undo the last action performed to the table.
   *
   * @fires Hooks#beforeUndoStackChange
   * @fires Hooks#afterUndoStackChange
   * @fires Hooks#beforeRedoStackChange
   * @fires Hooks#afterRedoStackChange
   * @fires Hooks#beforeUndo
   * @fires Hooks#afterUndo
   */ undo() {
        if (!this.isUndoAvailable()) {
            return;
        }
        const doneActionsCopy = this.doneActions.slice();
        this.hot.runHooks('beforeUndoStackChange', doneActionsCopy);
        const action = this.doneActions.pop();
        this.hot.runHooks('afterUndoStackChange', doneActionsCopy, this.doneActions.slice());
        const actionClone = (0, _object.deepClone)(action);
        const continueAction = this.hot.runHooks('beforeUndo', actionClone);
        if (continueAction === false) {
            return;
        }
        this.ignoreNewActions = true;
        const undoneActionsCopy = this.undoneActions.slice();
        this.hot.runHooks('beforeRedoStackChange', undoneActionsCopy);
        action.undo(this.hot, ()=>{
            this.ignoreNewActions = false;
            this.undoneActions.push(action);
        });
        this.hot.runHooks('afterRedoStackChange', undoneActionsCopy, this.undoneActions.slice());
        this.hot.runHooks('afterUndo', actionClone);
    }
    /**
   * Redo the previous action performed to the table (used to reverse an undo).
   *
   * @fires Hooks#beforeUndoStackChange
   * @fires Hooks#afterUndoStackChange
   * @fires Hooks#beforeRedoStackChange
   * @fires Hooks#afterRedoStackChange
   * @fires Hooks#beforeRedo
   * @fires Hooks#afterRedo
   */ redo() {
        if (!this.isRedoAvailable()) {
            return;
        }
        const undoneActionsCopy = this.undoneActions.slice();
        this.hot.runHooks('beforeRedoStackChange', undoneActionsCopy);
        const action = this.undoneActions.pop();
        this.hot.runHooks('afterRedoStackChange', undoneActionsCopy, this.undoneActions.slice());
        const actionClone = (0, _object.deepClone)(action);
        const continueAction = this.hot.runHooks('beforeRedo', actionClone);
        if (continueAction === false) {
            return;
        }
        this.ignoreNewActions = true;
        const doneActionsCopy = this.doneActions.slice();
        this.hot.runHooks('beforeUndoStackChange', doneActionsCopy);
        action.redo(this.hot, ()=>{
            this.ignoreNewActions = false;
            this.doneActions.push(action);
        });
        this.hot.runHooks('afterUndoStackChange', doneActionsCopy, this.doneActions.slice());
        this.hot.runHooks('afterRedo', actionClone);
    }
    /**
   * Checks if undo action is available.
   *
   * @returns {boolean} Return `true` if undo can be performed, `false` otherwise.
   */ isUndoAvailable() {
        return this.doneActions.length > 0;
    }
    /**
   * Checks if redo action is available.
   *
   * @returns {boolean} Return `true` if redo can be performed, `false` otherwise.
   */ isRedoAvailable() {
        return this.undoneActions.length > 0;
    }
    /**
   * Clears undo and redo history.
   */ clear() {
        this.doneActions.length = 0;
        this.undoneActions.length = 0;
    }
    /**
   * Destroys the plugin instance.
   */ destroy() {
        this.clear();
        this.doneActions = null;
        this.undoneActions = null;
        super.destroy();
    }
    constructor(hotInstance){
        super(hotInstance), _class_private_method_init(this, _onAfterChange), /**
   * The list of registered action do undo.
   *
   * @private
   * @type {Array}
   */ this.doneActions = [], /**
   * The list of registered action do redo.
   *
   * @private
   * @type {Array}
   */ this.undoneActions = [], /**
   * The flag that determines if new actions should be ignored.
   *
   * @private
   * @type {boolean}
   */ this.ignoreNewActions = false;
        (0, _actions.registerActions)(hotInstance, this);
    }
}
function onAfterChange(changes, source) {
    if (source === 'loadData') {
        this.clear();
    }
}
