#include "WebSocketConnection.h"
#include "WebSocketsModule.h"
#include "Log/CsdkLogMacros.h"
#include "Log/LogChatSdk.h"
#include "IWebSocket.h"


FWebSocketConnection::~FWebSocketConnection()
{
	OnConnectedEvent.Unbind();
	OnConnectionErrorEvent.Unbind();
	OnCloseEvent.Unbind();
	OnRawMsgRecvEvent.Unbind();
	if(WebSocket)
	{
		WebSocket->Close();
		WebSocket->OnConnected().Clear();
		WebSocket->OnConnectionError().Clear();
		WebSocket->OnClosed().Clear();
		WebSocket->OnRawMessage().Clear();
	}
}

void FWebSocketConnection::CreateWebSocket(const FString& Url, const FString& Protocol,
                                           const TMap<FString, FString>& Headers)
{
	FModuleManager::Get().LoadModuleChecked<FWebSocketsModule>("WebSockets");
	WebSocket = FWebSocketsModule::Get().CreateWebSocket(Url, Protocol, Headers);
	WebSocket->OnConnected()	  .AddRaw(this,&FWebSocketConnection::OnConnected);
	WebSocket->OnConnectionError().AddRaw(this, &FWebSocketConnection::OnConnectionError);
	WebSocket->OnClosed()         .AddRaw(this, &FWebSocketConnection::OnClosed);
	WebSocket->OnRawMessage()     .AddRaw(this, &FWebSocketConnection::OnRawMessage);
}

void FWebSocketConnection::Connect()
{
	if(!bIsConnecting)
	{
		bIsConnecting = true;
		WebSocket->Connect();
	}
}

void FWebSocketConnection::Close() const
{
	if(WebSocket)
	{
		WebSocket->Close();
	}

}

bool FWebSocketConnection::IsConnected() const
{
	return WebSocket && WebSocket->IsConnected();
}

void FWebSocketConnection::SendBinaryMessage(const TArray<uint8>& SendData) const
{
	if (IsConnected())
	{
		WebSocket->Send(SendData.GetData(),SendData.Num(),true);
	}
	else
	{
		CSDK_LOG(LogChatSdk, Error, TEXT("SendMessage() called but the WebSocket is not connected."));
	}	
}

void FWebSocketConnection::OnConnected()
{
	bIsConnecting = false;
	if(OnConnectedEvent.IsBound())
	{
		OnConnectedEvent.Execute();
	}
}

void FWebSocketConnection::OnConnectionError(const FString& Error)
{
	bIsConnecting = false;
	if(OnConnectionErrorEvent.IsBound())
	{
		OnConnectionErrorEvent.Execute(Error);
	}
}

void FWebSocketConnection::OnClosed(int32 Status, const FString& Reason, bool bWasClean) const
{
	if(OnCloseEvent.IsBound())
	{
		OnCloseEvent.Execute(Status,Reason,bWasClean);
	}
}

void FWebSocketConnection::OnRawMessage(const void* Data, SIZE_T Size, SIZE_T BytesRemaining)
{
	const TArray<uint8> ArrayData(static_cast<const uint8*>(Data), Size);
	TransientStorage.Append(ArrayData);
	if(0 == BytesRemaining)
	{
		TArray<uint8> Temp = MoveTemp(TransientStorage);
		TransientStorage.Empty();
		OnRawMsgRecvEvent.ExecuteIfBound(Temp);
	}
}



