/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/

#include "AnimGraphGroupParameterCommands.h"
#include "AnimGraphParameterCommands.h"
#include "CommandManager.h"
#include <EMotionFX/Source/ActorManager.h>
#include <EMotionFX/Source/AnimGraph.h>
#include <EMotionFX/Source/AnimGraphInstance.h>
#include <EMotionFX/Source/AnimGraphManager.h>
#include <EMotionFX/Source/BlendTreeParameterNode.h>
#include <EMotionFX/Source/Parameter/ParameterFactory.h>
#include <MCore/Source/Random.h>

namespace CommandSystem
{
    //--------------------------------------------------------------------------------
    // CommandAnimGraphAdjustGroupParameter
    //--------------------------------------------------------------------------------
    CommandAnimGraphAdjustGroupParameter::CommandAnimGraphAdjustGroupParameter(MCore::Command* orgCommand)
        : MCore::Command("AnimGraphAdjustGroupParameter", orgCommand)
    {
    }


    CommandAnimGraphAdjustGroupParameter::~CommandAnimGraphAdjustGroupParameter()
    {
    }

    CommandAnimGraphAdjustGroupParameter::Action CommandAnimGraphAdjustGroupParameter::GetAction(const MCore::CommandLine& parameters)
    {
        // Do we want to add new parameters to the group, remove some from it or replace it entirely.
        AZStd::string actionString;
        parameters.GetValue("action", this, actionString);

        if (actionString == "add")
        {
            return ACTION_ADD;
        }
        else if (actionString == "clear" || actionString == "remove")
        {
            return ACTION_REMOVE;
        }

        return ACTION_NONE;
    }


    bool CommandAnimGraphAdjustGroupParameter::Execute(const MCore::CommandLine& parameters, AZStd::string& outResult)
    {
        // Get the parameter name.
        AZStd::string name;
        parameters.GetValue("name", this, name);
        mOldName = name;

        // Find the anim graph by using the id from command parameter.
        const uint32 animGraphID = parameters.GetValueAsInt("animGraphID", this);
        EMotionFX::AnimGraph* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(animGraphID);
        if (!animGraph)
        {
            outResult = AZStd::string::format("Cannot adjust group parameter '%s'. Anim graph id '%d' is not valid.", name.c_str(), animGraphID);
            return false;
        }

        // Find the group parameter index and get a pointer to the group parameter.
        EMotionFX::GroupParameter* groupParameter = animGraph->FindGroupParameterByName(name);
        if (!groupParameter)
        {
            outResult = AZStd::string::format("Cannot adjust group parameter '%s'. group parameter not found.", name.c_str());
            return false;
        }

        // Set the new name.
        AZStd::string newName;
        parameters.GetValue("newName", this, newName);
        if (!newName.empty())
        {
            if (!animGraph->RenameParameter(groupParameter, newName))
            {
                outResult = AZStd::string::format("Cannot adjust group parameter '%s'. Newname already belongs to a different parameter.", name.c_str());
                return false;
            }
        }

        m_oldDescription = groupParameter->GetDescription();
        if (parameters.CheckIfHasParameter("description"))
        {
            groupParameter->SetDescription(parameters.GetValue("description", this));
        }

        if (parameters.CheckIfHasParameter("parameterNames"))
        {
            // Do we want to add new parameters to the group, remove some from it or replace it entirely.
            const Action action = GetAction(parameters);

            // Get the parameter string and tokenize it.
            AZStd::string parametersString;
            parameters.GetValue("parameterNames", this, parametersString);

            AZStd::vector<AZStd::string> parameterNames;
            AzFramework::StringFunc::Tokenize(parametersString.c_str(), parameterNames, ";", false, true);

            const size_t numParameters = parameterNames.size();
            mOldGroupParameterNames.resize(numParameters);

            EMotionFX::ValueParameterVector valueParametersBeforeChange = animGraph->RecursivelyGetValueParameters();

            // Iterate through all parameter names.
            for (size_t i = 0; i < numParameters; ++i)
            {
                const EMotionFX::Parameter* parameter = animGraph->FindParameterByName(parameterNames[i]);
                if (!parameter)
                {
                    mOldGroupParameterNames[i].clear();
                    continue;
                }

                // Save the group parameter (for undo) to which the parameter belonged before command execution.
                const EMotionFX::GroupParameter* parentParameter = animGraph->FindParentGroupParameter(parameter);
                mOldGroupParameterNames[i] = parentParameter ? parentParameter->GetName() : "";

                // Make sure the parameter is not in any other group.
                animGraph->TakeParameterFromParent(parameter);

                // Add the parameter to the desired group.
                // Note: If the group parameter is nullptr, the parameter will move back to the Default group.
                // The remove action will move the parameter back to the default group
                animGraph->AddParameter(const_cast<EMotionFX::Parameter*>(parameter),
                    action == ACTION_ADD ? groupParameter : nullptr);
            }

            EMotionFX::ValueParameterVector valueParametersAfterChange = animGraph->RecursivelyGetValueParameters();

            AZStd::vector<EMotionFX::AnimGraphObject*> affectedObjects;
            animGraph->RecursiveCollectObjectsOfType(azrtti_typeid<EMotionFX::ObjectAffectedByParameterChanges>(), affectedObjects);
            EMotionFX::GetAnimGraphManager().RecursiveCollectObjectsAffectedBy(animGraph, affectedObjects);

            for (EMotionFX::AnimGraphObject* affectedObject : affectedObjects)
            {
                EMotionFX::ObjectAffectedByParameterChanges* affectedObjectByParameterChanges = azdynamic_cast<EMotionFX::ObjectAffectedByParameterChanges*>(affectedObject);
                affectedObjectByParameterChanges->ParameterOrderChanged(valueParametersBeforeChange, valueParametersAfterChange);
            }

            // Update the parameter values for all instances as their type might have changed.
            const size_t numInstances = animGraph->GetNumAnimGraphInstances();
            for (size_t i = 0; i < numInstances; ++i)
            {
                EMotionFX::AnimGraphInstance* animGraphInstance = animGraph->GetAnimGraphInstance(i);
                animGraphInstance->ReInitParameterValues();
            }
        }

        // save the current dirty flag and tell the anim graph that something got changed
        mOldDirtyFlag = animGraph->GetDirtyFlag();
        animGraph->SetDirtyFlag(true);

        animGraph->RecursiveInvalidateUniqueDatas();
        return true;
    }


