// Oleg Petruny proprietary.

#include "LevelBase.h"

#include "Engine/StaticMesh.h"
#include "Engine/StaticMeshActor.h"
#include "EngineUtils.h"
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "LevelSequencePlayer.h"

#include "CustomGameInstance.h"
#include "CustomPlayerController.h"
#include "Interactable/Interactable.h"
#include "Levels/Checkpoint.h"
#include "MainGameModeBase.h"
#include "Minigame/Minigame.h"
#include "PlayerBase.h"
#include "SaveData.h"

ALevelBase* ALevelBase::Get()
{
    if(auto GM = AMainGameModeBase::Get())
        return GM->leadLevel.Get();
    return nullptr;
}

void ALevelBase::BeginPlay()
{
    AMainGameModeBase::leadLevel = TStrongObjectPtr<ALevelBase>{ this };

    ALevelScriptActor::BeginPlay();

    BroadcastNewLevelBeginPlay();
    StartLevelAnimations();
    ApplySaveData();

    // workaround for forward shading renderer skipping meshes without custom depth,
    // so custom objects are visible through obstacles
    TArray<AActor*> actors;
    UGameplayStatics::GetAllActorsOfClass(GetWorld(), AStaticMeshActor::StaticClass(), actors);
    for(auto actor : actors)
    {
        if(!actor)
            continue;
        auto mesh = reinterpret_cast<AStaticMeshActor*>(actor)->GetStaticMeshComponent();
        if(!mesh->bRenderCustomDepth)
            mesh->SetRenderCustomDepth(true);
    }
}

void ALevelBase::IterateToState(int32 to)
{
    while(state < to)
        CallNextState();
}

void ALevelBase::BroadcastNewLevelBeginPlay()
{
    if(auto GI = UCustomGameInstance::Get())
        GI->OnLevelBeginned.Broadcast(GetFName());
}

void ALevelBase::StartLevelAnimations()
{
    FMovieSceneSequencePlaybackSettings playbackSettings;
    playbackSettings.bAutoPlay = true;
    FMovieSceneSequenceLoopCount playbackLoopCount;
    playbackLoopCount.Value = -1;
    playbackSettings.LoopCount = playbackLoopCount;
    for(auto& sequence : onBeginPlaySequences)
    {
        ALevelSequenceActor* sequenceActor;
        auto sequencePlayer = ULevelSequencePlayer::CreateLevelSequencePlayer(GetWorld(), sequence.LoadSynchronous(), playbackSettings, sequenceActor);
        onBeginPlaySequencesActors.Add(sequenceActor);
    }
}

void ALevelBase::ApplySaveData()
{
    auto GI = UCustomGameInstance::Get();
    if(!GI || !GI->saveData || GI->saveData->level != GetWorld()->GetFName())
        return;

    IterateToState(GI->saveData->state);

    auto player = APlayerBase::Get();
    if(!player)
        return;

    for(TActorIterator<ACheckpoint> it(GetWorld()); it; ++it)
    {
        if(it->GetFName() == GI->saveData->checkpoint)
        {
            player->SetActorLocation(it->GetActorLocation(), false, nullptr, ETeleportType::ResetPhysics);
            player->Controller->SetControlRotation(it->GetActorRotation());
            break;
        }
    }

    int pocketItems = 0;
    for(TActorIterator<AInteractable> it(GetWorld()); it; ++it)
    {
        if(it->GetFName() == GI->saveData->playerLeftPocketItem)
        {
            ++pocketItems;
            player->TakeItemToLeftHand(*it);
        }
        else if(it->GetFName() == GI->saveData->playerRightPocketItem)
        {
            ++pocketItems;
            player->TakeItemToRightHand(*it);
        }
        if(pocketItems > 1)
        {
            break;
        }
    }
}
