#include "PortaSequenceFSMActor.h"
#include "PortaStateTarget.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "Math/ControlRigMathLibrary.h"
#include "Misc/Paths.h"
#include "LevelSequencePlayer.h"
#include "Runtime/Launch/Resources/Version.h"
#include "AssetRegistry/AssetData.h"

APortaSequenceFSMActor::APortaSequenceFSMActor() : SequenceFolder(TEXT("States"))
{
	TargetsTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("Timeline"));
}

void APortaSequenceFSMActor::BeginPlay()
{
	Super::BeginPlay();
	if (TargetsTimeline && TargetsCurve)
	{
		FOnTimelineFloat Callback;
		Callback.BindUFunction(this, FName("TimelineUpdate"));
		TargetsTimeline->AddInterpFloat(TargetsCurve, Callback);
	}

	FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
	IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();

	FARFilter Filter;
	Filter.ClassPaths.Add(FTopLevelAssetPath("/Script/LevelSequence", "LevelSequence"));
	Filter.PackagePaths.Add(FName(TEXT("/Game/") + SequenceFolder));
	TArray<FAssetData> Assets;
	AssetRegistryModule.Get().GetAssets(Filter, Assets);
	for (auto Data : Assets)
	{
		FString AssetName = Data.AssetName.ToString();
		auto Sequence = Cast<ULevelSequence>(Data.ToSoftObjectPath().TryLoad());
		if (!Sequence)
			continue;

		FString Left;
		FString Right;
		AssetName.Split(TEXT("_"), &Left, &Right);

		if (!Left.IsEmpty() && !Right.IsEmpty())
		{
			auto StartState = FindOrCreateState(Left);

			StartState->Transitions.Add(Right, FPortaFSMTransition(Sequence, false));
		}
	}

	//fix up the reverse transitions

	for (auto Pair : States)
	{
		for (auto OtherPair : States)
		{
			if (OtherPair.Key != Pair.Key && !Pair.Value->Transitions.Contains(OtherPair.Key))
			{
				auto TransRef = OtherPair.Value->Transitions.FindRef(Pair.Key);
				if (TransRef.Sequence)
				{
					Pair.Value->Transitions.Add(OtherPair.Key, FPortaFSMTransition(TransRef.Sequence, true));
				}
			}
		}
	}
}

void APortaSequenceFSMActor::GotoState(const FString& State)
{
	auto StateDef = States.FindRef(CurrentState);
	if (StateDef)
	{
		auto Transition = StateDef->Transitions.FindRef(State);
		if (Transition.Sequence)
		{
			if (PlayerActor)
			{
                if (auto player = PlayerActor->GetSequencePlayer())
                    player->Stop();

				PlayerActor->Destroy();
			}

			FMovieSceneSequencePlaybackSettings settings;
			auto Player = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), Transition.Sequence, settings, PlayerActor);
			if (Transition.Reverse)
				Player->PlayReverse();
			else
				Player->Play();
		}
		else if (ActorToMoveToTargets && State != CurrentState)
		{
			auto NextState = States.FindRef(State);
			if (NextState && NextState->Target)
			{
				StartTransform = ActorToMoveToTargets->GetActorTransform();
				EndTransform = NextState->Target->GetActorTransform();
				TargetsTimeline->PlayFromStart();
			}
		}
	}

	CurrentState = State;
}

void APortaSequenceFSMActor::GetStates(TArray<FString>& OutStates)
{
	TSet<FString> Keys;
	States.GetKeys(Keys);
	for (auto State : Keys)
	{
		OutStates.Add(State);
	}
}

void APortaSequenceFSMActor::AddStateTarget(APortaStateTarget* Target)
{
	auto State = FindOrCreateState(Target->StateName);
	State->Target = Target;
}

UPortaFSMState* APortaSequenceFSMActor::FindOrCreateState(const FString& Key)
{
	auto State = States.FindRef(Key);
	if (!State)
	{
		State = NewObject<UPortaFSMState>();
		State->Name = Key;
		States.Add(Key, State);
	}

	return State;
}

void APortaSequenceFSMActor::TimelineUpdate(float Update)
{
	if (ActorToMoveToTargets)
	{
		ActorToMoveToTargets->SetActorTransform(FControlRigMathLibrary::LerpTransform(StartTransform, EndTransform, Update));
	}
}
