#include "PortaSequenceSubsystem.h"
#include "PortaUtils.h"

void UPortaSequenceSubsystem::StopAllSequencesStatic()
{
	PortaUtils::CallWorldSubsystemMember(&UPortaSequenceSubsystem::StopAllSequences);
}

void UPortaSequenceSubsystem::StopAllSequences()
{
	for (auto iter : SequencePlayers)
	{
		iter.Value->Stop();
	}
}

void UPortaSequenceSubsystem::PlaySequencesStatic(const TArray<FString>& Sequences)
{
	PortaUtils::CallWorldSubsystemMember<UPortaSequenceSubsystem, const TArray<FString>&>(&UPortaSequenceSubsystem::PlaySequences, Sequences);
}

void UPortaSequenceSubsystem::PlaySequences(const TArray<FString>& Sequences)
{
	PendingStarts.Append(Sequences);
}

bool UPortaSequenceSubsystem::PlaySequenceImmediate(const FString& SequencePath)
{
	FSoftObjectPath SequenceName(SequencePath);
	auto Sequence = Cast<ULevelSequence>(SequenceName.TryLoad());
	if (!Sequence)
		return false;

	if (SequencePlayers.Contains(SequencePath))
	{
		auto Player = SequencePlayers[SequencePath];
		if (!Player->IsPlaying())
		{
			FMovieSceneSequencePlaybackParams params;
			params.Time = 0;
			params.UpdateMethod = EUpdatePositionMethod::Jump;
			Player->SetPlaybackPosition(params);
			Player->Play();
		}
	}
	else
	{
		FMovieSceneSequencePlaybackSettings settings;
		settings.bAutoPlay = true;
		settings.bPauseAtEnd = false;
		ALevelSequenceActor* Actor;
		auto Player = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), Sequence, settings, Actor);
		SequencePlayers.Add(SequencePath, Player);
	}
	return true;
}

bool UPortaSequenceSubsystem::PlaySequenceReverse(const FString& SequencePath)
{
	FSoftObjectPath SequenceName(SequencePath);
	auto Sequence = Cast<ULevelSequence>(SequenceName.TryLoad());
	if (!Sequence)
		return false;

	if (SequencePlayers.Contains(SequencePath))
	{
		auto Player = SequencePlayers[SequencePath];
		if (!Player->IsPlaying())
		{
			FMovieSceneSequencePlaybackParams params;
			params.Time = Player->GetEndTime().AsSeconds();
			params.UpdateMethod = EUpdatePositionMethod::Jump;
			Player->SetPlaybackPosition(params);
			Player->PlayReverse();
		}
	}
	else
	{
		FMovieSceneSequencePlaybackSettings settings;
		settings.bAutoPlay = false;
		settings.bPauseAtEnd = true;
		ALevelSequenceActor* Actor;
		auto Player = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), Sequence, settings, Actor);
		SequencePlayers.Add(SequencePath, Player);
		Player->PlayReverse();
	}
	return true;
}

bool UPortaSequenceSubsystem::PlaySequenceReverseStatic(const FString& SequencePath)
{
	return PortaUtils::GetWorldSubsystem<UPortaSequenceSubsystem>()->PlaySequenceReverse(SequencePath);
}

void UPortaSequenceSubsystem::PauseSequence(const FString& SequencePath)
{
	ULevelSequencePlayer* Player = SequencePlayers.FindRef(SequencePath);
	if (Player)
	{
		Player->Pause();
	}
}

void UPortaSequenceSubsystem::PauseSequenceStatic(const FString& SequencePath)
{
	PortaUtils::GetWorldSubsystem<UPortaSequenceSubsystem>()->PauseSequence(SequencePath);
}

void UPortaSequenceSubsystem::ResumeSequence(const FString& SequencePath)
{
	ULevelSequencePlayer* Player = SequencePlayers.FindRef(SequencePath);
	if (Player)
	{
		Player->Play();
	}
}

void UPortaSequenceSubsystem::ResumeSequenceStatic(const FString& SequencePath)
{
	PortaUtils::GetWorldSubsystem<UPortaSequenceSubsystem>()->ResumeSequence(SequencePath);
}

void UPortaSequenceSubsystem::ResumeSequenceReverse(const FString& SequencePath)
{
	ULevelSequencePlayer* Player = SequencePlayers.FindRef(SequencePath);
	if (Player)
	{
		Player->PlayReverse();
	}
}

void UPortaSequenceSubsystem::ResumeSequenceReverseStatic(const FString& SequencePath)
{
	PortaUtils::GetWorldSubsystem<UPortaSequenceSubsystem>()->ResumeSequenceReverse(SequencePath);
}

void UPortaSequenceSubsystem::ShowEndOfSequence(const FString& SequencePath)
{
	FSoftObjectPath SequenceName(SequencePath);
	auto Sequence = Cast<ULevelSequence>(SequenceName.TryLoad());
	if (!Sequence)
		return;

	ULevelSequencePlayer* Player = SequencePlayers.FindRef(SequencePath);
	if (!Player)
	{
		FMovieSceneSequencePlaybackSettings settings;
		settings.bPauseAtEnd = true;
		settings.bAutoPlay = true;
		ALevelSequenceActor* Actor;
		Player = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), Sequence, settings, Actor);
		SequencePlayers.Add(SequencePath, Player);
	}

	FMovieSceneSequencePlaybackParams params;
	params.Time = Player->GetDuration().AsSeconds();
	params.PositionType = EMovieScenePositionType::Time;
	params.UpdateMethod = EUpdatePositionMethod::Jump;
	params.bHasJumped = false;
	Player->SetPlaybackPosition(params);
	Player->Play();
	Player->Pause();
}

void UPortaSequenceSubsystem::ShowEndOfSequenceStatic(const FString& SequencePath)
{
	PortaUtils::CallWorldSubsystemMember<UPortaSequenceSubsystem, const FString&>(&UPortaSequenceSubsystem::ShowEndOfSequence, SequencePath);
}


void UPortaSequenceSubsystem::SeekSequence(const FString& SequencePath, float Time)
{
	if (SequencePlayers.Contains(SequencePath))
	{
		auto Player = SequencePlayers[SequencePath];

		FMovieSceneSequencePlaybackParams params;
		params.Time = Time;
		params.UpdateMethod = EUpdatePositionMethod::Play;
		params.PositionType = EMovieScenePositionType::Time;
		params.bHasJumped = false;
		Player->SetPlaybackPosition(params);
		Player->Pause();
	}
}

void UPortaSequenceSubsystem::SeekSequenceStatic(const FString& SequencePath, float Time)
{
	PortaUtils::CallWorldSubsystemMember<UPortaSequenceSubsystem, const FString&, float>(&UPortaSequenceSubsystem::SeekSequence, SequencePath, Time);
}

void UPortaSequenceSubsystem::ResetAllSequences()
{
	for (auto kvp : SequencePlayers)
	{
		auto Player = kvp.Value;
		FMovieSceneSequencePlaybackParams params;
		params.Time = 0;
		params.UpdateMethod = EUpdatePositionMethod::Jump;
		Player->SetPlaybackPosition(params);
		Player->StopAtCurrentTime();
	}
	SequencePlayers.Empty();
}

void UPortaSequenceSubsystem::ResetAllSequencesStatic()
{
	PortaUtils::CallWorldSubsystemMember(&UPortaSequenceSubsystem::ResetAllSequences);
}

void UPortaSequenceSubsystem::Tick(float DeltaTime)
{
	for (auto iter : PendingStarts)
	{
		PlaySequenceImmediate(*iter);
	}
	PendingStarts.Empty();
}