    bool CommandAnimGraphAdjustGroupParameter::Undo(const MCore::CommandLine& parameters, AZStd::string& outResult)
    {
        // Find the anim graph by using the id from command parameter.
        const uint32 animGraphID = parameters.GetValueAsInt("animGraphID", this);
        EMotionFX::AnimGraph* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(animGraphID);
        if (!animGraph)
        {
            outResult = AZStd::string::format("Cannot undo adjust group parameter. Anim graph id '%d' is not valid.", animGraphID);
            return false;
        }

        MCore::CommandGroup commandGroup;

        // Undo the group name as first step. All commands afterwards have to use mOldName as group name.
        if (parameters.CheckIfHasParameter("newName"))
        {
            AZStd::string newName;
            parameters.GetValue("newName", this, newName);

            const AZStd::string command = AZStd::string::format("AnimGraphAdjustGroupParameter -animGraphID %i -name \"%s\" -newName \"%s\"",
                    animGraph->GetID(), newName.c_str(), mOldName.c_str());

            commandGroup.AddCommandString(command);
        }

        if (parameters.CheckIfHasParameter("description"))
        {
            const AZStd::string command = AZStd::string::format("AnimGraphAdjustGroupParameter -animGraphID %i -name \"%s\" -description \"%s\"",
                animGraph->GetID(), mOldName.c_str(), m_oldDescription.c_str());
            commandGroup.AddCommandString(command);
        }

        if (parameters.CheckIfHasParameter("parameterNames"))
        {
            // Do we want to add new parameters to the group, remove some from it or replace it entirely.
            const Action action = GetAction(parameters);

            AZStd::string parametersString;
            parameters.GetValue("parameterNames", this, parametersString);

            AZStd::vector<AZStd::string> parameterNames;
            AzFramework::StringFunc::Tokenize(parametersString.c_str(), parameterNames, ";", false, true);

            const size_t parameterCount = parameterNames.size();
            AZ_Assert(parameterCount == mOldGroupParameterNames.size(), "The number of parameter names has to match the saved group parameter info for undo.");

            for (size_t i = 0; i < parameterCount; ++i)
            {
                const AZStd::string& parameterName = parameterNames[i];
                const AZStd::string& oldGroupName = mOldGroupParameterNames[i];

                switch (action)
                {
                case ACTION_ADD:
                {
                    if (oldGroupName.empty())
                    {
                        // An empty old group name means that the parameter was in the Default group before, so in this case just remove the parameter from the group.
                        const AZStd::string command = AZStd::string::format("AnimGraphAdjustGroupParameter -animGraphID %i -name \"%s\" -action \"remove\" -parameterNames \"%s\"",
                                animGraph->GetID(), mOldName.c_str(), parameterName.c_str());

                        commandGroup.AddCommandString(command);
                    }
                    else
                    {
                        // Add the parameter to the old group which auto removes it from all other groups.
                        const AZStd::string command = AZStd::string::format("AnimGraphAdjustGroupParameter -animGraphID %i -name \"%s\" -action \"add\" -parameterNames \"%s\"",
                                animGraph->GetID(), oldGroupName.c_str(), parameterName.c_str());

                        commandGroup.AddCommandString(command);
                    }

                    break;
                }

                case ACTION_REMOVE:
                {
                    const AZStd::string command = AZStd::string::format("AnimGraphAdjustGroupParameter -animGraphID %i -name \"%s\" -action \"add\" -parameterNames \"%s\"",
                            animGraph->GetID(), oldGroupName.c_str(), parameterName.c_str());

                    commandGroup.AddCommandString(command);

                    break;
                }
                }
            }
        }

        // Execute the command group.
        AZStd::string result;
        if (!GetCommandManager()->ExecuteCommandGroupInsideCommand(commandGroup, result))
        {
            AZ_Error("EMotionFX", false, result.c_str());
        }

        // Set the dirty flag back to the old value.
        animGraph->SetDirtyFlag(mOldDirtyFlag);
        return true;
    }


