#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "HyperTwistCore/HyperTwistCoreLibrary.h"
#include "HyperTwistVirtual3333ProjectionLibrary.generated.h"

UENUM(BlueprintType)
enum class EHyperTwistVirtual3333Axis : uint8
{
	X UMETA(DisplayName = "X"),
	Y UMETA(DisplayName = "Y"),
	Z UMETA(DisplayName = "Z"),
	W UMETA(DisplayName = "W")
};

UENUM(BlueprintType)
enum class EHyperTwistVirtual3333TurnDirection : uint8
{
	Clockwise UMETA(DisplayName = "Clockwise"),
	CounterClockwise UMETA(DisplayName = "Counter Clockwise")
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333SignedAxis
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	EHyperTwistVirtual3333Axis Axis = EHyperTwistVirtual3333Axis::X;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bPositiveDirection = true;

	bool IsStructurallyValid() const
	{
		return true;
	}
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333PieceOrientation
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<FHyperTwistVirtual3333SignedAxis> Basis;

	bool IsStructurallyValid() const
	{
		if (Basis.Num() != 4)
		{
			return false;
		}

		bool bSeenAxes[4] = {false, false, false, false};
		int32 NegativeDirectionCount = 0;
		int32 PermutationInversionCount = 0;
		for (const FHyperTwistVirtual3333SignedAxis& SignedAxis : Basis)
		{
			const int32 AxisIndex = static_cast<int32>(SignedAxis.Axis);
			if (!SignedAxis.IsStructurallyValid()
				|| AxisIndex < 0
				|| AxisIndex >= 4
				|| bSeenAxes[AxisIndex])
			{
				return false;
			}

			bSeenAxes[AxisIndex] = true;
			NegativeDirectionCount += SignedAxis.bPositiveDirection ? 0 : 1;
		}

		for (int32 LeftIndex = 0; LeftIndex < Basis.Num(); ++LeftIndex)
		{
			for (int32 RightIndex = LeftIndex + 1; RightIndex < Basis.Num(); ++RightIndex)
			{
				if (static_cast<int32>(Basis[LeftIndex].Axis)
					> static_cast<int32>(Basis[RightIndex].Axis))
				{
					++PermutationInversionCount;
				}
			}
		}

		// Legal 4D quarter turns are proper rotations, never reflections.
		return (NegativeDirectionCount + PermutationInversionCount) % 2 == 0;
	}
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333GridCoordinate
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 X = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 Y = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 Z = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 W = 0;

	bool IsStructurallyValid() const
	{
		return FMath::Abs(X) <= 8
			&& FMath::Abs(Y) <= 8
			&& FMath::Abs(Z) <= 8
			&& FMath::Abs(W) <= 8;
	}

	bool IsValidForOrder(int32 Order) const;
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333RuntimeState
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FString StateProfile = TEXT("hypercube-3x3x3x3-runtime-v1");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FHyperTwistPuzzleDefinitionRef Definition;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<int32> PositionToPiece;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<FHyperTwistVirtual3333PieceOrientation> PieceOrientations;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bIsSolved = true;

	bool IsStructurallyValid() const;
	int32 GetOrder() const;
	int32 GetPieceCount() const;
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333SliceTurnRequest
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	EHyperTwistVirtual3333Axis SliceAxis = EHyperTwistVirtual3333Axis::W;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 SliceCoordinate = 1;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	EHyperTwistVirtual3333Axis RotationAxis = EHyperTwistVirtual3333Axis::X;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	EHyperTwistVirtual3333TurnDirection Direction = EHyperTwistVirtual3333TurnDirection::Clockwise;

