// Oleg Petruny proprietary.


#include "CustomGameInstanceBase.h"

#include "EnhancedInputLibrary.h"
#include "EnhancedInputSubsystems.h"
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetSystemLibrary.h"

#include "CommonFunctions.h"
#include "ContentLoader.h"
#include "CustomGameUserSettings.h"
#include "Interactable/Activators/InCameraInteractableActivator.h"
#include "Interactable/Activators/RaycastInteractableActivator.h"
#include "Interactable/Modificators/ActivateInteractableModificator.h"
#include "Interactable/Modificators/SawInteractableModificator.h"
#include "Levels/LevelBase.h"
#include "PlayerBase.h"
#include "SaveData.h"

UCustomGameInstanceBase* UCustomGameInstanceBase::instance = nullptr;

void UCustomGameInstanceBase::Init()
{
    UGameInstance::Init();

    instance = this;

    contentLoader = NewObject<UContentLoader>(this);

    interactionsActivators.Add(URaycastInteractableActivator::StaticClass());
    interactionsActivators.Add(UInCameraInteractableActivator::StaticClass());

    for(auto& modificator : interactionsModificators)
    {
        if(modificator.GetDefaultObject()->GetMappingContext())
        {
            inputContexts.Add(modificator.GetDefaultObject()->GetMappingContext());
        }
    }

    ApplyMouseSettings();

    saveData = Cast<USaveData>(UGameplayStatics::CreateSaveGameObject(USaveData::StaticClass()));
}

UCustomGameInstanceBase* UCustomGameInstanceBase::GetGameInstance()
{
    return instance;
}

UContentLoader* UCustomGameInstanceBase::GetContentLoader()
{
    if(auto GI = GetGameInstance())
        return GI->contentLoader;

    return nullptr;
}

void UCustomGameInstanceBase::ApplyMouseSettings()
{
    if(auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings())
    {
        for(auto& context : inputContexts)
        {
            if(!context.LoadSynchronous())
                continue;

            for(auto& mapping : context.LoadSynchronous()->GetMappings())
            {
                if(mapping.Key == EKeys::Mouse2D)
                {
                    for(auto& modifier : mapping.Modifiers)
                    {
                        if(auto negate_modifier = Cast<UInputModifierNegate>(modifier))
                        {
                            negate_modifier->bY = !gameSettings->bMouseInverted;
                        }
                        if(auto scalar_modifier = Cast<UInputModifierScalar>(modifier))
                        {
                            scalar_modifier->Scalar = FVector{ gameSettings->GetMouseSensetivity() * 0.5 };
                        }
                    }
                }
            }
            UEnhancedInputLibrary::RequestRebuildControlMappingsUsingContext(context.LoadSynchronous());
        }
    }
}

void UCustomGameInstanceBase::AppendInteractableModificatorClass(TSubclassOf<class UInteractableModificator> modificator)
{
    bool alreadyPresent = false;
    interactionsModificators.Add(modificator, &alreadyPresent);
    if(!alreadyPresent)
    {
        if(auto IC = modificator.GetDefaultObject()->GetMappingContext())
        {
            inputContexts.Add(IC);
            if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
            {
                if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
                {
                    inputSubsystem->AddMappingContext(IC, 0);
                }
            }
        }
    }
}

void UCustomGameInstanceBase::SaveGame(FName checkpointName)
{
    auto levelScript = UCommonFunctions::GetCurrentLevelScript(this);
    if(!levelScript)
        return;

    auto player = UCommonFunctions::GetPlayer(this);
    if(!player)
        return;
    saveData->level = GetWorld()->GetFName();
    saveData->state = levelScript->GetState();
    saveData->checkpoint = checkpointName;
    if(player->leftPocketItem)
        saveData->playerLeftPocketItem = player->leftPocketItem->GetFName();
    else
        saveData->playerLeftPocketItem = FName(TEXT(""));
    if(player->rightPocketItem)
        saveData->playerRightPocketItem = player->rightPocketItem->GetFName();
    else
        saveData->playerRightPocketItem = FName(TEXT(""));

    UGameplayStatics::SaveGameToSlot(saveData, TEXT("Save"), 0);
}

void UCustomGameInstanceBase::LoadGame()
{
    saveData = Cast<USaveData>(UGameplayStatics::LoadGameFromSlot(TEXT("Save"), 0));
    if(!saveData)
        return;

    UGameplayStatics::OpenLevel(this, saveData->level);
}

void UCustomGameInstanceBase::ExitGame()
{
    UKismetSystemLibrary::QuitGame(GetWorld(), nullptr, EQuitPreference::Quit, true);
}