    void CommandAnimGraphAdjustGroupParameter::InitSyntax()
    {
        GetSyntax().ReserveParameters(6);
        GetSyntax().AddRequiredParameter("animGraphID", "The id of the blend set the group parameter belongs to.", MCore::CommandSyntax::PARAMTYPE_INT);
        GetSyntax().AddParameter("name",            "The name of the group parameter to adjust.", MCore::CommandSyntax::PARAMTYPE_STRING, "");
        GetSyntax().AddParameter("newName",     "The new name of the group parameter.", MCore::CommandSyntax::PARAMTYPE_STRING, "");
        GetSyntax().AddParameter("parameterNames",  "A list of parameter names that should be added/removed to/from the group parameter.", MCore::CommandSyntax::PARAMTYPE_STRING, "");
        GetSyntax().AddParameter("action",          "The action to perform with the parameters passed to the command.", MCore::CommandSyntax::PARAMTYPE_STRING, "select");
        GetSyntax().AddParameter("description", "The description of the parameter group.", MCore::CommandSyntax::PARAMTYPE_STRING, "");
        GetSyntax().AddParameter("updateUI", "Setting this to true will trigger a refresh of the parameter UI.", MCore::CommandSyntax::PARAMTYPE_BOOLEAN, "true");
    }


    const char* CommandAnimGraphAdjustGroupParameter::GetDescription() const
    {
        return "This command can be used to adjust the group parameters of the given anim graph.";
    }

    //--------------------------------------------------------------------------------
    // CommandAnimGraphAddGroupParameter
    //--------------------------------------------------------------------------------
    CommandAnimGraphAddGroupParameter::CommandAnimGraphAddGroupParameter(MCore::Command* orgCommand)
        : MCore::Command("AnimGraphAddGroupParameter", orgCommand)
    {
    }


    CommandAnimGraphAddGroupParameter::~CommandAnimGraphAddGroupParameter()
    {
    }


