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

#include "InworldRuntime/Public/Graph/RuntimeData/InworldGraphRuntimeData_EventHistory.h"

#include "Engine/DataTable.h"
#include "Graph/InworldGraph.h"

THIRD_PARTY_INCLUDES_START
#include "InworldRuntimeLibrary/inworld.h"
THIRD_PARTY_INCLUDES_END

UInworldGraphRuntimeData_EventHistory::UInworldGraphRuntimeData_EventHistory()
{
	Key = "EventHistory";
}

FInworldDataHandle UInworldGraphRuntimeData_EventHistory::GetData()
{
	FInworldData_EventHistory EventHistoryData;
	EventHistoryData.EventHistory = GetEventHistory();
	EventHistoryData.SpeechEvents = GetSpeechEvents();
	return EventHistoryData;
}

void UInworldGraphRuntimeData_EventHistory::Initialize_Implementation()
{
	EventHistory = MakeShared<inworld::vector_Event>();

	if (InitialHistoryTable != nullptr)
	{
		TArray<const FInworldEventHistoryEntry*> Entries;
		InitialHistoryTable->GetAllRows(
			TEXT("UInworldGraphRuntimeData_EventHistory::UInworldGraphRuntimeData_EventHistory"), Entries);

		for (const FInworldEventHistoryEntry* Entry : Entries)
		{
			AddSpeechEvent({Entry->AgentName, Entry->Utterance});
		}
	}
	Super::Initialize_Implementation();
}

void UInworldGraphRuntimeData_EventHistory::AddEvent()
{
	// TODO: Add swig wrapper to perform operation more efficiently
	if (EventHistory->size() == Capacity)
	{
		for (int i{1}; i < EventHistory->size(); ++i)
		{
			EventHistory->set(i - 1, EventHistory->get(i));
		}
		int32 EntrySize{};
		EntrySizes.Dequeue(EntrySize);
		const int32 ToChop{EntrySize == HistoryString.Len() ? EntrySize : EntrySize + Delimiter.Len()};
		HistoryString.RightChopInline(ToChop);
	}
	else
	{
		EventHistory->push_back({});
		SpeechEvents.AddDefaulted();
	}
}

void UInworldGraphRuntimeData_EventHistory::AddGenericEvent(const FString& Event)
{
	if (Capacity == 0)
	{
		return;
	}
	AddEvent();
	inworld::Event event;
	EventHistory->set(EventHistory->size() - 1, event);
	if (HistoryString.Len() > 0)
	{
		HistoryString.Append(Delimiter);
	}
	HistoryString.Append(Event);
	EntrySizes.Enqueue(Event.Len());
}

void UInworldGraphRuntimeData_EventHistory::AddSpeechEvent(const FInworldEventSpeech& Speech)
{
	if (Capacity == 0)
	{
		return;
	}
	AddEvent();
	inworld::Event event;
	event.speech()->agent_name(TCHAR_TO_UTF8(*Speech.AgentName));
	event.speech()->utterance(TCHAR_TO_UTF8(*Speech.Utterance));
	EventHistory->set(EventHistory->size() - 1, event);
	SpeechEvents.Last() = Speech;
	if (HistoryString.Len() > 0)
	{
		HistoryString.Append(Delimiter);
	}
	const FStringFormatNamedArguments NamedArguments{{TEXT("AgentName"), Speech.AgentName}, {TEXT("Utterance"), Speech.Utterance}};
	const FString StringifiedSpeechEvent{FString::Format(*SpeechEventFormatter, NamedArguments)};
	HistoryString.Append(StringifiedSpeechEvent);
	EntrySizes.Enqueue(StringifiedSpeechEvent.Len());
}

void UInworldGraphRuntimeData_EventHistory::Clear()
{
	EventHistory.Reset();
	EventHistory = MakeShared<inworld::vector_Event>();
	SpeechEvents.Empty();
	EntrySizes.Empty();
	HistoryString = {};
}

void UInworldGraphRuntimeData_EventHistory::DebugDumpEventHistory()
{
	UE_LOG(LogInworldGraph, Log, TEXT("EventHistory Dump:"));
	if (EventHistory.IsValid())
	{
		for (int i = 0; i < EventHistory->size(); ++i)
		{
			inworld::Event evt = EventHistory->get(i);
			if (evt.speech())
			{
				FString AgentName = UTF8_TO_TCHAR(evt.speech()->agent_name().c_str());
				FString Utterance = UTF8_TO_TCHAR(evt.speech()->utterance().c_str());

				UE_LOG(LogInworldGraph, Log, TEXT("---> EventHistory[%d] - Utterance: AgentName: %s, Utterance: %s"), i, *AgentName,
					*Utterance);
			}
			else if (evt.action())
			{
				FString AgentName = UTF8_TO_TCHAR(evt.action()->agent_name().c_str());
				UE_LOG(LogInworldGraph, Log, TEXT("---> EventHistory[%d] - Action: AgentName: %s"), i, *AgentName);
			}
		}
	}
	else
	{
		UE_LOG(LogInworldGraph, Warning, TEXT("---> EventHistory is invalid."));
	}
}
