// RadialMenuController.h
// Local-only radial weapon selector for the Naked Desire project.
// No replication, no RPCs: this is single-player UI logic.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "RadialMenuController.generated.h"

class URadialMenuWidget;

/**
 * One entry in the radial menu. Swap this out for your real item type later
 * (e.g. a soft pointer to a UPrimaryDataAsset) — the controller only ever
 * reads DisplayName + Icon to build slices, so the rest of the menu is agnostic.
 */
USTRUCT(BlueprintType)
struct FRadialMenuEntry
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu")
    FText DisplayName;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu")
    TObjectPtr<UTexture2D> Icon = nullptr;

    // Optional payload tag so the owning code knows what to equip on confirm.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu")
    FName ItemId = NAME_None;

    // Set false to draw the slice greyed-out (e.g. weapon not yet unlocked).
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu")
    bool bEnabled = true;
};

// Fires when the player confirms a slice. Index is into the Entries array; -1 = cancelled.
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRadialSelectionConfirmed, int32, SelectedIndex);

UCLASS(ClassGroup = (UI), meta = (BlueprintSpawnableComponent))
class URadialMenuController : public UActorComponent
{
    GENERATED_BODY()

public:
    URadialMenuController();

    // --- Public API ---------------------------------------------------------

    // Call on "hold" pressed. Opens the menu, dilates time, shows the cursor.
    UFUNCTION(BlueprintCallable, Category = "Radial Menu")
    void OpenMenu();

    // Call on "hold" released. Confirms the hovered slice and closes.
    UFUNCTION(BlueprintCallable, Category = "Radial Menu")
    void CloseAndConfirm();

    // Close without selecting anything (e.g. pressed a cancel key).
    UFUNCTION(BlueprintCallable, Category = "Radial Menu")
    void CloseAndCancel();

    UFUNCTION(BlueprintPure, Category = "Radial Menu")
    bool IsOpen() const { return bIsOpen; }

    UFUNCTION(BlueprintPure, Category = "Radial Menu")
    int32 GetHoveredIndex() const { return HoveredIndex; }

    // Update the hovered slice from a direction vector (X right, Y up), measured
    // in the same frame as the menu. Used by both the mouse path (controller
    // tick) and the gamepad path (widget forwards the right-stick vector here,
    // since CommonUI consumes the stick before the controller can read it).
    // Magnitude below the dead zone leaves the current hover unchanged.
    void UpdateHoverFromDirection(float DirX, float DirY, float Magnitude);

    // The data the menu draws. Populate from wherever you like (data table,
    // inventory component, hardcoded list) before calling OpenMenu().
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu")
    TArray<FRadialMenuEntry> Entries;

    UPROPERTY(BlueprintAssignable, Category = "Radial Menu")
    FOnRadialSelectionConfirmed OnSelectionConfirmed;

    // --- Tunables -----------------------------------------------------------

    // How long after opening (real seconds) confirms are ignored, so the tap
    // that opens the wheel can't immediately select. ~0.15s is imperceptible
    // but covers the same-frame / next-frame case. Set 0 to disable.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu|Tuning",
        meta = (ClampMin = "0.0"))
    float OpenInputGuardSeconds = 0.15f;

    // Time scale while the menu is open. GTA sits around 0.2; 0.0 = full pause.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu|Tuning",
        meta = (ClampMin = "0.0", ClampMax = "1.0"))
    float OpenTimeDilation = 0.2f;

    // Dead zone (gamepad) / radius in px (mouse) below which we keep the last
    // hovered slice instead of snapping to a new one. Stops jitter near center.
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu|Tuning")
    float SelectionDeadZone = 0.25f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Radial Menu|Tuning")
    TSubclassOf<URadialMenuWidget> WidgetClass;

    virtual void TickComponent(float DeltaTime, ELevelTick TickType,
        FActorComponentTickFunction* ThisTickFunction) override;

private:
    // Converts a screen-space or stick direction into a slice index.
    // Angle is measured clockwise from straight up (12 o'clock), matching GTA.
    int32 AngleToSegment(float AngleDegrees) const;

    // Reads current input (mouse delta or right stick) and updates HoveredIndex.
    void UpdateHoverFromInput();

    void SetTimeDilation(float Scale);

    UPROPERTY(Transient)
    TObjectPtr<URadialMenuWidget> ActiveWidget = nullptr;

    bool bIsOpen = false;
    int32 HoveredIndex = -1;

    // Real-time timestamp when the menu opened. CloseAndConfirm ignores confirms
    // that arrive within OpenInputGuardSeconds, so the tap that opens the wheel
    // can't also register as a selection on the same/next frame.
    double OpenRealTimeSeconds = 0.0;

    // Cached so we can restore exactly what the game had before we opened.
    float CachedTimeDilation = 1.0f;
};