    bool CommandAnimGraphAddGroupParameter::Execute(const MCore::CommandLine& parameters, AZStd::string& outResult)
    {
        // Find the anim graph by using the id from command parameter.
        const uint32 animGraphID = parameters.GetValueAsInt("animGraphID", this);
        EMotionFX::AnimGraph* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(animGraphID);
        if (!animGraph)
        {
            outResult = AZStd::string::format("Cannot add group parameter. Anim graph id '%d' is not valid.", animGraphID);
            return false;
        }

        AZStd::string valueString;
        if (parameters.CheckIfHasParameter("name"))
        {
            parameters.GetValue("name", this, &valueString);
        }
        else
        {
            // generate a unique parameter name
            valueString = MCore::GenerateUniqueString("Parameter", [&](const AZStd::string& value)
                    {
                        return (animGraph->FindGroupParameterByName(value) == nullptr);
                    });
        }

        if (animGraph->FindParameterByName(valueString))
        {
            outResult = AZStd::string::format("There is already a parameter with the name '%s', please use a unique name.", valueString.c_str());
            return false;
        }

        // add new group parameter to the anim graph
        EMotionFX::Parameter* groupParameter = EMotionFX::ParameterFactory::Create(azrtti_typeid<EMotionFX::GroupParameter>());
        groupParameter->SetName(valueString);

        const AZStd::string parentName = parameters.GetValue("parent", this);
        const EMotionFX::GroupParameter* parentGroupParameter = animGraph->FindGroupParameterByName(parentName);

        // if the index parameter is specified and valid insert the group parameter at the given position, else just add it to the end
        const uint32 index = parameters.GetValueAsInt("index", this);
        if (index != MCORE_INVALIDINDEX32)
        {
            animGraph->InsertParameter(index, groupParameter, parentGroupParameter);
        }
        else
        {
            animGraph->AddParameter(groupParameter, parentGroupParameter);
        }

        // save the current dirty flag and tell the anim graph that something got changed
        mOldDirtyFlag   = animGraph->GetDirtyFlag();
        mOldName        = groupParameter->GetName();
        animGraph->SetDirtyFlag(true);

        animGraph->RecursiveInvalidateUniqueDatas();
        return true;
    }


    bool CommandAnimGraphAddGroupParameter::Undo(const MCore::CommandLine& parameters, AZStd::string& outResult)
    {
        // Find the anim graph by using the id from command parameter.
        const uint32 animGraphID = parameters.GetValueAsInt("animGraphID", this);
        EMotionFX::AnimGraph* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(animGraphID);
        if (!animGraph)
        {
            outResult = AZStd::string::format("Cannot undo add group parameter. Anim graph id '%d' is not valid.", animGraphID);
            return false;
        }

        // Construct and execute the command.
        const AZStd::string command = AZStd::string::format("AnimGraphRemoveGroupParameter -animGraphID %i -name \"%s\"", animGraphID, mOldName.c_str());

        AZStd::string result;
        if (!GetCommandManager()->ExecuteCommandInsideCommand(command, result))
        {
            AZ_Error("EMotionFX", false, result.c_str());
        }

        // Set the dirty flag back to the old value.
        animGraph->SetDirtyFlag(mOldDirtyFlag);
        return true;
    }


    void CommandAnimGraphAddGroupParameter::InitSyntax()
    {
        GetSyntax().ReserveParameters(5);
        GetSyntax().AddRequiredParameter("animGraphID", "The id of the blend set the group parameter belongs to.", MCore::CommandSyntax::PARAMTYPE_INT);
        GetSyntax().AddParameter("name", "The name of the group parameter.", MCore::CommandSyntax::PARAMTYPE_STRING, "Unnamed group parameter");
        GetSyntax().AddParameter("index", "The index position where the new group parameter should be added to, relative to the parent.", MCore::CommandSyntax::PARAMTYPE_INT, "-1");
        GetSyntax().AddParameter("parent", "The parent group parameter where the new parameter should be added to.", MCore::CommandSyntax::PARAMTYPE_STRING, "");
        GetSyntax().AddParameter("updateUI", "Setting this to true will trigger a refresh of the parameter UI.", MCore::CommandSyntax::PARAMTYPE_BOOLEAN, "true");
    }


    const char* CommandAnimGraphAddGroupParameter::GetDescription() const
    {
        return "This command can be used to add a new group parameter to the given anim graph.";
    }

