#include "HyperTwistRecognition/HyperTwistVisionClient.h"

#include "HyperTwistBootstrap/HyperTwistContractLibrary.h"

namespace HyperTwistMockVisionClientInternal
{
	void ResolveMockClassicFaceSwatch(
		const FString& FaceId,
		FString& OutColorId,
		FString& OutNotation,
		FIntVector& OutSampleBgr
	)
	{
		const FString NormalizedFaceId = FaceId.ToUpper();
		if (NormalizedFaceId == TEXT("U"))
		{
			OutColorId = TEXT("white");
			OutNotation = TEXT("U");
			OutSampleBgr = FIntVector(255, 255, 255);
			return;
		}
		if (NormalizedFaceId == TEXT("R"))
		{
			OutColorId = TEXT("red");
			OutNotation = TEXT("R");
			OutSampleBgr = FIntVector(0, 0, 255);
			return;
		}
		if (NormalizedFaceId == TEXT("D"))
		{
			OutColorId = TEXT("yellow");
			OutNotation = TEXT("D");
			OutSampleBgr = FIntVector(0, 255, 255);
			return;
		}
		if (NormalizedFaceId == TEXT("L"))
		{
			OutColorId = TEXT("orange");
			OutNotation = TEXT("L");
			OutSampleBgr = FIntVector(0, 165, 255);
			return;
		}
		if (NormalizedFaceId == TEXT("B"))
		{
			OutColorId = TEXT("blue");
			OutNotation = TEXT("B");
			OutSampleBgr = FIntVector(255, 0, 0);
			return;
		}

		OutColorId = TEXT("green");
		OutNotation = TEXT("F");
		OutSampleBgr = FIntVector(0, 255, 0);
	}

	void ApplyMockClassicFaceToObservation(
		FHyperTwistVisionFaceObservation& Observation,
		const FString& FaceId
	)
	{
		Observation.FaceId = FaceId;

		FString ColorId;
		FString Notation;
		FIntVector SampleBgr = FIntVector::ZeroValue;
		ResolveMockClassicFaceSwatch(FaceId, ColorId, Notation, SampleBgr);

		for (FHyperTwistVisionObservedSticker& Sticker : Observation.OrderedStickers)
		{
			Sticker.ColorId = ColorId;
			Sticker.Notation = Notation;
			Sticker.SampleBgr = SampleBgr;
		}
	}
}

bool UHyperTwistMockVisionClient::OpenVisionSession(const FHyperTwistVisionSessionConfig& Config, FString& OutError)
{
	OutError.Reset();

	if (Config.SessionId.IsEmpty())
	{
		OutError = TEXT("session-id-required");
		return false;
	}

	SessionConfigs.Add(Config.SessionId, Config);
	SessionFrameCounts.Add(Config.SessionId, 0);
	return true;
}

FHyperTwistVisionPreviewResult UHyperTwistMockVisionClient::SubmitVisionFrame(const FHyperTwistVisionFrameEnvelope& Frame)
{
	FHyperTwistVisionPreviewResult Result = UHyperTwistContractLibrary::MakeMockVisionPreviewResult();
	Result.SessionId = Frame.SessionId;
	Result.FrameId = Frame.FrameId;
	Result.PreviewState.State.SourceSessionId = Frame.SessionId;

	const int32 NextFrameCount = SessionFrameCounts.FindRef(Frame.SessionId) + 1;
	SessionFrameCounts.Add(Frame.SessionId, NextFrameCount);
	HyperTwistMockVisionClientInternal::ApplyMockClassicFaceToObservation(
		Result.ObservedFace,
		!Frame.TargetFaceHint.IsEmpty() ? Frame.TargetFaceHint : Result.ObservedFace.FaceId
	);
	Result.ObservedFace.ObservationId = Result.FrameId.IsEmpty()
		? TEXT("observation_preview")
		: FString::Printf(TEXT("%s/preview"), *Result.FrameId);
	Result.ObservedFace.bCommitReady = NextFrameCount >= 3;
	Result.Guidance = Result.ObservedFace.bCommitReady ? TEXT("commit-ready") : TEXT("keep-scanning");

	return Result;
}

FHyperTwistVisionCommitResult UHyperTwistMockVisionClient::CommitVisionObservation(const FHyperTwistVisionCommitRequest& Request)
{
	FHyperTwistVisionCommitResult Result = UHyperTwistContractLibrary::MakeMockVisionCommitResult();
	Result.SessionId = Request.SessionId;
	Result.CommitKind = Request.CommitKind;
	Result.CommittedUnit = !Request.TargetFace.IsEmpty() ? Request.TargetFace : Request.TargetStage;
	Result.Snapshot.State.SourceSessionId = Request.SessionId;
	HyperTwistMockVisionClientInternal::ApplyMockClassicFaceToObservation(
		Result.ObservedFace,
		!Request.TargetFace.IsEmpty() ? Request.TargetFace : Result.ObservedFace.FaceId
	);
	Result.ObservedFace.ObservationId = Request.SessionId.IsEmpty()
		? TEXT("observation_commit")
		: FString::Printf(TEXT("%s/commit"), *Request.SessionId);
	Result.ObservedFace.bCommitReady = true;

	if (!SessionConfigs.Contains(Request.SessionId))
	{
		Result.Warnings.Add(TEXT("session-not-open"));
		Result.Confidence = 0.0f;
	}

	return Result;
}

FHyperTwistVisionFinalizeResult UHyperTwistMockVisionClient::FinalizeVisionSession(const FString& SessionId)
{
	FHyperTwistVisionFinalizeResult Result = UHyperTwistContractLibrary::MakeMockVisionFinalizeResult();
	Result.SessionId = SessionId;
	Result.FinalState.SourceSessionId = SessionId;
	Result.FinalSnapshot.State.SourceSessionId = SessionId;

	if (!SessionConfigs.Contains(SessionId))
	{
		Result.NormalizationWarnings.Add(TEXT("session-not-open"));
	}
	else
	{
		SessionConfigs.Remove(SessionId);
		SessionFrameCounts.Remove(SessionId);
	}

	return Result;
}

bool UHyperTwistMockVisionClient::CloseVisionSession(const FString& SessionId, FString& OutError)
{
	OutError.Reset();

	if (!SessionConfigs.Contains(SessionId))
	{
		OutError = TEXT("session-not-open");
		return false;
	}

	SessionConfigs.Remove(SessionId);
	SessionFrameCounts.Remove(SessionId);
	return true;
}

FHyperTwistVisionServiceHealth UHyperTwistMockVisionClient::GetVisionServiceHealth() const
{
	FHyperTwistVisionServiceHealth Health = UHyperTwistContractLibrary::MakeMockVisionServiceHealth();
	Health.ProviderLabel = TEXT("mock");
	Health.ServiceEndpoint = TEXT("in-process://mock");
	Health.Capabilities.Add(TEXT("sessioned-mock-client"));
	return Health;
}
