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

#pragma once

#include "CoreMinimal.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Input/Events.h"
#include "Common.h"
#include "Kismet/BlueprintFunctionLibrary.h"

#include "UtilityLibrary.generated.h"

class UEnhancedInputLocalPlayerSubsystem;
class UAnimSequenceBase;


/**
 *
 * UlxUtilityLibrary is for functions that are aren't particularly luprex-specific,
 * but rather, are just generally-useful functionality that could help in any
 * Unreal program.
 * 
 */
UCLASS()
class INTEGRATION_API UlxUtilityLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

public:
	
	// Call a function by name, on any UObject.  If the function doesn't exist, calls
	// the fallback function instead.  If that isn't found either, returns false.
	//
	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Utility")
		static void CallFunctionByName(UObject *target, const FString &NamePart1, const FString &NamePart2, const FString &fallback, bool bFailIfNotFound = true);

	// Get the axis-aligned bounding box of an actor.
	//
	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Luprex|Utility")
		static FBox GetActorBounds(const AActor *target, bool bOnlyCollidingComponents = false, bool bIncludeFromChildActors = false);
	
	// Add movement input, using the yaw of the control rotation to find a rightward vector.
	//
	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Pawn|Input")
		static void AddMovementInputRightward(APawn *target, double ScaleValue=1.0, bool Force=false);

	// Add movement input, using the yaw of the control rotation to find a forward vector.
	//
	UFUNCTION(BlueprintCallable, Meta = (DefaultToSelf = "target"), Category = "Pawn|Input")
		static void AddMovementInputForward(APawn *target, double ScaleValue=1.0, bool Force=false);

	// Get the enhanced input local player subsystem from a controller.  If the controller
	// is not a player controller, or if it is a player controller but it doesn't have an
	// enhanced input subsystem, return nullptr.
	UFUNCTION(BlueprintPure, Category="Player Controller|Local Player Subsystems")
		static UEnhancedInputLocalPlayerSubsystem *GetEnhancedInputLocalPlayerSubsystem(AController *Controller);

	// Given a Pixel XY coordinate, convert it to a viewport position.
	//
	// The input should be in the range (0.0, 0.0) to (ViewportX, ViewportY).
	// The output XY will be in the range 0.0 to 1.0.  The input
	// may specify a fraction of a pixel.  So the input (0.0, 0.0)
	// represents the upper-left corner of the upper-left pixel, whereas the
	// input (0.5, 0.5) represents the center of the upper-left pixel.
	UFUNCTION(BlueprintPure, meta = (ReturnDisplayName = "Percent XY"), Category="Luprex|Utility")
	static FVector2D PixelToViewportPosition(FVector2D PixelXY);

	// Given a viewport position, convert it to a Pixel XY coordinate.
	//
	// The input X and Y coordinates should be in the range 0 to 1.  The output
	// will be in the range (0,0) to (ViewportX, ViewportY).  The output may
	// specify a fraction of a pixel.  If SnapToCenter is true, the output is
	// rounded to the center of the nearest pixel.
	UFUNCTION(BlueprintPure, meta = (ReturnDisplayName = "Pixel XY"), Category="Luprex|Utility")
	static FVector2D ViewportPositionToPixel(FVector2D PercentXY, bool SnapToCenter);
	
	// Do a Line Trace from the camera through a specified pixel.
	//
	// This can be used when you have a crosshair on the screen and you want to
	// determine the object that the crosshairs are pointing at.  It can also
	// be used to do a line trace through the mouse. Fractional pixels are allowed.
	// Be aware that (0.0, 0.0) is the upper-left corner of the upper-left pixel,
	// whereas (0.5, 0.5) is the center of the upper-left pixel.
	//
	// The resulting TraceStart and TraceEnd fields of the HitResult will not
	// contain world positions, instead, they will contain the PixelXY.
	//
	UFUNCTION(BlueprintCallable, Category="Collision", meta=(AutoCreateRefTerm="ActorsToIgnore", Keywords="raycast"))
		static bool LineTraceThroughPixel(const APlayerController* PlayerController, 
			FVector2D PixelXY, double MaxDistanceFromCamera,
			ETraceTypeQuery TraceChannel, bool bTraceComplex, EDrawDebugTrace::Type DrawDebugType, bool bIgnorePlayerPawn,
			const TArray<AActor*>& ActorsToIgnore, FHitResult& HitResult);

	// Set Position of GridPanel Middle Cell
	//
	// Sometimes, you want to specify the position of a widget, and you
	// don't want to specify the position in slate units, instead, you
	// want to specify the position using fractions: ie, (0,0) is the
	// upper left corner of the screen, and (1,1) is the lower-right corner.
	//
	// One way to accomplish this is to put your widget in the middle cell
	// of a 3x3 GridPanel.  Then, you can position it by adjusting the grid
	// fill rules.  This utility routine can do the math necessary to
	// correctly populate those fill rules.
	//
	// This routine must be passed a 3x3 GridPanel.  This will reposition
	// the middle cell.  You must specify the upper-left and lower-right
	// corners of the middle cell as fractions between (0,0) and (1,1).
	//
	// Be aware that if the content of a grid cell overflows the amount of
	// space allocated for it, then the grid will adjust to make room.
	// But that will mean that the grid is no longer faithful to the 
	// positions specified in its fill rules.  One way to ensure that the
	// grid remains faithful to its fill rules is to put an Overlay
	// into the GridPanel cell, then put -1000 padding into the
	// Overlay's GridPanel slot, then put +1000 padding into the Overlay
	// slot.  The two paddings cancel each other out, leaving the item in
	// the Overlay at the originally-intended position.  But if the item
	// in the overlay overflows, it doesn't cause the Grid to deform.
	// Instead, the item exceeds the bounds of the grid cell, but it leaves
	// the grid cell where it belongs.
	// 
	UFUNCTION(BlueprintCallable, Category="Widget")
	static void SetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D UpperLeftXY, FVector2D LowerRightXY);

	// Get Position of GridPanel Middle Cell
	//
	// The routine must be passed a 3x3 GridPanel.  This will return the
	// position of the middle cell of the gridpanel, expressed on a scale
	// from (0,0) to (1,1).
	// 
	// The numbers returned by this routine are based entirely on the
	// GridPanel fill rules.  If an item in the grid is overflowing its
	// allocated space, causing the grid to deform, then that won't be
	// reflected in the output of this routine.
	//
	UFUNCTION(BlueprintPure, Category="Widget")
	static void GetPositionOfGridPanelMiddleCell(UGridPanel *GridPanel, FVector2D &UpperLeftXY, FVector2D &LowerRightXY);

	// Check if a given key is used by the specified mapping context.
	//
	// This is true if the key is mapped to anything at all within
	// the specified mapping context.
	//
    UFUNCTION(BlueprintCallable, Category = "Input", meta = (ExpandEnumAsExecs="ReturnValue"))
	static ElxUsedOrNotUsed IsKeyUsedByMappingContext(const FKey &Key, const UInputMappingContext *MappingContext);

	// Get a key by name.
	//
	// Returns the null key if there is no such key.
	//
    UFUNCTION(BlueprintPure, Category = "Input|Key")
	static FKey GetKeyByName(const FName &Name);

	// Get a key by name string.
	//
	// Returns the null key if there is no such key.
	//
    UFUNCTION(BlueprintPure, Category = "Input|Key")
	static FKey GetKeyByNameString(const FString &Name);

	// Get the actor's forward vector multiplied by a speed.
	// If SnapToXY is true, the forward vector is projected
	// onto the XY plane and renormalized before scaling.
	//
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Luprex|Utility", meta = (DefaultToSelf = "Actor"))
	static FVector GetActorForwardVelocity(const AActor *Actor, double Speed = 1.0, bool bSnapToXY = false);

	// Syntactically validate lua code. Parses the
	// code and returns an error message. If the code
	// is error-free, the error message is empty.
	//
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "context"), Category = "Luprex|Utility")
	static void ValidateLuaExpr(ElxLuaSyntaxCheck &Status, FString &ErrorMessage, UObject *context, const FString &Code);
};
