#pragma once

// Helpers de leitura/escrita binarios LE (UTF-8) compartilhados pelas telas
// do FrontEnd (Boot/Login/ServerSelect/Lobby/CharCreate). Funcoes `inline`
// pra evitar ODR violation quando dois .cpp caem no mesmo unity build do
// UBT — definicoes em anonymous namespace cada um colidiam apos a
// reorganizacao automatica de unities.

#include "CoreMinimal.h"

namespace ZMMOWire
{
	inline bool ReadU8(const TArray<uint8>& Buf, int32& Pos, uint8& Out)
	{
		if (Pos + 1 > Buf.Num()) return false;
		Out = Buf[Pos];
		Pos += 1;
		return true;
	}

	inline bool ReadU16(const TArray<uint8>& Buf, int32& Pos, uint16& Out)
	{
		if (Pos + 2 > Buf.Num()) return false;
		Out = static_cast<uint16>(Buf[Pos]) | (static_cast<uint16>(Buf[Pos + 1]) << 8);
		Pos += 2;
		return true;
	}

	inline bool ReadU32(const TArray<uint8>& Buf, int32& Pos, uint32& Out)
	{
		if (Pos + 4 > Buf.Num()) return false;
		Out = static_cast<uint32>(Buf[Pos])
		    | (static_cast<uint32>(Buf[Pos + 1]) << 8)
		    | (static_cast<uint32>(Buf[Pos + 2]) << 16)
		    | (static_cast<uint32>(Buf[Pos + 3]) << 24);
		Pos += 4;
		return true;
	}

	inline bool ReadU64(const TArray<uint8>& Buf, int32& Pos, uint64& Out)
	{
		if (Pos + 8 > Buf.Num()) return false;
		Out = 0;
		for (int32 i = 0; i < 8; ++i)
		{
			Out |= (static_cast<uint64>(Buf[Pos + i]) << (8 * i));
		}
		Pos += 8;
		return true;
	}

	inline bool ReadFloat(const TArray<uint8>& Buf, int32& Pos, float& Out)
	{
		uint32 Raw = 0;
		if (!ReadU32(Buf, Pos, Raw)) return false;
		FMemory::Memcpy(&Out, &Raw, sizeof(float));
		return true;
	}

	inline bool ReadStringUtf8(const TArray<uint8>& Buf, int32& Pos, FString& Out)
	{
		uint16 Len = 0;
		if (!ReadU16(Buf, Pos, Len)) return false;
		if (Pos + Len > Buf.Num()) return false;
		if (Len == 0) { Out.Reset(); return true; }
		Out = FString(FUTF8ToTCHAR(reinterpret_cast<const ANSICHAR*>(Buf.GetData() + Pos), Len));
		Pos += Len;
		return true;
	}

	/** Le 16 bytes raw e devolve UUID canonico "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx". */
	inline bool ReadUuid16(const TArray<uint8>& Buf, int32& Pos, FString& Out)
	{
		if (Pos + 16 > Buf.Num()) return false;
		auto HexNibble = [](uint8 B, ANSICHAR* Dst)
		{
			static const ANSICHAR* H = "0123456789abcdef";
			Dst[0] = H[(B >> 4) & 0xF];
			Dst[1] = H[B & 0xF];
		};
		ANSICHAR Raw[37]; Raw[36] = 0;
		int32 J = 0;
		for (int32 I = 0; I < 16; ++I)
		{
			if (I == 4 || I == 6 || I == 8 || I == 10) Raw[J++] = '-';
			HexNibble(Buf[Pos + I], &Raw[J]);
			J += 2;
		}
		Out = ANSI_TO_TCHAR(Raw);
		Pos += 16;
		return true;
	}

	inline void WriteUInt8(TArray<uint8>& Buf, uint8 V) { Buf.Add(V); }

	inline void WriteUInt16(TArray<uint8>& Buf, uint16 V)
	{
		Buf.Add(static_cast<uint8>(V & 0xFF));
		Buf.Add(static_cast<uint8>((V >> 8) & 0xFF));
	}

	inline void WriteUInt32(TArray<uint8>& Buf, uint32 V)
	{
		for (int32 I = 0; I < 4; ++I) Buf.Add(static_cast<uint8>((V >> (8 * I)) & 0xFF));
	}

	inline void WriteStringUtf8(TArray<uint8>& Buf, const FString& S)
	{
		FTCHARToUTF8 Conv(*S);
		const int32 Len = Conv.Length();
		WriteUInt16(Buf, static_cast<uint16>(Len));
		Buf.Append(reinterpret_cast<const uint8*>(Conv.Get()), Len);
	}

	/** Aceita UUID canonico (com hifens) e escreve 16 bytes binarios. */
	inline bool WriteUuid16(TArray<uint8>& Buf, const FString& Uuid)
	{
		FString Hex = Uuid.Replace(TEXT("-"), TEXT(""));
		if (Hex.Len() != 32) return false;
		auto HexVal = [](TCHAR C) -> int32
		{
			if (C >= TEXT('0') && C <= TEXT('9')) return C - TEXT('0');
			if (C >= TEXT('a') && C <= TEXT('f')) return C - TEXT('a') + 10;
			if (C >= TEXT('A') && C <= TEXT('F')) return C - TEXT('A') + 10;
			return -1;
		};
		for (int32 I = 0; I < 16; ++I)
		{
			const int32 H = HexVal(Hex[I * 2]);
			const int32 L = HexVal(Hex[I * 2 + 1]);
			if (H < 0 || L < 0) return false;
			Buf.Add(static_cast<uint8>((H << 4) | L));
		}
		return true;
	}
}
