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

#include "InworldRuntimeUtils.h"

#include "InworldRuntimeSettings.h"
#include "Misc/Base64.h"
#include "Serialization/MemoryReader.h"
#include "Serialization/ObjectAndNameAsStringProxyArchive.h"
#include "Serialization/ObjectReader.h"
#include "Serialization/ObjectWriter.h"

THIRD_PARTY_INCLUDES_START
#include "InworldRuntimeLibrary/inworld.h"

#include <unordered_map>
THIRD_PARTY_INCLUDES_END

inworld::Device FInworldRuntimeUtils::GetDevice(inworld::DeviceType device_type)
{
	inworld::vector_Device devices{inworld::DeviceRegistry::GetInstance().GetAvailableDevices()};

	for (int i = 0; i < devices.size(); ++i)
	{
		inworld::Device device = devices.get(i);
		if (device.type() == device_type)
		{
			return device;
		}
	}

	return devices.get(0);
}

std::string FInworldRuntimeUtils::PhonemeToViseme(const std::string& Phoneme)
{
	static const std::unordered_map<std::string, std::string> PhonemeToVisemeMap_ = {
		// Consonants
		{"b", "PP"},
		{"d", "DD"},
		{"d\u0361\u0292", "CH"},
		{"\xF0", "TH"},
		{"f", "FF"},
		{"\u0261", "Kk"},
		{"h", "Kk"},
		{"j", "I"},
		{"k", "Kk"},
		{"", "Nn"},
		{"m", "PP"},
		{"n", "Nn"},
		{"\u014B", "Kk"},
		{"p", "PP"},
		{"\u0279", "RR"},
		{"s", "SS"},
		{"\u0283", "CH"},
		{"t", "DD"},
		{"t\u0361\u0283", "CH"},
		{"\u03B8", "TH"},
		{"v", "FF"},
		{"w", "U"},
		{"z", "SS"},
		{"\u0292", "CH"},
		// Vowels
		{"\u0259", "E"},
		{"\u025A", "E"},
		{"\xE6", "Aa"},
		{"a\u026A", "Aa"},
		{"a\u028A", "Aa"},
		{"\u0251", "Aa"},
		{"e\u026A", "E"},
		{"\u025D", "E"},
		{"\u025B", "E"},
		{"i", "I"},
		{"\u026A", "I"},
		{"o\u028A", "O"},
		{"\u0254", "O"},
		{"\u0254\u026A", "O"},
		{"u", "U"},
		{"\u028A", "U"},
		{"\u028C", "E"},
		// Additional Symbols
		{"\u02C8", "STOP"},
		{"\u02CC", "STOP"},
		{".", "STOP"},
	};
	if (PhonemeToVisemeMap_.find(Phoneme) != PhonemeToVisemeMap_.end())
	{
		return PhonemeToVisemeMap_.at(Phoneme);
	}
	return "";
}

FString FInworldRuntimeUtils::SerializeInstance(UObject* Instance)
{
	TArray<uint8> InstanceBinaryData;
	FMemoryWriter MemoryWriter(InstanceBinaryData, true);
	FObjectAndNameAsStringProxyArchive Ar(MemoryWriter, false);
	Instance->Serialize(Ar);
	return {FBase64::Encode(InstanceBinaryData)};
}

void FInworldRuntimeUtils::DeserializeInstance(UObject* Instance, const FString& InstanceData)
{
	TArray<uint8> InstanceBinaryData;
	FBase64::Decode(InstanceData, InstanceBinaryData);
	FMemoryReader MemoryReader(InstanceBinaryData, true);
	FObjectAndNameAsStringProxyArchive Ar(MemoryReader, true);
	Instance->Serialize(Ar);
}

const FString& GetInworldPluginVersion()
{
	return IPluginManager::Get().FindPlugin("InworldRuntime")->GetDescriptor().VersionName;
}

const FString& GetInworldRuntimeApiKey()
{
	return GetDefault<UInworldRuntimeSettings>()->RuntimeApiKey;
}
