// Oleg Petruny proprietary.


#include "PlayerBase.h"

#include "Camera/CameraComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "InputMappingContext.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"

#include "CustomGameInstanceBase.h"
#include "Interactable/Activators/InteractableActivator.h"

#include "CustomGameUserSettings.h"
#include "MainGameModeBase.h"

APlayerBase::APlayerBase()
{
    PrimaryActorTick.bCanEverTick = true;
}

void APlayerBase::Tick(float DeltaTime)
{
    ACharacter::Tick(DeltaTime);

    // stabilize move speed by fps
    AddActorLocalOffset(ConsumeMovementInputVector() * DeltaTime);
}

void APlayerBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    ACharacter::SetupPlayerInputComponent(PlayerInputComponent);
}

void APlayerBase::BeginPlay()
{
    ACharacter::BeginPlay();

    auto world = GetWorld();

    GetCharacterMovement()->MaxWalkSpeed = moveSpeed;

    cameraManager = UGameplayStatics::GetPlayerCameraManager(world, 0);
    if(cameraManager)
    {
        cameraManager->ViewPitchMin = minPitch;
        cameraManager->ViewPitchMax = maxPitch;
    }

    auto gameSettings = UCustomGameUserSettings::GetCustomGameUserSettings();

    camera = FindComponentByClass<UCameraComponent>();
    if(camera)
    {
        camera->PostProcessSettings.MotionBlurAmount = gameSettings->bUseMotionBlur ? 1.0f : 0.0f;
    }

    if(inputMapping)
    {
        if(auto PC = Cast<APlayerController>(GetController()))
        {
            if(auto inputSubsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer()))
            {
                inputSubsystem->ClearAllMappings();
                inputSubsystem->AddMappingContext(inputMapping.LoadSynchronous(), 0);
            }
        }
    }

    LoadInteractablesActivators();
}

void APlayerBase::SwitchToCameraPawn()
{
    if(auto gamemode_base = UGameplayStatics::GetGameMode(GetWorld()))
    {
        if(auto gamemode = Cast<AMainGameModeBase>(gamemode_base))
        {
            gamemode->SwitchCameraMode();
        }
    }
}

void APlayerBase::MoveCamera(FVector2D value)
{
    if(cameraLocked)
        return;

    AddControllerYawInput(value.X);
    AddControllerPitchInput(value.Y);
}

void APlayerBase::MoveCharacter(FVector2D value)
{
    if(moveLocked)
        return;

    AddMovementInput(GetActorRightVector(), value.X);
    AddMovementInput(GetActorForwardVector(), value.Y);
}

void APlayerBase::Jump()
{
    if(jumpLocked)
        return;

    ACharacter::Jump();
}

void APlayerBase::SwitchRun(bool run)
{
    if(runLocked)
        return;

    if(run)
        GetCharacterMovement()->MaxWalkSpeed = moveSpeed * runSpeedMultiplier;
    else
        GetCharacterMovement()->MaxWalkSpeed = moveSpeed;
}

void APlayerBase::UpdatePitch(float min, float max)
{
    minPitch = min;
    maxPitch = max;

    if(cameraManager)
    {
        cameraManager->ViewPitchMin = minPitch;
        cameraManager->ViewPitchMax = maxPitch;
    }
}

void APlayerBase::LoadInteractablesActivators()
{
    if(auto GI = Cast<UCustomGameInstanceBase>(GetWorld()->GetGameInstance()))
    {
        TSet<UClass*> instancedActivators;
        for(auto& act : GI->interactionsActivators)
        {
            if(instancedActivators.Contains(act))
                continue;
            instancedActivators.Add(act);

            auto component = NewObject<UInteractableActivator>(this, act);
            component->interactableActivatedDelegate.BindUObject(this, &APlayerBase::InteractableActivated);
            component->interactableDeactivatedDelegate.BindUObject(this, &APlayerBase::InteractableDeactivated);
            component->SetupAttachment(camera);
            component->RegisterComponent();
        }
    }
}

void APlayerBase::InteractableActivated(AInteractable* interactable, EActivatorType type)
{
    if(interactionLocked)
        return;

    interactable->Activate(type);
#ifdef INTERACTABLE_DEBUG
    GEngine->AddOnScreenDebugMessage(30 + (int)type, 5.0f, FColor::Cyan, TEXT("Player activate: ") + interactable->GetName()
        + TEXT(" by: ") + StaticEnum<EActivatorType>()->GetNameByValue(static_cast<int64>(type)).ToString());
#endif // INTERACTABLE_DEBUG
}

void APlayerBase::InteractableDeactivated(AInteractable* interactable, EActivatorType type)
{
    if(interactionLocked)
        return;

    interactable->Deactivate(type);
#ifdef INTERACTABLE_DEBUG
    GEngine->AddOnScreenDebugMessage(30 + (int)type, 5.0f, FColor::Magenta, TEXT("Player deactivate: ") + interactable->GetName()
        + TEXT(" by: ") + StaticEnum<EActivatorType>()->GetNameByValue(static_cast<int64>(type)).ToString());
#endif // INTERACTABLE_DEBUG
}