// ©2022 Batty Bovine Productions, LLC. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"

#include "Engine/DataAsset.h"

#include "ComboInputAssets.generated.h"


/**
 * Struct that is used as the value for a combo branch. ComboAction is the action to be
 * executed, and NextNode is the node that will be activated next in the sequence.
 */
USTRUCT(BlueprintType)
struct COMBOINPUT_API FComboSequenceAction
{
	GENERATED_BODY()
public:
	// Action to perform when the associated combo sequence node is activated.
	UPROPERTY(BlueprintReadOnly, EditAnywhere)
		TObjectPtr<const class UComboAction> ComboAction;

	// Sequence node to switch to once this action is complete.
	UPROPERTY(BlueprintReadOnly, EditAnywhere)
		TObjectPtr<const class UComboSequenceNode> NextNode;
};

/**
 * An action that can be executed as part of a combo sequence. This is essentially a
 * representation of an attack, and can be sent to the Animation Graph to play an
 * attack animation and the like.
 */
UCLASS(BlueprintType)
class COMBOINPUT_API UComboAction : public UDataAsset
{
	GENERATED_BODY()

public:
	// Human-readable name of this combo action.
	UPROPERTY(BlueprintReadOnly, EditAnywhere)
		FName ActionName;
};

/**
 * This represents a node in the combo graph, with each key in the ComboBranch being
 * an input this node can react to, and each value containing the action to be executed
 * next, and the node to activate after the action is complete.
 */
UCLASS(BlueprintType)
class COMBOINPUT_API UComboSequenceNode : public UDataAsset
{
	GENERATED_BODY()

public:
	UPROPERTY(BlueprintReadOnly, EditAnywhere)
		TMap<const class UComboInputAsset *, struct FComboSequenceAction> ComboBranch;
};

/**
 * This maps a sequence of button inputs from EnhancedInput to a combo action that can
 * be used to execute a sequence of moves. This gets sent from the input buffer subsystem
 * to the player controller's ComboManagerComponent, which executes the associated action
 * in the current ComboSequenceNode.
 */
UCLASS(BlueprintType)
class COMBOINPUT_API UComboInputAsset : public UDataAsset
{
	GENERATED_BODY()

public:
	// Checks if this combo input contains the given action, and only that action, in its
	// action group.
	bool MatchesInputAction(const class UInputAction* Action) const
	{
		return (this->ActionGroup.Num() == 1 && this->ActionGroup.Contains(Action));
	}
	// Checks if this combo input's action group contains all of the given actions, and no
	// others.
	bool MatchesInputActions(TSet<const class UInputAction*> Actions) const
	{
		if (this->ActionGroup.Num() == Actions.Num())
		{
			for (const UInputAction *Action : Actions)
			{
				if (!this->ActionGroup.Contains(Action))
				{
					return false;
				}
			}
			return true;
		}
		return false;
	}
	// Checks if this combo input's action group contains the given action.
	bool ContainsAction(const class UInputAction* Action) const
	{
		return this->ActionGroup.Contains(Action);
	}
	// Checks if this combo input's action group contains any of the given actions.
	bool ContainsOneOf(TSet<const class UInputAction*> Actions) const
	{
		for (const UInputAction *Action : Actions)
		{
			if (this->ActionGroup.Contains(Action))
			{
				return true;
			}
		}
		return false;
	}
	
	// Human-readable name of this combo input.
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		FName ComboInputName;

	// Combined actions that add up to this combo input when activated
	// within a short time of one another. If only one is present, then
	// this combo input asset will simply represent that action.
	UPROPERTY(BlueprintReadWrite, EditAnywhere)
		TSet<const class UInputAction*> ActionGroup;
};
