////////////////////////////////////////////////////////////
//
// RootCanvas.h
//
// Luprex provides a "window management system" for root
// widgets. In this system, all top-level widgets have to go
// into the root canvas (instead of the viewport).  The
// window management system monitors the widgets within the
// root canvas and continuously updates the z-orders, the
// pointer visibility, the mouse capture mode, the keyboard
// focus, and enhanced input event routing based on hints
// and directives given by the widgets.
//
// To learn more, read Docs/Luprex-Window-Management.md
//
////////////////////////////////////////////////////////////

#pragma once

#include "CoreMinimal.h"
#include "Common.h"
#include "UObject/ObjectKey.h"
#include "Blueprint/UserWidget.h"
#include "Components/CanvasPanel.h"
#include "Components/CanvasPanelSlot.h"
#include "RootCanvas.generated.h"

class UWidget;



////////////////////////////////////////////////////////////
//
// UlxRootCanvasSlot
//
////////////////////////////////////////////////////////////
UCLASS()
class INTEGRATION_API UlxRootCanvasSlot : public UCanvasPanelSlot
{
	GENERATED_BODY()

public:

	UlxRootCanvasSlot(const FObjectInitializer& ObjectInitializer);

	// When this window is in front, the mouse pointer is shown and the
	// viewport does not capture the mouse.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Input Mode")
	bool ShowPointer = false;

	// When this window is in front, this window's input component blocks
	// lower-priority input components in the input stack.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Input Mode")
	bool BlockInput = false;

	// If true, this widget's input component is enabled, which is to say,
	// that enhanced input events are enabled.  If false, enhanced input
	// events in the Event Graph are deactivated.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Input Mode")
	bool EnableEnhancedInput = true;

	// Sequence number assigned when this slot was last brought to front.
	int32 BringToFrontCount = 0;

	// Widget Z Ordering: widgets that show the pointer go on top of windows
	// that don't, and within a group, widgets that have greater BringToFrontCount
	// go on top.  Sorting an array with this puts the top windows at the end
	// of the array.
	bool operator<(const UlxRootCanvasSlot &Other) const
	{
		if (ShowPointer != Other.ShowPointer) return !ShowPointer;
		return BringToFrontCount < Other.BringToFrontCount;
	}
};

////////////////////////////////////////////////////////////
//
// UlxRootCanvasPanel
//
////////////////////////////////////////////////////////////
UCLASS()
class INTEGRATION_API UlxRootCanvasPanel : public UCanvasPanel
{
	GENERATED_BODY()

public:

	// Add a child to the canvas.  Also brings the child to the front
	// for the first time, ensuring that every widget has a unique
	// BringToFront counter.
	UFUNCTION()
	UlxRootCanvasSlot* AddChildToRootCanvas(UWidget* Content);

	// Find children of type UserWidget.  Return them in a sorted
	// order, with the bottom window first and the top window last.
	TArray<UlxRootCanvasSlot*> GetSortedUserWidgets();

	// Return the highest-priority UserWidget slot, or nullptr if there are none.
	UlxRootCanvasSlot* GetTopWidget();

	// This function updates several window-management-related properties
	// which are stored in the UserWidget and the lxRootCanvasSlot.  Note that
	// it is perfectly legal to edit these properties by other means: this
	// function is one of many valid setters.  See the documentation in
	// Docs/Keyboard-Focus-and-Input-Modes.md for information about how Luprex
	// window management works.
	UFUNCTION(BlueprintCallable, Category = "Luprex|Window Management",
		meta = (DefaultToSelf = "Widget", EnableEnhancedInput = "true"))
	static void SetWidgetWindowManagement(class UUserWidget *Widget,
		bool ShowPointer, bool BlockInput, bool EnableEnhancedInput,
		bool BringToFront, UWidget *DesiredFocusWidget);

	// Bring the widget to the front of the Z order within its group.
	// This is implemented using the BringToFront counter.
	UFUNCTION(BlueprintCallable, Category = "Luprex|Window Management",
		meta = (DefaultToSelf = "Widget"))
	static void BringToFront(class UUserWidget *Widget);

	// Recompute and apply ZOrder values for all slots based on ShowPointer and
	// BringToFrontCount.  Must be called from a context where SetZOrder is safe -
	// i.e. not during OnConstruct.
	void UpdateZOrders();

	// Fetch the UlxRootCanvasSlot for a widget that is parented to a
	// UlxRootCanvasPanel. Returns nullptr via the WrongType exec pin
	// if the widget isn't a root widget (no slot, or slot is not a
	// UlxRootCanvasSlot).
	UFUNCTION(BlueprintCallable, Category = "Luprex|Window Management",
		meta = (DefaultToSelf = "Widget", ExpandEnumAsExecs = "Result"))
	static UlxRootCanvasSlot *GetRootCanvasSlot(class UUserWidget *Widget, ElxSuccessOrWrongType &Result);

protected:

	// We inherit most of our code from CanvasPanel.   This causes the
	// CanvasPanel code to allocate slots of type UlxRootCanvasSlot.
	virtual UClass* GetSlotClass() const override;

	// We override OnSlotAdded in order to be able to be able to
	// fully finish initializing the slot before the widget
	// OnConstruct method has a chance to execute.
	virtual void OnSlotAdded(UPanelSlot* InSlot) override;

	// Monotonic counter incremented each time any slot is brought to front.
	int32 BringToFrontCounter = 0;
};



////////////////////////////////////////////////////////////
//
// UlxRootWidget
//
// A trivial concrete UUserWidget subclass used by the
// PlayerController to host the root UlxRootCanvasPanel.
// UUserWidget itself is marked Abstract in UMG, so we need
// a non-abstract class to instantiate.
//
////////////////////////////////////////////////////////////
UCLASS()
class INTEGRATION_API UlxRootWidget : public UUserWidget
{
	GENERATED_BODY()
};
