// Oleg Petruny proprietary.

#pragma once

#include "GameFramework/Character.h"

#include "PlayerBase.generated.h"

enum class EActivatorType : uint8;

/**
 * Bit collection of player locks.
 */
USTRUCT(BlueprintType)
struct FPlayerLock
{
    GENERATED_BODY()

    uint8 walk : 1;
    uint8 jump : 1;
    uint8 run : 1;
    uint8 interaction : 1;
    uint8 camera : 1;
    uint8 inventory : 1;

    static FPlayerLock All();
};

/**
 * Base class for player pawn.
 * Synchronizes movement speed with fps, manages camera and inventory,
 * prepares for work with interactables, and manages basic player input.
 */
UCLASS(Blueprintable, BlueprintType)
class APlayerBase : public ACharacter
{
    GENERATED_BODY()

public:
    /** setup input context, camera and inventory */
    APlayerBase();


    virtual void Tick(float DeltaTime) override;
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

    UFUNCTION(BlueprintPure, meta = (DisplayName = "Get Player Base Pawn"))
    static APlayerBase* Get();

    UFUNCTION(BlueprintPure)
    bool IsMoving();
    UFUNCTION(BlueprintPure)
    bool IsRunning();

    FVector GetCameraLocation();
    FVector GetCameraDirection();
    void LockPlayer(const FPlayerLock lock);
    void UnlockPlayer(const FPlayerLock lock);
    void FlyMode(bool on);

    /** Force interactable activators reset */
    void ResetInteractions();

    /** Switch camera possesion to passed Actor */
    UFUNCTION(BlueprintCallable, Category = Character)
    void SwitchToView(class AActor* target);
    /** Posess player camera */
    UFUNCTION(BlueprintCallable, Category = Character)
    void ReturnPlayerView();

    /** Place item into first inventory */
    UFUNCTION(BlueprintCallable, Category = Character)
    void TakeItemToLeftHand(class AActor* actor);
    /** Place item into second inventory */
    UFUNCTION(BlueprintCallable, Category = Character)
    void TakeItemToRightHand(class AActor* actor);
    /** Check if Actor is in inventory */
    UFUNCTION(BlueprintCallable, Category = Character)
    bool IsInInventory(const class AActor* actor);

    FVector moveVector;

    class AActor* leftPocketItem = nullptr;
    class AActor* rightPocketItem = nullptr;

protected:
    virtual void BeginPlay() override;

    UFUNCTION(BlueprintCallable, Category = CameraMode)
    void SwitchToCameraPawn();

    UFUNCTION(BlueprintCallable, Category = Character)
    void MoveCamera(FVector2D value);
    UFUNCTION(BlueprintCallable, Category = Character)
    void MoveCharacter(FVector2D value);
    virtual void Jump() override;
    UFUNCTION(BlueprintCallable, Category = Character)
    void SwitchRun(bool run);

    /** Sets camera pitch range */
    UFUNCTION(BlueprintCallable, Category = Character)
    void UpdatePitch(float min, float max);

    UFUNCTION(BlueprintCallable, Category = Character)
    void DropLeftHandItem();
    UFUNCTION(BlueprintCallable, Category = Character)
    void DropRightHandItem();
    UFUNCTION(BlueprintCallable, Category = Character)
    void ShowInventory();

    UFUNCTION(BlueprintCallable, Category = Character)
    void ShowJournal();
    UFUNCTION(BlueprintCallable, Category = Character)
    void ShowMenu();

    class APlayerController* playerController;
    UPROPERTY(EditAnywhere)
    float moveSpeed = 200;
    UPROPERTY(EditAnywhere)
    float runSpeedMultiplier = 4;

    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    uint8 walkLocked = 0;
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    uint8 jumpLocked = 0;
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    uint8 runLocked = 0;
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    uint8 interactionLocked = 0;
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    uint8 cameraLocked = 0;
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    uint8 inventoryLocked = 0;

    APlayerCameraManager* cameraManager;
    UPROPERTY(EditAnywhere, BlueprintReadWrite)
    class UCameraComponent* camera;
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    float minPitch = -80;
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    float maxPitch = 65;

    bool bIsMoving = false;

    class AInteractable* lastInteractable = nullptr;
    TArray<class UInteractableActivator*> interactableActivators;
    UPROPERTY()
    class USpringArmComponent* itemDropSpringArm;

private:
    void LoadInteractablesActivators();
    void InteractableActivated(class AInteractable* interactable, EActivatorType type);
    void InteractableDeactivated(class AInteractable* interactable, EActivatorType type);

    void TakeToInventory(class AActor* actor);
    void DropItem(class AActor* actor);

    FVector oldLocation;
    FRotator rotationInput;

    bool itemDropLocked = false;
    FTimerHandle itemDropLockTimer;

    TSoftObjectPtr<class UInputMappingContext> context;
};