    //--------------------------------------------------------------------------------
    // CommandAnimGraphRemoveGroupParameter
    //--------------------------------------------------------------------------------
    CommandAnimGraphRemoveGroupParameter::CommandAnimGraphRemoveGroupParameter(MCore::Command* orgCommand)
        : MCore::Command("AnimGraphRemoveGroupParameter", orgCommand)
    {
    }


    CommandAnimGraphRemoveGroupParameter::~CommandAnimGraphRemoveGroupParameter()
    {
    }


    bool CommandAnimGraphRemoveGroupParameter::Execute(const MCore::CommandLine& parameters, AZStd::string& outResult)
    {
        // Get the parameter name.
        AZStd::string name;
        parameters.GetValue("name", this, name);

        // Find the anim graph by using the id from command parameter.
        const uint32 animGraphID = parameters.GetValueAsInt("animGraphID", this);
        EMotionFX::AnimGraph* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(animGraphID);
        if (!animGraph)
        {
            outResult = AZStd::string::format("Cannot remove group parameter. Anim graph id '%d' is not valid.", animGraphID);
            return false;
        }

        // find the parameter and remove it
        const EMotionFX::Parameter* parameter = animGraph->FindParameterByName(name);
        if (!parameter)
        {
            outResult = AZStd::string::format("Cannot remove group parameter from anim graph. Group parameter '%s' was not found.", name.c_str());
            return false;
        }
        if (azrtti_typeid(parameter) != azrtti_typeid<EMotionFX::GroupParameter>())
        {
            outResult = AZStd::string::format("Cannot remove group parameter from anim graph. Parameter '%s' is not a group parameter.", name.c_str());
            return false;
        }

        // read out information for the command undo
        mOldName = parameter->GetName();
        const EMotionFX::GroupParameter* parentGroup = animGraph->FindParentGroupParameter(parameter);
        if (parentGroup)
        {
            mOldParent = parentGroup->GetName();
            mOldIndex = parentGroup->FindParameterIndex(parameter).GetValue();
        }
        else
        {
            mOldParent = "";
            mOldIndex = animGraph->FindParameterIndex(parameter).GetValue();
        }

        mOldParameterNames.clear();

        // Collect all child parameters and move them to the default group. Keep the child hierarchy as it is.
        // Add the immediate child ones to mOldParameterNames so they get moved back on undo
        const EMotionFX::GroupParameter* groupParameter = static_cast<const EMotionFX::GroupParameter*>(parameter);
        const EMotionFX::ParameterVector childParameters = groupParameter->RecursivelyGetChildParameters();
        AZStd::vector<const EMotionFX::GroupParameter*> childParents;

        const size_t childParameterCount = childParameters.size();
        childParents.resize(childParameterCount);

        EMotionFX::ValueParameterVector valueParametersBeforeChange = animGraph->RecursivelyGetValueParameters();

        // childParameters is sorted from root to leaf, first we iterate from leaf to root getting the parents
        // and taking the parameters out of them
        for (int i = static_cast<int>(childParameterCount) - 1; i >= 0; --i)
        {
            childParents[i] = animGraph->FindParentGroupParameter(childParameters[i]);
            animGraph->TakeParameterFromParent(childParameters[i]);
        }

        // Now we iterate root to child and add the parameters to the right parent
        for (size_t i = 0; i < childParameterCount; ++i)
        {
            const EMotionFX::GroupParameter* parent = childParents[i];
            if (parent == groupParameter)
            {
                mOldParameterNames += childParameters[i]->GetName() + ";";
                animGraph->AddParameter(childParameters[i]); // add to default group
            }
            else
            {
                animGraph->AddParameter(childParameters[i], parent); // add to default group
            }
        }
        if (!mOldParameterNames.empty())
        {
            mOldParameterNames.pop_back(); // remove trailing ";"
        }

        // remove the group parameter
        animGraph->RemoveParameter(const_cast<EMotionFX::Parameter*>(parameter));

        EMotionFX::ValueParameterVector valueParametersAfterChange = animGraph->RecursivelyGetValueParameters();

        if (valueParametersBeforeChange != valueParametersAfterChange)
        {
            AZStd::vector<EMotionFX::AnimGraphObject*> affectedObjects;
            animGraph->RecursiveCollectObjectsOfType(azrtti_typeid<EMotionFX::ObjectAffectedByParameterChanges>(), affectedObjects);
            EMotionFX::GetAnimGraphManager().RecursiveCollectObjectsAffectedBy(animGraph, affectedObjects);

            for (EMotionFX::AnimGraphObject* affectedObject : affectedObjects)
            {
                EMotionFX::ObjectAffectedByParameterChanges* affectedObjectByParameterChanges = azdynamic_cast<EMotionFX::ObjectAffectedByParameterChanges*>(affectedObject);
                affectedObjectByParameterChanges->ParameterOrderChanged(valueParametersBeforeChange, valueParametersAfterChange);
            }
        }

        // save the current dirty flag and tell the anim graph that something got changed
        mOldDirtyFlag = animGraph->GetDirtyFlag();
        animGraph->SetDirtyFlag(true);

        animGraph->RecursiveInvalidateUniqueDatas();
        return true;
    }


