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

#pragma once

#include "CoreMinimal.h"

#include "Decorators/ComboActionDecoratorBase.h"

#include "ComboActionGraphNode.generated.h"

DECLARE_LOG_CATEGORY_EXTERN(LogComboActionGraphNode, Log, All);


/**
 * Combo Action graph Node abstract base class.
 * 
 * Provides generic functionality to be enhanced or overriden by Child Classes.
 * Does come with ability to define Colours, Name, Description and Title.
 * Contains information about Parent and Children Nodes.
 */
UCLASS(Abstract, BlueprintType, ClassGroup=("Combo Input|Action"), AutoExpandCategories=("Combo Input", "Action", "Combo Input|Action"), HideCategories=("Private"))
class COMBOINPUT_API UComboActionGraphNode : public UObject
{
	GENERATED_BODY()

public:
	UComboActionGraphNode();

#pragma region Variables

#pragma region ReadOnly
public:
	/**
	 * Array of parent nodes for the current active node in the combo string.
	 *❗ Parent nodes are nodes that have a directed edge pointing to the current active node.
	 */
	UPROPERTY(BlueprintReadOnly, Category="Private")
		TArray<class UComboActionGraphNode*> ParentNodes;
	/**
	 * The array of child nodes of the current dialogue node.
	 */
	UPROPERTY(BlueprintReadOnly, Category="Private")
		TArray<class UComboActionGraphNode*> ChildrenNodes;
	/**
	 * Map of edges connecting this Node in the Combo Action graph.
	 *❗ The key of the map is the source node, and the value is the edge connecting it to its target node.
	 *❔ Can be used to traverse the graph, get information about node connections...
	 */
	UPROPERTY(BlueprintReadOnly, Category="Private")
		TMap<class UComboActionGraphNode*, class UComboActionGraphEdge*> Edges;
	/**
	 * Pointer to the parent dialogue graph of this node.
	 */
	UPROPERTY(BlueprintReadOnly, Category="Private", meta=(DisplayThumbnail=false))
		TObjectPtr<class UComboActionGraph> Graph;
	/**
	 * Temporary NodeIndex.
	 * This variable will be deleted.
	 */
	UPROPERTY(BlueprintReadOnly, Category="Private")
		int32 NodeIndex = INDEX_NONE;


protected:
	/**
	 * The unique identifier for this Dialogue Graph Node.
	 *❗ This is used to differentiate between nodes, and must be unique within the graph.
	 *❔ Can be used for debugging and tracing purposes.
	 */
	UPROPERTY(BlueprintReadOnly, Category="Private")
		FGuid NodeGUID;

	/**
	 * The world that owns this Dialogue Graph Node.
	 *❗ This is the world in which this Dialogue Graph Node is currently running.
	 *❔ Can be used for accessing world-related functionality.
	 */
	UPROPERTY(BlueprintReadOnly, Category="Private")
		TObjectPtr<UWorld> OwningWorld;

#pragma endregion

#pragma region Editable
public:
	/**
	 * The array of allowed input classes for this Dialogue Node.
	 *❗ Only nodes with classes from this array can be connected as inputs to this node.
	 *❔ Can be used to restrict the types of inputs this node can accept.
	 */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Combo Input|Action")
		TArray<TSubclassOf<class UComboActionGraphNode>> AllowedInputClasses;

	/**
	 * The maximum number of children nodes that this node can have.
	 *❗ If this value is -1, then there is no limit on the number of children nodes.
	 *❔ Can be used to enforce a maximum number of connections for certain types of nodes.
	 */
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category="Combo Input|Action")
		int32 MaxChildrenNodes = -1;

#pragma endregion

#pragma endregion 

#pragma region Functions

public:
	/**
	 * Initializes the node with the given world.
	 *
	 * @param InWorld The world to use for initialization.
	 */
	UFUNCTION(BlueprintNativeEvent, Category = "Combo Input|Action")
		void InitializeNode(UWorld *InWorld);
		virtual void InitializeNode_Implementation(UWorld *InWorld);

	virtual void PreProcessNode(const TScriptInterface<class IComboActionGraphManagerInterface> &Manager){}
	virtual void ProcessNode(const TScriptInterface<class IComboActionGraphManagerInterface> &Manager);
	/**
	 * Returns how many Children Nodes this Node allows to have.
	 *❔ -1 means no limits.
	 *
	 * @return MaxChildrenNodes
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category="Combo Input|Action")
	int32 GetMaxChildNodes() const { return MaxChildrenNodes; }


	/**
	 * Gets the index of the node within the dialogue graph.
	 *
	 * @return The index of the node.
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category="Combo Input|Action")
		FORCEINLINE int32 GetNodeIndex() const { return NodeIndex; }
	/**
	 * Sets the index of this dialogue node in the dialogue graph.
	 *
	 * @param NewIndex The new index to set.
	 */
	void SetNodeIndex(const int32 NewIndex);

	/**
	 * Gets the GUID of the node.
	 *
	 * @return The GUID of the node.
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category="Combo Input|Action")
		FORCEINLINE FGuid GetNodeGUID() const { return NodeGUID; }

	/**
	 * Gets the owning Graph of the node.
	 *❗ Might be null
	 *
	 * @return The owning Graph of the node.
	 */
	UFUNCTION(BlueprintCallable, Category = "Combo Input|Action")
		class UComboActionGraph *GetGraph() const { return this->Graph; }

	
	/**
	 * Gets children Nodes this one has,
	 *❗ Might be empty
	 *
	 * @return Amount of children Nodes
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category="Combo Input|Action")
		FORCEINLINE TArray<UComboActionGraphNode*> GetChildrenNodes() const { return ChildrenNodes; }
	/**
	 * Gets how many parent Nodes point to this one
	 *❗ Might be empty
	 *
	 * @return Amount of how parent Nodes
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure, Category="Combo Input|Action")
		FORCEINLINE TArray<UComboActionGraphNode*> GetParentNodes() const { return ParentNodes; }

	/**
	 * Serves purpose of validating Node before Dialogue gets Started.
	 * Any broken Node results in non-starting Dialogue to avoid crashes.
	 */
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category="Combo Input|Action")
		bool ValidateNodeRuntime() const;
		virtual bool ValidateNodeRuntime_Implementation() const { return true; }
	
