#pragma once

#include <memory>
#include <vector>
#include <string>

namespace UsdLayerManager {

/// Pure-virtual interface for all reversible operations.
class ICommand {
public:
    virtual ~ICommand() = default;
    virtual void Execute() = 0;
    virtual void Undo() = 0;
    virtual std::string GetDescription() const = 0;
};

/// Application-level undo/redo stack.
///
/// Push(cmd) executes the command, pushes it onto the undo stack,
/// and clears the redo stack.  Undo() pops from the undo stack, calls
/// Undo() on the command, and pushes it onto the redo stack (and vice
/// versa for Redo()).
///
/// Call Clear() whenever the USD stage is replaced so stale USD object
/// references (UsdPrim, UsdAttribute, SdfLayerHandle) cannot be dereferenced.
class CommandHistory {
public:
    CommandHistory()  = default;
    ~CommandHistory() = default;

    /// Execute cmd and push onto undo stack; clears redo stack.
    void Push(std::unique_ptr<ICommand> cmd);

    /// Undo the top command (no-op if stack is empty).
    void Undo();

    /// Redo the top undone command (no-op if stack is empty).
    void Redo();

    /// Clear both stacks (must be called on stage close/open).
    void Clear();

    bool CanUndo() const { return !m_undoStack.empty(); }
    bool CanRedo() const { return !m_redoStack.empty(); }

    /// Description of the command that Undo() would reverse, or empty string.
    std::string GetUndoDescription() const;

    /// Description of the command that Redo() would replay, or empty string.
    std::string GetRedoDescription() const;

private:
    std::vector<std::unique_ptr<ICommand>> m_undoStack;
    std::vector<std::unique_ptr<ICommand>> m_redoStack;
};

} // namespace UsdLayerManager