    bool CommandAnimGraphRemoveGroupParameter::Undo(const MCore::CommandLine& parameters, AZStd::string& outResult)
    {
        // Find the anim graph by using the id from command parameter.
        const uint32 animGraphID = parameters.GetValueAsInt("animGraphID", this);
        EMotionFX::AnimGraph* animGraph = EMotionFX::GetAnimGraphManager().FindAnimGraphByID(animGraphID);
        if (!animGraph)
        {
            outResult = AZStd::string::format("Cannot undo remove group parameter. Anim graph id '%d' is not valid.", animGraphID);
            return false;
        }

        // Construct the command group.
        AZStd::string command;
        MCore::CommandGroup commandGroup;
        const AZStd::string updateWindow = parameters.GetValue("updateUI", this);

        command = AZStd::string::format("AnimGraphAddGroupParameter -animGraphID %i -name \"%s\" -index %d -parent \"%s\" -updateUI %s",
                animGraph->GetID(),
                mOldName.c_str(),
                mOldIndex,
                mOldParent.c_str(),
                updateWindow.c_str());
        commandGroup.AddCommandString(command);

        if (!mOldParameterNames.empty())
        {
            command = AZStd::string::format("AnimGraphAdjustGroupParameter -animGraphID %i -name \"%s\" -parameterNames \"%s\" -action \"add\" -updateUI %s",
                animGraph->GetID(),
                mOldName.c_str(),
                mOldParameterNames.c_str(),
                updateWindow.c_str());
            commandGroup.AddCommandString(command);
        }
        
        // Execute the command group.
        AZStd::string result;
        if (!GetCommandManager()->ExecuteCommandGroupInsideCommand(commandGroup, result))
        {
            AZ_Error("EMotionFX", false, result.c_str());
        }

        // Set the dirty flag back to the old value.
        animGraph->SetDirtyFlag(mOldDirtyFlag);
        return true;
    }


    void CommandAnimGraphRemoveGroupParameter::InitSyntax()
    {
        GetSyntax().ReserveParameters(3);
        GetSyntax().AddRequiredParameter("animGraphID", "The id of the blend set the group parameter belongs to.", MCore::CommandSyntax::PARAMTYPE_INT);
        GetSyntax().AddRequiredParameter("name", "The name of the group parameter to remove.", MCore::CommandSyntax::PARAMTYPE_STRING);
        GetSyntax().AddParameter("updateUI", "Setting this to true will trigger a refresh of the parameter UI.", MCore::CommandSyntax::PARAMTYPE_BOOLEAN, "true");
    }


    const char* CommandAnimGraphRemoveGroupParameter::GetDescription() const
    {
        return "This command can be used to remove a group parameter from the given anim graph.";
    }


