// Oleg Petruny proprietary.

#include "CustomPlayerController.h"

#include "EnhancedInputComponent.h"
#include "EnhancedInputLibrary.h"
#include "EnhancedInputSubsystems.h"
#include "InputMappingContext.h"

#include "CustomGameInstance.h"
#include "CustomGameSettings.h"

ACustomPlayerController* ACustomPlayerController::instance = nullptr;
UEnhancedInputComponent* ACustomPlayerController::input = nullptr;
UEnhancedInputLocalPlayerSubsystem* ACustomPlayerController::subsystem = nullptr;
TSet<TSoftObjectPtr<UInputMappingContext>> ACustomPlayerController::contexts = {};
TSet<TSoftObjectPtr<UInputMappingContext>> ACustomPlayerController::contextsBeforeInit = {};

void ACustomPlayerController::AppendInputContext(TSoftObjectPtr<class UInputMappingContext> context)
{
    if(!context.IsValid())
        return;

    if(!UCustomGameInstance::Get()) //game settings not initialized yet
    {
        contextsBeforeInit.Add(context);
        return;
    }

    ApplyMouseSettings(context);
    contexts.Add(context);
    if(subsystem)
        subsystem->AddMappingContext(context.LoadSynchronous(), 0);
}

void ACustomPlayerController::BeginPlay()
{
    Super::BeginPlay();

    instance = this;

    for(auto& context : contextsBeforeInit)
    {
        ApplyMouseSettings(context);
        contexts.Add(context);
    }
    contextsBeforeInit.Empty();

    subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
    if(subsystem)
    {
        subsystem->ClearAllMappings();
        for(auto& inputContext : contexts)
            subsystem->AddMappingContext(inputContext.LoadSynchronous(), 0);
    }

    input = Cast<UEnhancedInputComponent>(InputComponent);
    if(input)
    {
        ((UInputComponent*)input)->BindAction(TEXT("AnyKey"), IE_Pressed, this, &ACustomPlayerController::OnAnyKeyPressed);
        ((UInputComponent*)input)->BindAction(TEXT("AnyKey"), IE_Released, this, &ACustomPlayerController::OnAnyKeyReleased);
    }
}

void ACustomPlayerController::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    instance = nullptr;
    input = nullptr;
    subsystem = nullptr;

    Super::EndPlay(EndPlayReason);
}

void ACustomPlayerController::ApplyMouseSettings(TSoftObjectPtr<class UInputMappingContext>& context)
{
    auto gameSettings = UCustomGameSettings::Get();
    if(!gameSettings)
        return;

    if(!context.LoadSynchronous())
        return;

    for(auto& mapping : context.LoadSynchronous()->GetMappings())
    {
        if(mapping.Key != EKeys::Mouse2D)
            continue;

        for(auto& modifier : mapping.Modifiers)
        {
            if(gameSettings->bMouseInverted)
            {
                if(auto negate_modifier = Cast<UInputModifierNegate>(modifier))
                {
                    negate_modifier->bY = !negate_modifier->bY;
                    continue;
                }
            }
            if(auto scalar_modifier = Cast<UInputModifierScalar>(modifier))
                scalar_modifier->Scalar = FVector{ gameSettings->GetMouseSensetivity() * 0.5 };
        }
    }

    UEnhancedInputLibrary::RequestRebuildControlMappingsUsingContext(context.LoadSynchronous());
}

void ACustomPlayerController::OnAnyKeyPressed(FKey key)
{
    if(onAnyKeyPressed.IsBound())
        onAnyKeyPressed.Broadcast(key);
}

void ACustomPlayerController::OnAnyKeyReleased(FKey key)
{
    if(onAnyKeyReleased.IsBound())
        onAnyKeyReleased.Broadcast(key);
}
