// 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/BillboardComponent.h"
#include "Components/ArrowComponent.h"
#include "Components/SceneComponent.h"
#include "Components/StaticMeshComponent.h"

#include "MapJumpPad.generated.h"

class UShapeComponent;

/**
 * Authoring-only enum for a nicer dropdown. The game only reads the synced
 * bool TargetMode: Target here becomes TargetMode=true, Direction becomes
 * TargetMode=false.
 */
UENUM(BlueprintType)
enum class EMapJumpPadMode : uint8
{
    Target    UMETA(DisplayName = "Target (LocalTarget)"),
    Direction UMETA(DisplayName = "Direction (Arrow)")
};

/**
 * Mapper-facing proxy for the game's jump pad.
 *
 * This is NOT the game's AJumpad/BP_JumpPad. Place this actor instead in a
 * vanilla-UE mapping project. At map load the shipped game swaps it for
 * BP_JumpPad, copies these properties across by name, and destroys the proxy.
 *
 * Two authoring workflows are supported:
 *   - Target mode: drag the LocalTarget component to the landing spot; the pad
 *     solves an arc to reach it using ApexClearance.
 *   - Direction mode: rotate the Arrow component; the pad launches the player
 *     along that direction with YeetForce.
 *
 * Property names and defaults must match the game-side mirror exactly. The
 * swap copies by name; mismatches silently drop the authored value back to the
 * game's CDO default.
 *
 * The Box component is a visual placement aid only -- BP_JumpPad triggers from
 * On Component Hit on its mesh, so the mapper sizes the pad by scaling the
 * actor, not by resizing this box.
 */
UCLASS(Blueprintable, meta = (DisplayName = "Jump Pad"))
class VHOLUME_API AMapJumpPad : public AActor
{
    GENERATED_BODY()

public:
    AMapJumpPad();

    /** Root scene component. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Jump Pad")
    USceneComponent* Root;

    /**
     * Visual trigger volume. Does NOT size the real pad's collision -- BP_JumpPad
     * fires from On Component Hit on its mesh. Kept as a placement aid only.
     */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Jump Pad",
              meta = (ToolTip = "Visual only -- BP_JumpPad triggers from its mesh, not this box. Scale the actor to size the pad."))
    UBoxComponent* Box;

    /** Placeholder visual mesh so the pad is visible and selectable in the editor. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Jump Pad")
    UStaticMeshComponent* PlaceholderVisual;

    /** Editor-only icon. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Jump Pad")
    UBillboardComponent* Billboard;

    /** Visualizes the launch direction in Direction mode. Points up by default. */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Jump Pad")
    UArrowComponent* Arrow;

    /**
     * Visual landing target in Target mode. Starts at the pad origin so the
     * mapper must move it; the default authoring mode is Direction to avoid an
     * invalid self-target.
     */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Jump Pad")
    USceneComponent* LocalTarget;

    /** Authoring-only dropdown. Synced into TargetMode for the game. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad",
              meta = (ToolTip = "Target aims an arc at LocalTarget. Direction launches along Arrow."))
    EMapJumpPadMode Mode;

    /** True = aim an arc at TargetPosition; false = launch along LaunchDirection. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad")
    bool TargetMode;

    /** Seconds between launches. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad",
              meta = (ClampMin = "0.0"))
    float Cooldown;

    /** Seconds the launched player ignores this pad's collision. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad",
              meta = (ClampMin = "0.0"))
    float LaunchIgnoreCollisionTime;

    /** How far the arc peaks above the higher of the pad and the target. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Target",
              meta = (EditCondition = "TargetMode", ClampMin = "0.0"))
    float ApexClearance;

    /** Radius used to sweep along the arc for geometry. 0 skips the check. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Target",
              meta = (EditCondition = "TargetMode", ClampMin = "0.0"))
    float ClearanceSweepRadius;

    /** Highest apex the arc solver will try before giving up. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Target",
              meta = (EditCondition = "TargetMode", ClampMin = "0.0"))
    float MaxApexClearance;

    /** True if the target marks the floor; the solve lifts the aim by a capsule half-height. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Target",
              meta = (EditCondition = "TargetMode"))
    bool bTargetIsFloorPoint;

    /** Per-pad in-game arc debug. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Target",
              meta = (EditCondition = "TargetMode"))
    bool bDebugArc;

    /** World-space landing target. Synced from LocalTarget in the editor. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Target",
              meta = (EditCondition = "TargetMode"))
    FVector TargetPosition;

    /** Lock trajectory so the player cannot steer out of the arc. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Target",
              meta = (EditCondition = "TargetMode"))
    bool TargetLockTrajectory;

    /** Launch direction. Synced from Arrow rotation in the editor. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Direction",
              meta = (EditCondition = "!TargetMode"))
    FVector LaunchDirection;

    /** Impulse magnitude for Direction mode. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Direction",
              meta = (EditCondition = "!TargetMode", ClampMin = "0.0"))
    float YeetForce;

    /** If true, zero current velocity before applying the launch impulse. */
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Jump Pad|Direction",
              meta = (EditCondition = "!TargetMode"))
    bool YeetOverrideVelocity;

protected:
    virtual void OnConstruction(const FTransform& Transform) override;

#if WITH_EDITOR
    virtual void PostEditMove(bool bFinished) override;
    virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override;
#endif

private:
    void ApplyCollisionSetup(UShapeComponent* ShapeComp) const;
    void SyncAuthoringToRuntime();
    void SyncGizmosToRuntime();
};