    //--------------------------------------------------------------------------------
    // Helper functions
    //--------------------------------------------------------------------------------
    void RemoveGroupParameter(EMotionFX::AnimGraph* animGraph, const EMotionFX::GroupParameter* groupParameter, bool removeParameters, MCore::CommandGroup* commandGroup, bool updateUI)
    {
        // Create the command group and construct the remove group parameter command.
        MCore::CommandGroup internalCommandGroup("Remove group parameter");
        MCore::CommandGroup* commandGroupToUse = commandGroup ? commandGroup : &internalCommandGroup;

        if (removeParameters)
        {
            AZStd::vector<AZStd::string> parameterNamesToBeRemoved;

            // Add all parameters from the group to the array of parameters to be removed.
            const EMotionFX::ValueParameterVector valueParameters = groupParameter->RecursivelyGetChildValueParameters();
            for (const EMotionFX::ValueParameter* valueParameter : valueParameters)
            {
                parameterNamesToBeRemoved.emplace_back(valueParameter->GetName());
            }

            // Construct remove parameter commands for all elements in the array.
            BuildRemoveParametersCommandGroup(animGraph, parameterNamesToBeRemoved, commandGroupToUse);
        }

        const AZStd::string command = AZStd::string::format("AnimGraphRemoveGroupParameter -animGraphID %i -name \"%s\" -updateUI %s", animGraph->GetID(), groupParameter->GetName().c_str(), updateUI ? "true" : "false");
        commandGroupToUse->AddCommandString(command.c_str());

        // Execute the internal command group.
        if (!commandGroup)
        {
            AZStd::string result;
            if (!GetCommandManager()->ExecuteCommandGroup(internalCommandGroup, result))
            {
                AZ_Error("EMotionFX", false, result.c_str());
            }
        }
    }


    void ClearGroupParameters(EMotionFX::AnimGraph* animGraph, MCore::CommandGroup* commandGroup)
    {
        MCore::CommandGroup internalCommandGroup("Clear group parameters");
        MCore::CommandGroup* commandGroupToUse = commandGroup ? commandGroup : &internalCommandGroup;

        // Construct remove group parameter commands for all groups and add them to the command group.
        const EMotionFX::GroupParameterVector parameters = animGraph->RecursivelyGetGroupParameters();
        for (uint32 i = 0; i < parameters.size(); ++i)
        {
            const bool updateUi = (i == 0 || i == parameters.size() - 1);
            RemoveGroupParameter(animGraph, parameters[i], false, commandGroupToUse, updateUi);
        }

        // Execute the command group.
        if (!commandGroup)
        {
            AZStd::string result;
            if (!GetCommandManager()->ExecuteCommandGroup(internalCommandGroup, result))
            {
                AZ_Error("EMotionFX", false, result.c_str());
            }
        }
    }


    void MoveGroupParameterCommand(EMotionFX::AnimGraph* animGraph, uint32 moveFrom, uint32 moveTo)
    {
        // Get the group parameter to move.
        const EMotionFX::Parameter* parameter = animGraph->FindParameter(moveFrom);

        MCore::CommandGroup commandGroup("Move command group");

        // 1: Remove the group parameter that we want to move.
        AZStd::string command;
        command = AZStd::string::format("AnimGraphRemoveGroupParameter -animGraphID %i -name \"%s\"", animGraph->GetID(), parameter->GetName().c_str());
        commandGroup.AddCommandString(command);

        // 2: Add a new group parameter at the desired position.
        command = AZStd::string::format("AnimGraphAddGroupParameter -animGraphID %i -name \"%s\" -index %d", animGraph->GetID(), parameter->GetName().c_str(), moveTo);
        commandGroup.AddCommandString(command);

        // 3: Move all parameters to the new group.
        AZ_Assert(azrtti_typeid(parameter) == azrtti_typeid<EMotionFX::GroupParameter>(), "Expected paramater group");
        AZStd::string parameterNamesString;
        const EMotionFX::ParameterVector childParameters = static_cast<const EMotionFX::GroupParameter*>(parameter)->RecursivelyGetChildParameters();
        for (const EMotionFX::Parameter* childParameter : childParameters)
        {
            parameterNamesString += childParameter->GetName() + ";";
        }
        if (!parameterNamesString.empty())
        {
            parameterNamesString.pop_back();
        }
        command = AZStd::string::format("AnimGraphAdjustGroupParameter -animGraphID %i -name \"%s\" -parameterNames \"%s\" -action \"add\"", animGraph->GetID(), parameter->GetName().c_str(), parameterNamesString.c_str());
        commandGroup.AddCommandString(command);

        // Execute the command group.
        AZStd::string result;
        if (!GetCommandManager()->ExecuteCommandGroup(commandGroup, result))
        {
            AZ_Error("EMotionFX", false, result.c_str());
        }
    }
} // namespace CommandSystem
