//======================================================================================================
// Company Name:       MotionTechnologies.inc
// Copyright Year:     2025
// File:               MoALiveLinkSource.h
//======================================================================================================

#pragma once

#include "CoreMinimal.h"
#include "ILiveLinkSource.h"
#include "HAL/Runnable.h"
#include "HAL/ThreadSafeBool.h"
#include "HAL/RunnableThread.h"
#include "Async/Async.h"

#include "IMessageContext.h"
#include "Interfaces/IPv4/IPv4Endpoint.h"
#include "Sockets.h"
#include "SocketSubsystem.h"

class FRunnableThread;
class FSocket;
class ILiveLinkClient;
class ISocketSubsystem;

class MOALIVELINK_API MoALiveLinkSource : public ILiveLinkSource, public FRunnable
{
public:
	MoALiveLinkSource(FIPv4Endpoint Endpoint);
	virtual ~MoALiveLinkSource();

	virtual void ReceiveClient(ILiveLinkClient* InClient, FGuid InSourceGuid) override;
	virtual bool IsSourceStillValid() const override;
	virtual bool RequestSourceShutdown() override;
	virtual FText GetSourceType() const override { return SourceType; }
	virtual FText GetSourceMachineName() const override { return SourceMachineName; }
	virtual FText GetSourceStatus() const override { return SourceStatus; }
	
	/** Get the endpoint this source is connected to */
	FIPv4Endpoint GetEndpoint() const { return DeviceEndpoint; }
	
	/** Get connection string for this source */
	FString GetConnectionString() const { return DeviceEndpoint.ToString(); }

	virtual bool Init() override { return true; }
	virtual uint32 Run() override;
	void Start();
	virtual void Stop() override;
	virtual void Exit() override {}

	void ProcessReceivedData(TSharedPtr<TArray<uint8>, ESPMode::ThreadSafe> ReceivedData);
	void ProcessCharacterData(const uint8* DataPtr, int32 DataSize, int32 Offset);
	void ProcessPropData(const uint8* DataPtr, int32 DataSize, int32 Offset);
	void InitStaticData(int32 NumBones, FName SubjectName);
	void InitPropStaticData(FName SubjectName, int32 PropType);
	FString ParseString(const uint8* DataPtr, int32& offset, int32 DataSize);
	void CleanupInactiveSubjects();

private:
	ILiveLinkClient* Client;
	FGuid SourceGuid;
	FMessageAddress ConnectionAddress;
	FIPv4Endpoint DeviceEndpoint;
	FText SourceType;
	FText SourceMachineName;
	FText SourceStatus;
	FSocket* Socket;
	ISocketSubsystem* SocketSubsystem;
	FThreadSafeBool Stopping;
	FRunnableThread* Thread;
	FString ThreadName;
	FTimespan WaitTime;
	TSet<FName> EncounterdSubjects;
	TMap<FName, FDateTime> LastReceivedTimes;
	FDateTime LastCleanupCheck;
	TArray<uint8> RecvBuffer;

	TArray<FString> SkeletalBoneNames = {
		"Hips",
		"LeftHip",
		"LeftKnee",
		"LeftAnkle",
		"LeftToes",
		"RightHip",
		"RightKnee",
		"RightAnkle",
		"RightToes",
		"Chest",
		"Chest1",
		"Chest2",
		"LeftCollar",
		"LeftShoulder",
		"LeftElbow",
		"LeftWrist",
		"Neck",
		"Neck1",
		"RightCollar",
		"RightShoulder",
		"RightElbow",
		"RightWrist",
		"LeftFinger0",
		"LeftFinger01",
		"LeftFinger02",
		"LeftFinger1",
		"LeftFinger11",
		"LeftFinger12",
		"LeftFinger2",
		"LeftFinger21",
		"LeftFinger22",
		"LeftFinger3",
		"LeftFinger31",
		"LeftFinger32",
		"LeftFinger4",
		"LeftFinger41",
		"LeftFinger42",
		"RightFinger0",
		"RightFinger01",
		"RightFinger02",
		"RightFinger1",
		"RightFinger11",
		"RightFinger12",
		"RightFinger2",
		"RightFinger21",
		"RightFinger22",
		"RightFinger3",
		"RightFinger31",
		"RightFinger32",
		"RightFinger4",
		"RightFinger41",
		"RightFinger42"
	};
};