---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by luojunxin.
--- DateTime: 2020/8/20 10:10
---

LUndoRedoManager = LUndoRedoManager or class("LUndoRedoManager");

function LUndoRedoManager:ctor()
    ImportScript("ugc_common/script/core/data_layer/undo_redo/action_base.lua");
    ImportScript("ugc_common/script/core/data_layer/undo_redo/action_attr_integer_modify.lua");
    ImportScript("ugc_common/script/core/data_layer/undo_redo/action_attr_bool_modify.lua");
    ImportScript("ugc_common/script/core/data_layer/undo_redo/action_attr_float_modify.lua");
    ImportScript("ugc_common/script/core/data_layer/undo_redo/action_attr_string_modify.lua");
    ImportScript("ugc_common/script/core/data_layer/undo_redo/action_entity_insert.lua");
    ImportScript("ugc_common/script/core/data_layer/undo_redo/action_entity_remove.lua");
    self.m_lstActions = {};
    self.m_lstURItems = {};
    self.m_nCurrURItemsIndex = 0;
    self.m_nTransactionDepth = 0;
    self.m_nURItemsLimit = 50;
end

function LUndoRedoManager:dtor()
end

function LUndoRedoManager:AddAction(action)
    self:__DiscardActions();
    local oldAction = self.m_lstActions[#self.m_lstActions];
    local typeAction = action.target:GetType();
    if typeAction ~= 0 and typeAction ~= 2 then
        oldAction = nil;    -- 只有数字类型的修改操作需要整合
    end
    if oldAction ~= nil and oldAction.target == action.target then
        local time = os.time();
        if time - oldAction.__time > 2 then
            action.__time = time;
            table.insert(self.m_lstActions, action);
            if self.m_nTransactionDepth == 0 then
                table.insert(self.m_lstURItems, #self.m_lstActions);
                self.m_nCurrURItemsIndex = #self.m_lstActions;
            end
        else
            oldAction.newValue = action.newValue;
            oldAction.__time = time;
        end
    else
        action.__time = os.time();
        table.insert(self.m_lstActions, action);
        if self.m_nTransactionDepth == 0 then
            table.insert(self.m_lstURItems, #self.m_lstActions);
            self.m_nCurrURItemsIndex = #self.m_lstActions;
        end
    end
    self:__ForgetActions();
end

function LUndoRedoManager:__DiscardActions()
    if self.m_nCurrURItemsIndex < #self.m_lstURItems then
        local func = function ()
            local nTempIndex = self.m_lstURItems[self.m_nCurrURItemsIndex + 1];
            local nTempMax = #self.m_lstActions;
            for nIndex = nTempIndex, nTempMax do
                self.m_lstActions[nIndex]:Discard();
            end
            for nIndex = nTempIndex, nTempMax do
                table.remove(self.m_lstActions, #self.m_lstActions);
            end
            nTempIndex = self.m_nCurrURItemsIndex + 1;
            nTempMax = #self.m_lstURItems;
            for nIndex = nTempIndex, nTempMax do
                table.remove(self.m_lstURItems, #self.m_lstURItems);
            end
        end
        xpcall(func, Traceback);
    end
end

function LUndoRedoManager:__ForgetActions()
    if self.m_nTransactionDepth == 0 and #self.m_lstURItems > self.m_nURItemsLimit then
        local nStart = self.m_lstURItems[1];
        local nEnd = self.m_lstURItems[2] - 1;
        for nIndex = nStart, nEnd do
            self.m_lstActions[1]:Forget();
            table.remove(self.m_lstActions, 1);
        end
        table.remove(self.m_lstURItems, 1);
        self.m_nCurrURItemsIndex = self.m_nCurrURItemsIndex - 1;
        for nIndex = 1, #self.m_lstURItems do
            self.m_lstURItems[nIndex] = self.m_lstURItems[nIndex] - nEnd;
        end
    end
end

function LUndoRedoManager:BeginGroup(szName)
    local action = LActionBase:new();
    action.name = szName;
    g_UndoRedoManager:AddAction(action);
    self.m_nTransactionDepth = self.m_nTransactionDepth + 1;
end

function LUndoRedoManager:EndGroup()
    self.m_nTransactionDepth = self.m_nTransactionDepth - 1;
end

function LUndoRedoManager:CanUndo()
    return self.m_nCurrURItemsIndex > 0;
end

function LUndoRedoManager:CanRedo()
    return self.m_nCurrURItemsIndex < #self.m_lstURItems;
end

function LUndoRedoManager:Undo()
    if self:CanUndo() then
        local nTempMax = #self.m_lstActions;
        if self.m_lstURItems[self.m_nCurrURItemsIndex + 1] ~= nil then
            nTempMax = self.m_lstURItems[self.m_nCurrURItemsIndex + 1] - 1;
        end
        local nTempIndex = self.m_lstURItems[self.m_nCurrURItemsIndex];
        for nIndex = nTempMax, nTempIndex, -1 do
            self.m_lstActions[nIndex]:Undo();
        end
        self.m_nCurrURItemsIndex = self.m_nCurrURItemsIndex - 1;
    end
end

function LUndoRedoManager:Redo()
    if self:CanRedo() then
        local nTempIndex = self.m_lstURItems[self.m_nCurrURItemsIndex + 1];
        local nTempMax = #self.m_lstActions;
        if self.m_lstURItems[self.m_nCurrURItemsIndex + 2] ~= nil then
            nTempMax = self.m_lstURItems[self.m_nCurrURItemsIndex + 2] - 1;
        end
        for nIndex = nTempIndex, nTempMax do
            self.m_lstActions[nIndex]:Redo();
        end
        self.m_nCurrURItemsIndex = self.m_nCurrURItemsIndex + 1;
    end
end

function LUndoRedoManager:UndoRedoItemCount()
    return #self.m_lstURItems;
end

function LUndoRedoManager:GetUndoRedoItemName(nIndex)
    if nIndex > 0 and nIndex <= #self.m_lstURItems then
        local nActionIndex = g_UndoRedoManager.m_lstURItems[nIndex];
        local action = g_UndoRedoManager.m_lstActions[nActionIndex];
        return action.name;
    end
    return "";
end

function LUndoRedoManager:GetCurrentUndoRedoState()
    return self.m_nCurrURItemsIndex;
end

function LUndoRedoManager:MoveToUndoRedoState(nIndex)
    if nIndex >= 0 and nIndex <= #self.m_lstURItems then
        while nIndex > self.m_nCurrURItemsIndex do
            self:Redo();
        end
        while nIndex < self.m_nCurrURItemsIndex do
            self:Undo();
        end
    end
end

_G.g_UndoRedoManager = LUndoRedoManager:new();