public:

	FORCEINLINE ULevel* GetLevel() const { return GetTypedOuter<ULevel>(); }

	/**
	 * Provides a way to update Node's owning World.
	 * Useful for Loading sub-levels.
	 */
	UFUNCTION(BlueprintCallable, Category="Combo Input|Action")
		virtual void SetNewWorld(UWorld* NewWorld);
		virtual UWorld* GetWorld() const override
		{
			if (OwningWorld) return OwningWorld;
		
			// CDO objects do not belong to a world
			// If the actors outer is destroyed or unreachable we are shutting down and the world should be nullptr
			if (
				!HasAnyFlags(RF_ClassDefaultObject) && ensureMsgf(GetOuter(), TEXT("Actor: %s has a null OuterPrivate in AActor::GetWorld()"), *GetFullName())
				&& !GetOuter()->HasAnyFlags(RF_BeginDestroyed) && !GetOuter()->IsUnreachable()
				)
			{
				if (ULevel* Level = GetLevel())
				{
					return Level->OwningWorld;
				}
			}
			return nullptr;
		}

#pragma endregion 


#if WITH_EDITORONLY_DATA

	// Defines whether this Node type allows inputs
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		bool bAllowInputNodes;

	// Defines whether this Node type allows outputs
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		bool bAllowOutputNodes;

	// Defines whether this Node can be copied
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		bool bAllowCopy;

	// Defines whether this Node can be cut
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		bool bAllowCut;

	// Defines whether this Node can be pasted
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		bool bAllowPaste;

	// Defines whether this Node can be deleted
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		bool bAllowDelete;

	// Defines whether this Node can be manually created
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		bool bAllowManualCreate;

	// Display title of the Node
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		FText NodeTitle;

	// Display name of the Node menu category
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		FText ContextMenuName;

	// List of compatible graph types
	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Category = "Editor")
		TSubclassOf<UObject> CompatibleGraphType;

	// Defines background colour of this Node
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		FLinearColor BackgroundColor;

	// Contains Node Tooltip text
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		FText NodeTooltipText;

	// User friendly node type name
	UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Editor")
		FText NodeTypeName;

#endif

#if WITH_EDITOR

	/**
	 * Returns the tooltip text for this graph node.
	 *
	 * @return The tooltip text for this node.
	 */
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Combo Input|Action", meta=(DevelopmentOnly=true))
		FText GetNodeTooltipText() const;
		virtual FText GetNodeTooltipText_Implementation() const;

	/**
	 * Returns the Title text for this graph node.
	 *
	 * @return The Title text for this node.
	 */
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Combo Input|Action", meta=(DevelopmentOnly=true))
		FText GetNodeTitle() const;
		virtual FText GetNodeTitle_Implementation() const { return this->NodeTitle; }

	/**
	 * Returns the Description text for this graph node.
	 *
	 * @return The Description text for this node.
	 */
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Combo Input|Action", meta=(DevelopmentOnly=true))
		FText GetDescription() const;
		virtual FText GetDescription_Implementation() const;

	/**
	 * Returns the Node Category text for this graph node.
	 *
	 * @return The Node Category text for this node.
	 */
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Combo Input|Action", meta=(DevelopmentOnly=true))
		FText GetNodeCategory() const;
		virtual FText GetNodeCategory_Implementation() const;

	/**
	 * Returns the Documentation Link for this graph node.
	 *
	 * @return The Documentation Link for this node.
	 */
	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Combo Input|Action", meta=(DevelopmentOnly=true))
		FString GetNodeDocumentationLink() const;
		virtual FString GetNodeDocumentationLink_Implementation() const;

	/**
	 * Returns the Background Colour for this graph node.
	 *
	 * @return The Background Colour for this node.
	 */
	virtual FLinearColor GetBackgroundColor() const { return this->BackgroundColor; }
	FText GetInternalName() const { return this->NodeTypeName; }

	// Allows setting up the Node Title
	virtual void SetNodeTitle(const FText &NewTitle) { this->NodeTitle = NewTitle; }

	// Allows advanced filtering if Node can be connected from other Node
	virtual bool CanCreateConnection(UComboActionGraphNode *Other, enum EEdGraphPinDirection Direction, FText& ErrorMessage);

	// Validation function responsible for generating user friendly validation messages
	virtual bool ValidateNode(TArray<FText> &ValidationsMessages, const bool RichFormat);

	// Once Node is pasted, this function is called
	virtual void OnPasted();

	// Generates default Tooltip body text used for all Nodes
	UFUNCTION(BlueprintPure, Category="Combo Input|Action", meta=(DevelopmentOnly=true))
		FText GetDefaultTooltipBody() const;
		virtual void OnCreatedInEditor() {};

#endif

};
