// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "Components/SphereComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/BillboardComponent.h"
#include "Components/ArrowComponent.h"

#include "WindZone.generated.h"

class UShapeComponent;

UENUM(BlueprintType)
enum class EWindTriggerShape : uint8
{
    Box UMETA(DisplayName = "Box"),
    Sphere UMETA(DisplayName = "Sphere"),
    Capsule UMETA(DisplayName = "Capsule (Cylinder)")
};

UCLASS(Blueprintable)
class VHOLUME_API  AWindZone : public AActor
{
    GENERATED_BODY()

public:
    // Sets default values for this actor's properties
    AWindZone();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    // The box component that defines the wind zone (default and also root)
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Wind")
    UBoxComponent *WindVolume;

    // Optional alternative trigger shapes (attached to root box)
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Wind")
    USphereComponent *WindVolumeSphere;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Wind")
    UCapsuleComponent *WindVolumeCapsule;

    // Choose which trigger shape is active (Box by default for backward compatibility)
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Wind|Trigger")
    EWindTriggerShape TriggerShape = EWindTriggerShape::Box;

    // Icon component to make the actor easier to select in editor
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Wind")
    UBillboardComponent *IconComponent;

    // Arrow component to visualize and control wind direction
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Wind|Direction & Force",
              meta = (DisplayName = "Wind Direction Arrow"))
    UArrowComponent *WindDirectionArrow;

    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Wind|Direction & Force",
              meta = (DisplayName = "Wind Direction (DEPRECATED)",
                      ToolTip = "This vector is deprecated. Use the Arrow Component above for wind direction. Click 'Apply WindDirection to Arrow' to migrate your current setup."))
    FVector WindDirection;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Direction & Force")
    float WindStrength;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Direction & Force")
    bool bNormalizeWindDirection;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Physics",
              meta = (DisplayName = "Apply Wind To Props"))
    bool bAffectPhysicsObjects;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Physics",
              meta = (DisplayName = "Props Only (No Character)", EditCondition = "bAffectPhysicsObjects"))
    bool bPhysicsObjectsOnly;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Physics",
              meta = (EditCondition = "bAffectPhysicsObjects", ClampMin = "0.0", ClampMax = "10.0",
                      ToolTip = "Multiplier for physics objects (1.0 = same as character force)"))
    float PhysicsObjectsMultiplier;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Physics",
              meta = (EditCondition = "bAffectPhysicsObjects",
                      ToolTip = "Apply enter force multiplier to physics objects"))
    bool bApplyEnterForceToPhysics;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Limits",
              meta = (ClampMin = "0.0"))
    float VelocityCap;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Speed Modifiers",
              meta = (DisplayName = "Modify Character Speed",
                      ToolTip = "Enable speed modification based on movement direction relative to wind"))
    bool bModifyCharacterSpeed;

    // BlueprintReadOnly (not ReadWrite) on purpose: in the game these values are
    // fingerprinted at BeginPlay, so any runtime write that doesn't go through that
    // cache desyncs the pair and trips the tamper verifier -- which flags the session
    // untrusted. Designers tune placed instances in the editor; there is no legit
    // runtime writer. Must stay BlueprintReadOnly here to match.
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Wind|Speed Modifiers",
              meta = (EditCondition = "bModifyCharacterSpeed", ClampMin = "0.0", ClampMax = "500.0",
                      ToolTip = "Speed multiplier when moving in wind direction (1.0 = normal speed)"))
    float SpeedModifierWithWind;

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Wind|Speed Modifiers",
              meta = (EditCondition = "bModifyCharacterSpeed", ClampMin = "0.0", ClampMax = "500.0",
                      ToolTip = "Speed multiplier when moving against wind direction (1.0 = normal speed)"))
    float SpeedModifierAgainstWind;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Exponential Force",
              meta = (DisplayName = "Enable Exponential Force Against Wind",
                      ToolTip = "Enable exponential force multiplier when player moves opposite to wind direction"))
    bool bEnableExponentialForce;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Exponential Force",
              meta = (EditCondition = "bEnableExponentialForce", ClampMin = "1.0", ClampMax = "5.0",
                      ToolTip = "Base multiplier for exponential force growth per tick"))
    float ExponentialGrowthRate;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Exponential Force",
              meta = (EditCondition = "bEnableExponentialForce", ClampMin = "1.0",
                      ToolTip = "Maximum exponential multiplier cap (0 = no limit)"))
    float MaxExponentialMultiplier;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Exponential Force",
              meta = (EditCondition = "bEnableExponentialForce", ClampMin = "100.0", ClampMax = "2000.0",
                      ToolTip = "Distance from arrow start where exponential force begins to ramp up"))
    float ExponentialForceDistance;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Enter Effects")
    bool bApplyEnterForce;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Enter Effects",
              meta = (ClampMin = "0.0", EditCondition = "bApplyEnterForce"))
    float EnterForceMultiplier;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Enter Effects")
    bool bResetVelocityOnEnter;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Enter Effects",
              meta = (EditCondition = "!bResetVelocityOnEnter",
                      ToolTip = "Keep current speed but redirect it in the wind direction"))
    bool bRedirectVelocityInWindDirection;

    // If true, the character keeps full parkour (wall run / wall climb / etc.) while inside
    // the zone -- wind force still applies, but the entry/tick handlers skip the RaiseHands
    // cancel and the FOV-change block. Reset times below become no-ops in this mode.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Parkour",
              meta = (DisplayName = "Allow Parkour Inside Zone",
                      ToolTip = "Don't cancel or block parkour while inside the zone (wind force still applies)"))
    bool bAllowParkourInsideZone;

    // Seconds the WallClimb gate stays closed after the player exits this wind zone.
    // 0 = re-open immediately (current behavior). Prevents instantly reattaching to a
    // wall the moment the player leaves a downdraft that was already suppressing climb.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Parkour",
              meta = (ClampMin = "0.0", EditCondition = "!bAllowParkourInsideZone",
                      ToolTip = "Seconds to keep the WallClimb gate closed after exiting the zone (0 = open immediately)"))
    float WallClimbResetTime;

    // Seconds the WallRun gate stays closed after the player exits this wind zone.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Parkour",
              meta = (ClampMin = "0.0", EditCondition = "!bAllowParkourInsideZone",
                      ToolTip = "Seconds to keep the WallRun gate closed after exiting the zone (0 = open immediately)"))
    float WallRunResetTime;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Visualization",
              meta = (DisplayName = "Show Wind Direction"))
    bool bShowWindArrow;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Wind|Visualization",
              meta = (DisplayName = "Show Debug Info"))
    bool bShowDebug;