	bool IsStructurallyValid() const
	{
		return FMath::Abs(SliceCoordinate) <= 8 && RotationAxis != SliceAxis;
	}
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333SliceTurnResult
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FHyperTwistVirtual3333RuntimeState State;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FString AppliedNotation;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<FString> Warnings;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bApplied = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bExactStateUpdate = false;
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333ScrambleResult
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FHyperTwistVirtual3333RuntimeState State;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<FString> AppliedMoves;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FHyperTwistVirtual3333SliceTurnRequest FinalSelection;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<FString> Warnings;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bGenerated = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bExactStateUpdate = false;
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333ProjectedCell
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	EHyperTwistVirtual3333Axis Axis = EHyperTwistVirtual3333Axis::X;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bPositiveSide = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bHasSticker = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	EHyperTwistMelindaStickerColor Color = EHyperTwistMelindaStickerColor::Unknown;

	bool IsStructurallyValid() const
	{
		return !bHasSticker || Color != EHyperTwistMelindaStickerColor::Unknown;
	}
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333ProjectedTesseract
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 PositionIndex = INDEX_NONE;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 PieceId = INDEX_NONE;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FIntVector LocalGridCoordinate = FIntVector::ZeroValue;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FString SlotLabel;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FString PieceLabel;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bPieceInSolvedPosition = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 PuzzleOrder = 3;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<FHyperTwistVirtual3333ProjectedCell> VisibleCells;

	bool IsStructurallyValid() const;
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333VisibleProjection
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FString ProjectionProfile = TEXT("hypercube-3x3x3x3-visible-slice-projection-v1");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FHyperTwistPuzzleDefinitionRef Definition;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	EHyperTwistVirtual3333Axis VisibleSliceAxis = EHyperTwistVirtual3333Axis::W;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	int32 VisibleSliceCoordinate = 1;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<EHyperTwistVirtual3333Axis> DisplayAxes;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<FHyperTwistVirtual3333ProjectedTesseract> Tesseracts;

	bool IsStructurallyValid() const;
};

USTRUCT(BlueprintType)
struct FHyperTwistVirtual3333ProjectionBuildResult
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	FHyperTwistVirtual3333VisibleProjection Projection;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	TArray<FString> Warnings;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bProjected = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333")
	bool bExactProjection = false;
};

UCLASS()
class UNREALHYPERTWIST_API UHyperTwistVirtual3333ProjectionLibrary
	: public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static FHyperTwistPuzzleDefinitionRef MakePuzzleDefinition(int32 Order = 3);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static FHyperTwistVirtual3333RuntimeState BuildSolvedState(int32 Order = 3);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static FHyperTwistPuzzleState BuildPuzzleStateEnvelope(
		const FHyperTwistVirtual3333RuntimeState& RuntimeState
	);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static FString SerializeRuntimeStateToJson(
		const FHyperTwistVirtual3333RuntimeState& RuntimeState
	);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static FString GetAxisLabel(EHyperTwistVirtual3333Axis Axis);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static TArray<EHyperTwistVirtual3333Axis> GetAvailableRotationAxesForSliceAxis(
		EHyperTwistVirtual3333Axis SliceAxis
	);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static TArray<int32> GetSliceCoordinatesForOrder(int32 Order);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static int32 GetSliceCoordinateForLayer(int32 Order, int32 OneBasedLayer);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static bool IsSupportedOrder(int32 Order);

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	static bool IsRotationAxisAvailable(
		EHyperTwistVirtual3333Axis SliceAxis,
		EHyperTwistVirtual3333Axis RotationAxis
	);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	static FHyperTwistVirtual3333SliceTurnResult ApplySliceTurn(
		const FHyperTwistVirtual3333RuntimeState& State,
		const FHyperTwistVirtual3333SliceTurnRequest& Request
	);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	static FHyperTwistVirtual3333ScrambleResult GenerateScramble(
		int32 Order,
		int32 MoveCount = 40,
		int32 RandomSeed = 2027
	);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	static FHyperTwistVirtual3333ProjectionBuildResult BuildVisibleProjection(
		const FHyperTwistVirtual3333RuntimeState& State,
		EHyperTwistVirtual3333Axis VisibleSliceAxis = EHyperTwistVirtual3333Axis::W,
		int32 VisibleSliceCoordinate = 1
	);
};
