/**
 * Copyright 2022-2025 Theai, Inc. dba Inworld AI
 */

#pragma once

#include "CoreMinimal.h"
#include "Graph/Nodes/InworldNode_Custom.h"

#include "InworldNode_TextProcessing.generated.h"

/**
 * @class UInworldNode_TextProcessing
 * @brief Abstract base class for other text processing nodes.
 */
UCLASS(Abstract)
class INWORLDRUNTIME_API UInworldNode_TextProcessing : public UInworldNode_Custom
{
	GENERATED_BODY()

public:
	UInworldNode_TextProcessing();

protected:
	UFUNCTION()
	virtual UPARAM(meta = (DisplayName = "Text Stream")) FInworldData_DataStream_String
		Process_Default(const UInworldProcessContext* ProcessContext, const FInworldData_DataStream_String& TextStream) const;

	virtual FInworldData_DataStream_String Process_DataStream_String(
		const UInworldProcessContext* ProcessContext, const FInworldData_DataStream_String& Input) const
		PURE_VIRTUAL(UInworldNode_TextProcessing::Process_DataStream_String, return {};);
};

/**
 * @class UInworldNode_GeneralTextProcessor
 * @brief Performs the following (optional) text processing operations on the data stream:
 *
 * Text-in-Brackets removal - Some LLMs can generate actions descriptions inside a dialog. For example, "*She made a deep breath*
 * Hello!" This can be a problem when trying to generate speech from the text. This option removes text in brackets. Supports the
 * following brackets: "(...)", "[...]", "{...}", "<...>", "*...*".
 *
 * Substring removal - This option is used to remove certain substrings from the stream. Enables removal of substrings that are
 * split across several chunks. In case of overlapping substrings, the class will remove the first one found.
 *
 * Emoji removal - this option removes emoji characters from the stream.
 *
 * Sentence processing - intelligently splits text into sentence-like chunks while preserving natural language structure.
 * Splitting Rules:
 * 1. Punctuation-based Splits:
 *    - Splits on basic punctuation marks (., !, ?, :, ;, ,)
 *    - Comma-separated chunks shorter than 80 characters are kept together
 *    - Preserves trailing spaces by moving them to the next chunk
 *
 * 2. Special Cases (No Split):
 *    - Honorifics (e.g., Mr., Mrs., Dr., Prof.)
 *    - Common abbreviations (e.g., U.S.A., A.I., Inc.)
 *    - Domain names (e.g., example.com)
 *    - Version numbers (e.g., 1.2.3)
 *    - Time formats (e.g., 7:00 a.m.)
 *    - Author initials (e.g., A. A. Milne)
 *
 * 3. Newline Handling:
 *    - Always splits on newline characters
 *    - Preserves multiple consecutive newlines
 *    - Strips leading whitespace from new chunks
 *
 * 4. Numbered List Detection:
 *    - Recognizes and splits on numbered list items (1. through 10.)
 *    - Maintains list formatting and structure
 *    - Only splits if numbers are sequential
 *
 * 5. Capital Letter Detection:
 *    - Splits when a space is followed by a capital letter after punctuation
 *    - Helps maintain natural sentence boundaries
 *
 * @input FInworldData_DataStream_String
 * @output FInworldData_DataStream_String
 */
UCLASS()
class INWORLDRUNTIME_API UInworldNode_GeneralTextProcessor : public UInworldNode_TextProcessing
{
	GENERATED_BODY()

public:
	UInworldNode_GeneralTextProcessor();

	/** Runtime data key for accessing player profile information for personalized text processing */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Inworld|Graph",
		Meta = (ToolTip = "Runtime data key for accessing player profile information for personalized text processing"))
	FName PlayerProfileRuntimeDataKey{"PlayerProfile"};

protected:
	virtual FInworldData_DataStream_String Process_DataStream_String(
		const UInworldProcessContext* ProcessContext, const FInworldData_DataStream_String& Input) const override;

public:
	/** Whether to remove text enclosed in brackets like (...), [...], {...}, <...>, *...* */
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Inworld",
		Meta = (ToolTip = "Whether to remove text enclosed in brackets like (...), [...], {...}, <...>, *...*"))
	bool bRemoveTextInBrackets{true};

	/** Whether to remove specified substrings from the text stream */
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Inworld",
		Meta = (ToolTip = "Whether to remove specified substrings from the text stream"))
	bool bRemoveSubstrings{true};

	/** Whether to remove emoji characters from the text stream */
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Inworld",
		Meta = (ToolTip = "Whether to remove emoji characters from the text stream"))
	bool bRemoveEmojis{true};

	/** List of specific strings to remove from the input text */
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Inworld",
		Meta = (EditCondition = "bRemoveSubstrings", ToolTip = "These strings will be removed from the input string"))
	TArray<FString> SubstringsToRemove;

	/** Whether to automatically remove the player's name from text when available in runtime data */
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Inworld",
		Meta = (EditCondition = "bRemoveSubstrings",
			ToolTip = "Whether to automatically remove the player's name from text when available in runtime data"))
	bool bRemovePlayerName{true};

	/** Whether to intelligently split text into sentence-like chunks while preserving natural language structure */
	UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = "Inworld",
		Meta =
			(ToolTip = "Whether to intelligently split text into sentence-like chunks while preserving natural language structure"))
	bool bProcessAsSentenceStream{false};

protected:
	/**
	 * @brief Retrieves the complete list of substrings to remove from text processing.
	 *
	 * This function combines the configured SubstringsToRemove array with dynamically
	 * determined substrings (such as player name from runtime data) to create the
	 * complete list of text patterns to remove during processing.
	 *
	 * @param ProcessContext The process context containing runtime data
	 * @return Vector of strings to be removed from the text stream
	 */
	virtual inworld::vector_string GetSubstringsToRemove(const UInworldProcessContext* ProcessContext) const;
};