private:
    UFUNCTION()
    void OnOverlapBegin(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor,
                        UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult);

    UFUNCTION()
    void OnOverlapEnd(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor,
                      UPrimitiveComponent *OtherComp, int32 OtherBodyIndex);

    // Apply exponential force multiplier for characters moving against wind.
    // Signature intentionally differs from the game, which takes an AIronCharacter* --
    // that type lives in the game module and is not available here. Kept as a stub so
    // this file stays diffable against Source/VHOLUME/Public/WindZone.h in the game repo.
    FVector ApplyExponentialForce(AActor *Character, const FVector &BaseForce,
                                  const FVector &CharacterVelocity, const FVector &WindDirection);

    // Return the currently active trigger component based on TriggerShape
    UShapeComponent *GetActiveTriggerComponent() const;

    // Enable the chosen trigger shape and disable the others. Also applies uniform collision setup
    void UpdateTriggerShape();

    // Apply uniform collision/response settings to any shape component
    void ApplyCollisionSetup(UShapeComponent *ShapeComp, bool bEnableOverlap) const;

    // Map of actors currently in the wind zone and their entry times
    TMap<AActor *, float> ActorsInWind;

    // Map to track exponential force multipliers for each character
    TMap<AActor *, float> ExponentialMultipliers;

public:
    // Called every frame
    virtual void Tick(float DeltaTime) override;

#if WITH_EDITOR
    virtual void PostLoad() override;
    virtual void PostEditMove(bool bFinished) override;
#endif

    // Function to apply current WindDirection to arrow (for migration)
    UFUNCTION(CallInEditor, Category = "Wind|Direction & Force",
              meta = (DisplayName = "Apply WindDirection to Arrow",
                      ToolTip = "Rotates the arrow component to match the current WindDirection vector. Use this to migrate from the old vector-based setup to the new arrow-based setup."))
    void ApplyWindDirectionToArrow();

#if WITH_EDITOR
    virtual void PostEditChangeProperty(FPropertyChangedEvent &PropertyChangedEvent) override;
#endif
};
