// 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'") };
    _inputContext = asset.Object;
    ACustomPlayerController::AppendInputContext(_inputContext);
}

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

    FScopeLock lock1(&_sequencesLock);
    FScopeLock lock2(&_callbacksLock);

    if(_endCallbacks.IsEmpty()) // most first sequence, so widgets and binds don't exist
    {
        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 = _inputContext.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);
                }
            }
        }
    }

    _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::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);
    }
}
