// Oleg Petruny proprietary.

#include "Interactable.h"

#include "Kismet/GameplayStatics.h"

#include "Activators/InteractableActivator.h"
#include "CustomPlayerController.h"
#include "MainGameModeBase.h"
#include "Modificators/InteractableModificator.h"
#include "PlayerBase.h"
#include "Widgets/WidgetsManager.h"

TSet<TSubclassOf<class UInteractableActivator>> AInteractable::interactionActivators = {};
TSet<TSubclassOf<class UInteractableModificator>> AInteractable::interactionModificators = {};

void AInteractable::AppendActivatorClass(TSubclassOf<class UInteractableActivator> activator)
{
    if(interactionActivators.Contains(activator))
        return;

    interactionActivators.Add(activator);
}

void AInteractable::AppendModificatorClass(TSubclassOf<class UInteractableModificator> modificator)
{
    if(interactionModificators.Contains(modificator))
        return;

    interactionModificators.Add(modificator);

    auto IC = modificator.GetDefaultObject()->GetMappingContext();
    ACustomPlayerController::AppendInputContext(IC);
}

int32 AInteractable::GetActivatedFlags()
{
    return activated;
}

void AInteractable::BeginPlay()
{
    AActor::BeginPlay();

    // FOR ADRESS TO COLOR
    //if(auto m = material.LoadSynchronous())
    //{
    //    if(material->VectorParameterValues.Num() > 0)
    //    {
    //        GEngine->AddOnScreenDebugMessage(5, 5.0f, FColor::Emerald, material->VectorParameterValues[0].ParameterInfo.Name.ToString());
    //    }
    //}

    TArray<UInteractableModificator*> instancedModificators;
    GetComponents(instancedModificators);

    uint8 activatorTypes = 0;
    for(auto& modificator : instancedModificators)
    {
        uint8 modificatorActivatorTypes = static_cast<uint8>(modificator->GetActivatorTypes());
        activatorTypes |= modificatorActivatorTypes;

        switch(modificatorActivatorTypes & static_cast<uint8>(EActivatorType::Use))
        {
            case static_cast<uint8>(EActivatorType::Use):
                modificators.Add({ EActivatorType::Use, modificator });
                break;
            default:
                break;
        }
        switch(modificatorActivatorTypes & static_cast<uint8>(EActivatorType::Saw))
        {
            case static_cast<uint8>(EActivatorType::Saw):
                modificators.Add({ EActivatorType::Saw, modificator });
                break;
            default:
                break;
        }
    }

    GetComponents(collisions, true);
    for(auto collision : collisions)
    {
        if(activatorTypes)
        {
            collision->SetCollisionProfileName(TEXT("Interactable"));
        }

        if(activatorTypes & static_cast<uint8>(EActivatorType::Saw))
        {
            collision->CustomDepthStencilValue = 128;
            collision->CustomDepthStencilWriteMask = ERendererStencilMask::ERSM_Default;
            collision->SetRenderCustomDepth(true);
        }

        if(activatorTypes & static_cast<uint8>(EActivatorType::Use))
        {
            collision->CustomDepthStencilWriteMask = ERendererStencilMask::ERSM_Default;
            collision->SetRenderCustomDepth(true);
        }
    }

    if(auto PC = UGameplayStatics::GetPlayerController(GetWorld(), 0))
        player = Cast<APlayerBase>(PC->GetPawn());
}

void AInteractable::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    if(activated)
    {
        activationLockers.Empty();
        Deactivate(static_cast<EActivatorType>(activated));
    }

    Super::EndPlay(EndPlayReason);
}

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

    activated |= static_cast<uint8>(type);

    if(!player
        || modificators.IsEmpty()
        || !activationLockers.IsEmpty())
        return;

    if(auto WM = AMainGameModeBase::GetWidgetsManager())
    {
        for(const auto& modificator : modificators)
        {
            if(static_cast<uint8>(modificator.Value->GetActivatorTypes()) & static_cast<uint8>(type))
            {
                WM->ShowInteractionHints(modificator.Value);
                modificator.Value->Bind(ACustomPlayerController::GetInput());
            }
        }
    }

    if(activated & static_cast<uint8>(EActivatorType::Use))
    {
        for(auto collision : collisions)
            collision->SetCustomDepthStencilValue(132);
    }

    OnActivate(type);
}

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

    activated &= ~static_cast<uint8>(type);

    if(!player
        || modificators.IsEmpty()
        || !activationLockers.IsEmpty())
        return;

    if(auto WM = AMainGameModeBase::GetWidgetsManager())
    {
        for(const auto& modificator : modificators)
        {
            if(static_cast<uint8>(modificator.Key) & static_cast<uint8>(type))
            {
                WM->HideInteractionHints(modificator.Value);
                modificator.Value->Unbind();
            }
        }
    }

    if(!(activated & static_cast<uint8>(EActivatorType::Use)))
    {
        for(auto collision : collisions)
            collision->SetCustomDepthStencilValue(0);
    }

    OnDeactivate(type);
}
