// RadialMenuWidget.cpp

#include "RadialMenuWidget.h"
#include "RadialSliceWidget.h"
#include "Components/CanvasPanel.h"
#include "Components/CanvasPanelSlot.h"
#include "Input/CommonUIInputTypes.h" // FBindUIActionArgs
#include "InputCoreTypes.h" // EKeys::LeftMouseButton

void URadialMenuWidget::InitializeFromController(
    URadialMenuController* InController, const TArray<FRadialMenuEntry>& InEntries)
{
    OwningController = InController;
    BuildSlices(InEntries);
}

void URadialMenuWidget::NativeOnActivated()
{
    Super::NativeOnActivated();

    // Make sure the widget can actually hold focus; UCanvasPanel can't, so the
    // activatable widget itself is our focus target (see GetDesiredFocusTarget).
    SetIsFocusable(true);

    // Must be hit-testable for NativeOnMouseButtonDown (left-click confirm) to
    // fire. SelfHitTestInvisible/HitTestInvisible would let the click fall
    // through and never reach us.
    SetVisibility(ESlateVisibility::Visible);

    // Bind Confirm. FBindUIActionArgs accepts the row handle directly.
    // NOTE: Confirm must be mapped to a KEY (e.g. spacebar / face button), not
    // left mouse button. CommonUI routes left-click through its separate Default
    // Click Action path, so a click-mapped Confirm never reaches this binding.
    // To confirm via trackpad/left-click instead, handle the pointer click
    // directly (see NativeOnMouseButtonDown) rather than binding it here.
    if (!ConfirmAction.IsNull())
    {
        FBindUIActionArgs ConfirmArgs(
            ConfirmAction,
            FSimpleDelegate::CreateUObject(this, &URadialMenuWidget::HandleConfirm));
        // Show in the on-screen action bar so the player sees the prompt.
        ConfirmArgs.bDisplayInActionBar = true;
        ConfirmHandle = RegisterUIActionBinding(ConfirmArgs);
    }

    if (!BackAction.IsNull())
    {
        FBindUIActionArgs BackArgs(
            BackAction,
            FSimpleDelegate::CreateUObject(this, &URadialMenuWidget::HandleBack));
        BackArgs.bDisplayInActionBar = true;
        BackHandle = RegisterUIActionBinding(BackArgs);
    }

    // Reinforce: explicitly route focus to this widget for the owning player so
    // gamepad/keyboard input reaches the bindings even if the layer didn't.
    if (APlayerController* PC = GetOwningPlayer())
    {
        SetUserFocus(PC);
    }
}

void URadialMenuWidget::NativeOnDeactivated()
{
    // Clean up bindings so they don't linger if the widget is pooled/reused.
    if (ConfirmHandle.IsValid())
    {
        ConfirmHandle.Unregister();
    }
    if (BackHandle.IsValid())
    {
        BackHandle.Unregister();
    }

    Super::NativeOnDeactivated();
}

UWidget* URadialMenuWidget::NativeGetDesiredFocusTarget() const
{
    // A UCanvasPanel is NOT focusable by default, so returning it here would
    // silently fail and the bound actions wouldn't receive input. The radial
    // menu has no single "default" child to land on (there's no list to
    // highlight), so we focus the activatable widget itself, which participates
    // in focus correctly. Requires bIsFocusable = true (set in NativeConstruct).
    return const_cast<URadialMenuWidget*>(this);
}

FReply URadialMenuWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry,
    const FPointerEvent& InMouseEvent)
{
    // Left-click anywhere on the menu confirms the currently hovered slice.
    // The controller already tracks the hovered index each tick from the cursor
    // angle, so the click itself carries no position logic — it just triggers
    // the confirm. This bypasses CommonUI's Default Click Action entirely.
    if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton)
    {
        if (OwningController.IsValid())
        {
            OwningController->CloseAndConfirm();
        }
        // Handle the event so it doesn't fall through to the click system.
        return FReply::Handled();
    }

    return Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
}

FReply URadialMenuWidget::NativeOnAnalogValueChanged(const FGeometry& InGeometry,
    const FAnalogInputEvent& InAnalogEvent)
{
    // Analog events arrive one axis per event, so we cache the latest value of
    // each right-stick axis and recompute direction whenever either changes.
    const FKey Key = InAnalogEvent.GetKey();
    bool bRightStick = false;

    if (Key == EKeys::Gamepad_RightX)
    {
        RightStickX = InAnalogEvent.GetAnalogValue();
        bRightStick = true;
    }
    else if (Key == EKeys::Gamepad_RightY)
    {
        RightStickY = InAnalogEvent.GetAnalogValue();
        bRightStick = true;
    }

    if (bRightStick && OwningController.IsValid())
    {
        // Stick Y is +up already (unlike screen space), so pass it straight
        // through. The controller applies its own dead zone.
        const float Mag = FVector2D(RightStickX, RightStickY).Size();
        OwningController->UpdateHoverFromDirection(RightStickX, RightStickY, Mag);

        // Consume so CommonUI's analog cursor doesn't also act on it.
        return FReply::Handled();
    }

    return Super::NativeOnAnalogValueChanged(InGeometry, InAnalogEvent);
}

void URadialMenuWidget::HandleConfirm()
{
    // Route to the controller, which owns the state, time dilation, and the
    // OnSelectionConfirmed broadcast. The controller also tears down the widget.
    if (OwningController.IsValid())
    {
        OwningController->CloseAndConfirm();
    }
}

void URadialMenuWidget::HandleBack()
{
    if (OwningController.IsValid())
    {
        OwningController->CloseAndCancel();
    }
}

void URadialMenuWidget::BuildSlices(const TArray<FRadialMenuEntry>& InEntries)
{
    if (!SliceCanvas || !SliceWidgetClass)
    {
        return;
    }

    SliceCanvas->ClearChildren();
    Slices.Reset();
    HoveredIndex = -1;

    const int32 Count = InEntries.Num();
    if (Count == 0)
    {
        return;
    }

    const float AngleSize = 360.f / Count;

    for (int32 i = 0; i < Count; ++i)
    {
        URadialSliceWidget* Slice =
            CreateWidget<URadialSliceWidget>(GetOwningPlayer(), SliceWidgetClass);
        if (!Slice)
        {
            continue;
        }

        const float StartAngle = i * AngleSize - AngleSize * 0.5f;
        Slice->Setup(SliceMaterial, StartAngle, AngleSize,
            InEntries[i].Icon, InEntries[i].bEnabled);

        SliceCanvas->AddChild(Slice);
        if (UCanvasPanelSlot* CanvasSlot = Cast<UCanvasPanelSlot>(Slice->Slot))
        {
            CanvasSlot->SetAnchors(FAnchors(0.5f, 0.5f));
            CanvasSlot->SetAlignment(FVector2D(0.5f, 0.5f));
            CanvasSlot->SetPosition(FVector2D::ZeroVector);
            CanvasSlot->SetSize(FVector2D(MenuDiameter, MenuDiameter));
        }

        Slices.Add(Slice);
    }
}

void URadialMenuWidget::SetHoveredSegment(int32 Index)
{
    HoveredIndex = Index;
}

void URadialMenuWidget::NativeTick(const FGeometry& MyGeometry, float InDeltaTime)
{
    Super::NativeTick(MyGeometry, InDeltaTime);

    const float RealDelta = FApp::GetDeltaTime(); // undilated

    for (int32 i = 0; i < Slices.Num(); ++i)
    {
        if (Slices[i])
        {
            Slices[i]->UpdateHighlight(i == HoveredIndex, RealDelta);
        }
    }
}