// Oleg Petruny proprietary.


#include "CutsceneManager.h"

#include "EnhancedInputComponent.h"
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "LevelSequence.h"
#include "LevelSequencePlayer.h"

#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/CutsceneSkipWidget.h"
#include "Widgets/InputAnimatedWidgetInterface.h"
#include "Widgets/WidgetsManager.h"

UCutsceneManager::UCutsceneManager()
{
    static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/IMC_Cutscene.IMC_Cutscene'") };
    context = asset.Object;
    ACustomPlayerController::AppendInputContext(context);
}

void UCutsceneManager::EnqueueSequence(ULevelSequence* sequence, FCutsceneEndCallback endCallback)
{
    if(!sequence)
        return;

    FScopeLock lock1(&sequencesLock);
    FScopeLock lock2(&callbacksLock);

    OnFirstCutsceneInit();

    nextSequences.Enqueue(sequence);
    endCallbacks.Enqueue(endCallback);

    PlayNextSequence();
}

void UCutsceneManager::PlayNextSequence()
{
    if(sequencePlayer)
    {
        if(sequencePlayer->IsPlaying())
            return;
        else
            sequencePlayer->MarkAsGarbage();
    }

    FScopeLock lock(&sequencesLock);

    ULevelSequence* sequence;
    nextSequences.Dequeue(sequence);
    sequencePlayer = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), sequence, FMovieSceneSequencePlaybackSettings{}, sequencePlayerActor);
    sequencePlayer->OnStop.AddDynamic(this, &UCutsceneManager::OnSequenceEnd);
    sequencePlayer->Play();
}

void UCutsceneManager::SkipSequence()
{
    if(!sequencePlayer || !sequencePlayer->IsPlaying())
        return;

    lastlySkipped = true;
    sequencePlayer->GoToEndAndStop();
}

void UCutsceneManager::ClearQueue()
{
    FScopeLock lock1(&sequencesLock);
    FScopeLock lock2(&callbacksLock);
    if(!nextSequences.IsEmpty())
        nextSequences.Empty();
    if(!endCallbacks.IsEmpty())
        endCallbacks.Empty();
}

void UCutsceneManager::LockCallback(bool lock)
{
    lockCallback = lock;
}

void UCutsceneManager::OnSequenceEnd()
{
    if(lockCallback)
        return;

    sequencePlayer->MarkAsGarbage();
    sequencePlayer = nullptr;

    FScopeLock lock(&callbacksLock);

    FCutsceneEndCallback callback;
    endCallbacks.Dequeue(callback);
    if(callback.IsBound())
        callback.Execute();

    if(!nextSequences.IsEmpty())
    {
        PlayNextSequence();
        if(lastlySkipped)
        {
            lastlySkipped = false;
            if(holding)
            {
                OnInputHold();
            }
        }
        return;
    }

    if(auto WM = AMainGameModeBase::GetWidgetsManager())
    {
        WM->DisableCutsceneWidget();
        WM->ShowWidgets();
    }

    if(lastPlayer)
    {
        lastPlayer->UnlockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });

        auto input = ACustomPlayerController::GetInput();
        input->RemoveBindingByHandle(handlers.Key);
        input->RemoveBindingByHandle(handlers.Value);

        lastPlayer = nullptr;
    }

    lastlySkipped = false;
    holding = false;
}

void UCutsceneManager::OnFirstCutsceneInit() // most first sequence, so widgets and binds don't exist
{
    if(!endCallbacks.IsEmpty())
        return;

    if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
    {
        lastPlayer = Cast<APlayerBase>(PC->GetPawn());
        if(lastPlayer)
        {
            lastPlayer->LockPlayer({ .walk = true, .jump = true, .run = true, .interaction = true, .camera = true });

            auto& mapping = context.LoadSynchronous()->GetMapping(0);
            auto input = ACustomPlayerController::GetInput();
            int32 handler1 = input->BindAction(mapping.Action, ETriggerEvent::Started, this, &UCutsceneManager::OnInputHold).GetHandle();
            int32 handler2 = input->BindAction(mapping.Action, ETriggerEvent::Completed, this, &UCutsceneManager::OnInputUnhold).GetHandle();
            handlers = { handler1, handler2 };

            if(auto WM = AMainGameModeBase::GetWidgetsManager())
            {
                WM->HideWidgets();
                static FSkipCutsceneDelegate skipCutsceneDelegate;
                if(!skipCutsceneDelegate.IsBound())
                    skipCutsceneDelegate.BindDynamic(this, &UCutsceneManager::SkipSequence);
                WM->EnableCutsceneWidget(skipCutsceneDelegate);
            }
        }
    }
}

void UCutsceneManager::OnInputHold()
{
    holding = true;

    if(auto WM = AMainGameModeBase::GetWidgetsManager())
        WM->AnimateCutsceneWidget(EInputAnimatedWidgetAnimation::Hold);
}

void UCutsceneManager::OnInputUnhold()
{
    holding = false;

    if(auto WM = AMainGameModeBase::GetWidgetsManager())
        WM->AnimateCutsceneWidget(EInputAnimatedWidgetAnimation::Unhold);
}
