#include "HyperTwistSimulation/HyperTwistClassicCubeActor.h"
#include "Engine/World.h"

AHyperTwistClassicCubeActor::AHyperTwistClassicCubeActor()
{
	PrimaryActorTick.bCanEverTick = true;
	RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
}

void AHyperTwistClassicCubeActor::OnConstruction(const FTransform& Transform)
{
	Super::OnConstruction(Transform);
	if (bGenerateOnConstruction)
	{
		ResetCube();
	}
}

void AHyperTwistClassicCubeActor::BeginPlay()
{
	Super::BeginPlay();
	if (!bGenerateOnConstruction)
	{
		ResetCube();
	}
}

void AHyperTwistClassicCubeActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	if (!bIsAnimating || !ActiveRotation.Pivot)
	{
		return;
	}

	const float Now = GetWorld()->GetTimeSeconds();
	const float Elapsed = Now - ActiveRotation.StartTime;
	float Alpha = FMath::Clamp(Elapsed / ActiveRotation.Duration, 0.0f, 1.0f);
	// Smooth step for nicer animation feel
	Alpha = Alpha * Alpha * (3.0f - 2.0f * Alpha);

	const FQuat CurrentRot = FQuat::Slerp(ActiveRotation.PivotStartRot, ActiveRotation.PivotTargetRot, Alpha);
	ActiveRotation.Pivot->SetWorldRotation(CurrentRot);

	if (Alpha >= 1.0f)
	{
		FinalizeRotation();
	}
}

void AHyperTwistClassicCubeActor::ResetCube()
{
	ClearPieces();
	TrackedPieces.Empty();
	RotationQueue.Empty();
	bIsAnimating = false;
	ActiveRotation = FActiveRotation();
	GenerateCube();
}

bool AHyperTwistClassicCubeActor::ProcessClick(const FVector& RayOrigin, const FVector& RayDirection, bool bCounterClockwise)
{
	if (!GetWorld())
	{
		return false;
	}

	FHitResult Hit;
	FCollisionQueryParams Params(SCENE_QUERY_STAT(CubeClick), false, this);
	const FVector End = RayOrigin + RayDirection * 10000.0f;
	bool bHit = GetWorld()->LineTraceSingleByChannel(Hit, RayOrigin, End, ECC_Visibility, Params);

	if (!bHit || !Hit.GetComponent())
	{
		return false;
	}

	// Map hit normal to cube face
	const FVector& Normal = Hit.ImpactNormal;
	EHyperTwistClassicCubeFace Face = EHyperTwistClassicCubeFace::Up;
	bool bValidFace = true;

	if (FMath::IsNearlyEqual(Normal.Z, 1.0f))       Face = EHyperTwistClassicCubeFace::Up;
	else if (FMath::IsNearlyEqual(Normal.Z, -1.0f)) Face = EHyperTwistClassicCubeFace::Down;
	else if (FMath::IsNearlyEqual(Normal.Y, 1.0f))  Face = EHyperTwistClassicCubeFace::Front;
	else if (FMath::IsNearlyEqual(Normal.Y, -1.0f)) Face = EHyperTwistClassicCubeFace::Back;
	else if (FMath::IsNearlyEqual(Normal.X, 1.0f))  Face = EHyperTwistClassicCubeFace::Right;
	else if (FMath::IsNearlyEqual(Normal.X, -1.0f)) Face = EHyperTwistClassicCubeFace::Left;
	else bValidFace = false;

	if (!bValidFace)
	{
		return false;
	}

	RotateFace(Face, bCounterClockwise ? EHyperTwistRotationDirection::CounterClockwise : EHyperTwistRotationDirection::Clockwise);
	return true;
}

TArray<FString> AHyperTwistClassicCubeActor::GenerateScramble(int32 Length)
{
	TArray<FString> Result;
	const TArray<FString> FaceNames = { TEXT("U"), TEXT("D"), TEXT("F"), TEXT("B"), TEXT("R"), TEXT("L") };
	const TArray<FString> Suffixes = { TEXT(""), TEXT("'"), TEXT("2") };

	// Seed random if not already seeded
	static bool bSeeded = false;
	if (!bSeeded)
	{
		FMath::RandInit(FDateTime::Now().GetTicks());
		bSeeded = true;
	}

	for (int32 i = 0; i < Length; ++i)
	{
		FString Move = FaceNames[FMath::RandRange(0, 5)];
		FString Suffix = Suffixes[FMath::RandRange(0, 2)];
		Result.Add(Move + Suffix);
	}
	return Result;
}

void AHyperTwistClassicCubeActor::ApplyScramble(const TArray<FString>& MoveStrings)
{
	auto ParseFaceChar = [](TCHAR C) -> EHyperTwistClassicCubeFace
	{
		switch (C)
		{
		case TEXT('U'): return EHyperTwistClassicCubeFace::Up;
		case TEXT('D'): return EHyperTwistClassicCubeFace::Down;
		case TEXT('F'): return EHyperTwistClassicCubeFace::Front;
		case TEXT('B'): return EHyperTwistClassicCubeFace::Back;
		case TEXT('R'): return EHyperTwistClassicCubeFace::Right;
		case TEXT('L'): return EHyperTwistClassicCubeFace::Left;
		}
		return EHyperTwistClassicCubeFace::Up;
	};

	for (const FString& Move : MoveStrings)
	{
		if (Move.Len() == 0)
		{
			continue;
		}

		EHyperTwistClassicCubeFace Face = ParseFaceChar(Move[0]);
		EHyperTwistRotationDirection Dir = EHyperTwistRotationDirection::Clockwise;
		int32 Count = 1;

		if (Move.Len() > 1)
		{
			if (Move[1] == TEXT('\''))
			{
				Dir = EHyperTwistRotationDirection::CounterClockwise;
			}
			else if (Move[1] == TEXT('2'))
			{
				Count = 2;
			}
		}

		for (int32 i = 0; i < Count; ++i)
		{
			RotateFace(Face, Dir);
		}
	}
}

void AHyperTwistClassicCubeActor::ClearPieces()
{
	for (const FTrackedPiece& Piece : TrackedPieces)
	{
		if (Piece.Mesh && Piece.Mesh->IsValidLowLevel())
		{
			Piece.Mesh->DestroyComponent();
		}
	}
	TrackedPieces.Empty();

	if (ActiveRotation.Pivot && ActiveRotation.Pivot->IsValidLowLevel())
	{
		ActiveRotation.Pivot->DestroyComponent();
	}
	ActiveRotation = FActiveRotation();
	bIsAnimating = false;
}

void AHyperTwistClassicCubeActor::GenerateCube()
{
	TrackedPieces.SetNum(27);

	struct FPieceDef
	{
		FVector GridPos;
		TArray<EHyperTwistClassicCubeFace> IdentityFaces;
	};

	const TArray<FPieceDef> Pieces = {
		// Centers
		{FVector(0, 0, 1),   {EHyperTwistClassicCubeFace::Up}},
		{FVector(0, 0, -1),  {EHyperTwistClassicCubeFace::Down}},
		{FVector(0, 1, 0),   {EHyperTwistClassicCubeFace::Front}},
		{FVector(0, -1, 0),  {EHyperTwistClassicCubeFace::Back}},
		{FVector(-1, 0, 0),  {EHyperTwistClassicCubeFace::Left}},
		{FVector(1, 0, 0),   {EHyperTwistClassicCubeFace::Right}},
		// Edges
		{FVector(0, 1, 1),   {EHyperTwistClassicCubeFace::Up,    EHyperTwistClassicCubeFace::Front}},
		{FVector(1, 0, 1),   {EHyperTwistClassicCubeFace::Up,    EHyperTwistClassicCubeFace::Right}},
		{FVector(0, -1, 1),  {EHyperTwistClassicCubeFace::Up,    EHyperTwistClassicCubeFace::Back}},
		{FVector(-1, 0, 1),  {EHyperTwistClassicCubeFace::Up,    EHyperTwistClassicCubeFace::Left}},
		{FVector(0, 1, -1),  {EHyperTwistClassicCubeFace::Down,  EHyperTwistClassicCubeFace::Front}},
		{FVector(1, 0, -1),  {EHyperTwistClassicCubeFace::Down,  EHyperTwistClassicCubeFace::Right}},
		{FVector(0, -1, -1), {EHyperTwistClassicCubeFace::Down,  EHyperTwistClassicCubeFace::Back}},
		{FVector(-1, 0, -1), {EHyperTwistClassicCubeFace::Down,  EHyperTwistClassicCubeFace::Left}},
		{FVector(1, 1, 0),   {EHyperTwistClassicCubeFace::Front, EHyperTwistClassicCubeFace::Right}},
		{FVector(-1, 1, 0),  {EHyperTwistClassicCubeFace::Front, EHyperTwistClassicCubeFace::Left}},
		{FVector(1, -1, 0),  {EHyperTwistClassicCubeFace::Back,  EHyperTwistClassicCubeFace::Right}},
		{FVector(-1, -1, 0), {EHyperTwistClassicCubeFace::Back,  EHyperTwistClassicCubeFace::Left}},
		// Corners
		{FVector(1, 1, 1),    {EHyperTwistClassicCubeFace::Up,    EHyperTwistClassicCubeFace::Front, EHyperTwistClassicCubeFace::Right}},
		{FVector(-1, 1, 1),   {EHyperTwistClassicCubeFace::Up,    EHyperTwistClassicCubeFace::Front, EHyperTwistClassicCubeFace::Left}},
		{FVector(1, -1, 1),   {EHyperTwistClassicCubeFace::Up,    EHyperTwistClassicCubeFace::Back,  EHyperTwistClassicCubeFace::Right}},
		{FVector(-1, -1, 1),  {EHyperTwistClassicCubeFace::Up,    EHyperTwistClassicCubeFace::Back,  EHyperTwistClassicCubeFace::Left}},
		{FVector(1, 1, -1),   {EHyperTwistClassicCubeFace::Down,  EHyperTwistClassicCubeFace::Front, EHyperTwistClassicCubeFace::Right}},
		{FVector(-1, 1, -1),  {EHyperTwistClassicCubeFace::Down,  EHyperTwistClassicCubeFace::Front, EHyperTwistClassicCubeFace::Left}},
		{FVector(1, -1, -1),  {EHyperTwistClassicCubeFace::Down,  EHyperTwistClassicCubeFace::Back,  EHyperTwistClassicCubeFace::Right}},
		{FVector(-1, -1, -1), {EHyperTwistClassicCubeFace::Down,  EHyperTwistClassicCubeFace::Back,  EHyperTwistClassicCubeFace::Left}},
	};

	for (const FPieceDef& Def : Pieces)
	{
		CreatePiece(Def.GridPos, Def.IdentityFaces);
	}
}

void AHyperTwistClassicCubeActor::CreatePiece(const FVector& GridPos, const TArray<EHyperTwistClassicCubeFace>& IdentityFaces)
{
	UProceduralMeshComponent* Mesh = NewObject<UProceduralMeshComponent>(this, NAME_None, RF_Transactional);
	Mesh->RegisterComponent();
	Mesh->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);

	FVector Center = GridPos * (CubeletSize + Gap);
	float HalfSize = CubeletSize * 0.5f;

	for (int32 i = 0; i < 6; ++i)
	{
		EHyperTwistClassicCubeFace Face = static_cast<EHyperTwistClassicCubeFace>(i);
		UMaterialInterface* Mat = nullptr;
		int32 FaceIndex = static_cast<int32>(Face);
		if (IdentityFaces.Contains(Face) && FaceMaterials.IsValidIndex(FaceIndex) && FaceMaterials[FaceIndex] != nullptr)
		{
			Mat = FaceMaterials[FaceIndex];
		}
		else if (InternalMaterial != nullptr && !IdentityFaces.Contains(Face))
		{
			Mat = InternalMaterial;
		}
		CreateCubeletFace(Mesh, i, Center, HalfSize, Face, Mat);
	}

	int32 Index = GridToIndex(GridPos);
	if (TrackedPieces.IsValidIndex(Index))
	{
		TrackedPieces[Index] = { Mesh, GridPos, FQuat::Identity, IdentityFaces };
	}
}

void AHyperTwistClassicCubeActor::CreateCubeletFace(UProceduralMeshComponent* Mesh, int32 SectionIndex,
	const FVector& Center, float HalfSize, EHyperTwistClassicCubeFace Face, UMaterialInterface* Material)
{
	TArray<FVector> Vertices;
	TArray<int32> Triangles;
	TArray<FVector> Normals;
	TArray<FVector2D> UVs;
	TArray<FColor> Colors;
	TArray<FProcMeshTangent> Tangents;

	FVector Normal = FVector::ZeroVector;
	FVector TangentX = FVector::ZeroVector;
	TArray<FVector> FaceVerts;

	switch (Face)
	{
	case EHyperTwistClassicCubeFace::Up: // +Z
		Normal = FVector(0, 0, 1);
		TangentX = FVector(1, 0, 0);
		FaceVerts = {
			Center + FVector(-HalfSize, -HalfSize, +HalfSize),
			Center + FVector(+HalfSize, -HalfSize, +HalfSize),
			Center + FVector(+HalfSize, +HalfSize, +HalfSize),
			Center + FVector(-HalfSize, +HalfSize, +HalfSize)
		};
		break;

	case EHyperTwistClassicCubeFace::Down: // -Z
		Normal = FVector(0, 0, -1);
		TangentX = FVector(-1, 0, 0);
		FaceVerts = {
			Center + FVector(+HalfSize, -HalfSize, -HalfSize),
			Center + FVector(-HalfSize, -HalfSize, -HalfSize),
			Center + FVector(-HalfSize, +HalfSize, -HalfSize),
			Center + FVector(+HalfSize, +HalfSize, -HalfSize)
		};
		break;

	case EHyperTwistClassicCubeFace::Front: // +Y
		Normal = FVector(0, 1, 0);
		TangentX = FVector(1, 0, 0);
		FaceVerts = {
			Center + FVector(-HalfSize, +HalfSize, -HalfSize),
			Center + FVector(+HalfSize, +HalfSize, -HalfSize),
			Center + FVector(+HalfSize, +HalfSize, +HalfSize),
			Center + FVector(-HalfSize, +HalfSize, +HalfSize)
		};
		break;

	case EHyperTwistClassicCubeFace::Back: // -Y
		Normal = FVector(0, -1, 0);
		TangentX = FVector(-1, 0, 0);
		FaceVerts = {
			Center + FVector(+HalfSize, -HalfSize, -HalfSize),
			Center + FVector(-HalfSize, -HalfSize, -HalfSize),
			Center + FVector(-HalfSize, -HalfSize, +HalfSize),
			Center + FVector(+HalfSize, -HalfSize, +HalfSize)
		};
		break;

	case EHyperTwistClassicCubeFace::Right: // +X
		Normal = FVector(1, 0, 0);
		TangentX = FVector(0, 1, 0);
		FaceVerts = {
			Center + FVector(+HalfSize, -HalfSize, -HalfSize),
			Center + FVector(+HalfSize, +HalfSize, -HalfSize),
			Center + FVector(+HalfSize, +HalfSize, +HalfSize),
			Center + FVector(+HalfSize, -HalfSize, +HalfSize)
		};
		break;

	case EHyperTwistClassicCubeFace::Left: // -X
		Normal = FVector(-1, 0, 0);
		TangentX = FVector(0, -1, 0);
		FaceVerts = {
			Center + FVector(-HalfSize, +HalfSize, -HalfSize),
			Center + FVector(-HalfSize, -HalfSize, -HalfSize),
			Center + FVector(-HalfSize, -HalfSize, +HalfSize),
			Center + FVector(-HalfSize, +HalfSize, +HalfSize)
		};
		break;
	}

	Vertices = FaceVerts;
	Triangles = { 0, 1, 2, 0, 2, 3 };

	for (int32 i = 0; i < 4; ++i)
	{
		Normals.Add(Normal);
		UVs.Add(FVector2D((i == 1 || i == 2) ? 1.0f : 0.0f, (i >= 2) ? 1.0f : 0.0f));
		Colors.Add(FColor::White);
		Tangents.Add(FProcMeshTangent(TangentX, false));
	}

	Mesh->CreateMeshSection(SectionIndex, Vertices, Triangles, Normals, UVs, Colors, Tangents, true);
	if (Material)
	{
		Mesh->SetMaterial(SectionIndex, Material);
	}
}

bool AHyperTwistClassicCubeActor::IsAnimating() const
{
	return bIsAnimating;
}

void AHyperTwistClassicCubeActor::RotateFace(EHyperTwistClassicCubeFace Face, EHyperTwistRotationDirection Direction)
{
	RotationQueue.Add(TPair<EHyperTwistClassicCubeFace, EHyperTwistRotationDirection>(Face, Direction));
	ProcessRotationQueue();
}

void AHyperTwistClassicCubeActor::ProcessRotationQueue()
{
	if (bIsAnimating || RotationQueue.IsEmpty())
	{
		return;
	}

	auto Next = RotationQueue[0];
	RotationQueue.RemoveAt(0);
	StartFaceRotation(Next.Key, Next.Value);
}

void AHyperTwistClassicCubeActor::StartFaceRotation(EHyperTwistClassicCubeFace Face, EHyperTwistRotationDirection Direction)
{
	TArray<int32> Indices = GetFacePieceIndices(TrackedPieces, Face);
	if (Indices.IsEmpty())
	{
		ProcessRotationQueue();
		return;
	}

	// Create pivot at face center
	USceneComponent* Pivot = NewObject<USceneComponent>(this, NAME_None, RF_Transactional);
	Pivot->RegisterComponent();
	Pivot->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
	Pivot->SetWorldLocation(GetFaceCenter(Face) * (CubeletSize + Gap));

	// Attach face pieces to pivot (keep world transform)
	for (int32 Idx : Indices)
	{
		if (TrackedPieces[Idx].Mesh)
		{
			TrackedPieces[Idx].Mesh->AttachToComponent(Pivot, FAttachmentTransformRules::KeepWorldTransform);
		}
	}

	FQuat FaceRot = GetFaceRotationQuat(Face, Direction);
	ActiveRotation.Pivot = Pivot;
	ActiveRotation.PieceIndices = Indices;
	ActiveRotation.PivotStartRot = FQuat::Identity;
	ActiveRotation.PivotTargetRot = FaceRot;
	ActiveRotation.StartTime = GetWorld()->GetTimeSeconds();
	ActiveRotation.Duration = TurnDuration;
	ActiveRotation.Face = Face;
	ActiveRotation.Direction = Direction;
	bIsAnimating = true;
}

void AHyperTwistClassicCubeActor::FinalizeRotation()
{
	if (!ActiveRotation.Pivot)
	{
		bIsAnimating = false;
		ProcessRotationQueue();
		return;
	}

	// Detach pieces from pivot, keeping their world transforms
	for (int32 Idx : ActiveRotation.PieceIndices)
	{
		if (TrackedPieces[Idx].Mesh)
		{
			TrackedPieces[Idx].Mesh->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
		}
	}

	// Compute and apply new grid positions and orientations
	FQuat FaceRot = ActiveRotation.PivotTargetRot;
	for (int32 Idx : ActiveRotation.PieceIndices)
	{
		FTrackedPiece& Piece = TrackedPieces[Idx];
		Piece.GridPos = RotateGridPosition(Piece.GridPos, ActiveRotation.Face, ActiveRotation.Direction);
		Piece.Orientation = FaceRot * Piece.Orientation;
	}

	// Destroy pivot
	ActiveRotation.Pivot->DestroyComponent();
	ActiveRotation = FActiveRotation();
	bIsAnimating = false;
	ProcessRotationQueue();
}

int32 AHyperTwistClassicCubeActor::GridToIndex(const FVector& GridPos) const
{
	return (FMath::RoundToInt(GridPos.X) + 1) * 9 + (FMath::RoundToInt(GridPos.Y) + 1) * 3 + (FMath::RoundToInt(GridPos.Z) + 1);
}

FVector AHyperTwistClassicCubeActor::IndexToGrid(int32 Index) const
{
	int32 X = Index / 9 - 1;
	int32 Y = (Index % 9) / 3 - 1;
	int32 Z = Index % 3 - 1;
	return FVector((float)X, (float)Y, (float)Z);
}

FVector AHyperTwistClassicCubeActor::RotateGridPosition(const FVector& Pos, EHyperTwistClassicCubeFace Face, EHyperTwistRotationDirection Direction)
{
	const float X = Pos.X;
	const float Y = Pos.Y;
	const float Z = Pos.Z;

	if (Direction == EHyperTwistRotationDirection::Clockwise)
	{
		switch (Face)
		{
		case EHyperTwistClassicCubeFace::Up:    return FVector( Y, -X,  Z);
		case EHyperTwistClassicCubeFace::Down:  return FVector(-Y,  X,  Z);
		case EHyperTwistClassicCubeFace::Front: return FVector(-Z,  Y,  X);
		case EHyperTwistClassicCubeFace::Back:  return FVector(-Z,  Y,  X);
		case EHyperTwistClassicCubeFace::Right: return FVector( X, -Z,  Y);
		case EHyperTwistClassicCubeFace::Left:  return FVector( X, -Z,  Y);
		}
	}
	else
	{
		switch (Face)
		{
		case EHyperTwistClassicCubeFace::Up:    return FVector(-Y,  X,  Z);
		case EHyperTwistClassicCubeFace::Down:  return FVector( Y, -X,  Z);
		case EHyperTwistClassicCubeFace::Front: return FVector( Z,  Y, -X);
		case EHyperTwistClassicCubeFace::Back:  return FVector( Z,  Y, -X);
		case EHyperTwistClassicCubeFace::Right: return FVector( X,  Z, -Y);
		case EHyperTwistClassicCubeFace::Left:  return FVector( X,  Z, -Y);
		}
	}
	return Pos;
}

FQuat AHyperTwistClassicCubeActor::GetFaceRotationQuat(EHyperTwistClassicCubeFace Face, EHyperTwistRotationDirection Direction)
{
	const bool bCW = (Direction == EHyperTwistRotationDirection::Clockwise);
	switch (Face)
	{
	case EHyperTwistClassicCubeFace::Up:    return FQuat(FRotator(0.0f,  bCW ? -90.0f : 90.0f, 0.0f));
	case EHyperTwistClassicCubeFace::Down:  return FQuat(FRotator(0.0f,  bCW ?  90.0f : -90.0f, 0.0f));
	case EHyperTwistClassicCubeFace::Front: return FQuat(FRotator(bCW ? -90.0f : 90.0f, 0.0f, 0.0f));
	case EHyperTwistClassicCubeFace::Back:  return FQuat(FRotator(bCW ? -90.0f : 90.0f, 0.0f, 0.0f));
	case EHyperTwistClassicCubeFace::Right: return FQuat(FRotator(0.0f, 0.0f, bCW ? 90.0f : -90.0f));
	case EHyperTwistClassicCubeFace::Left:  return FQuat(FRotator(0.0f, 0.0f, bCW ? 90.0f : -90.0f));
	}
	return FQuat::Identity;
}

TArray<int32> AHyperTwistClassicCubeActor::GetFacePieceIndices(const TArray<FTrackedPiece>& Pieces, EHyperTwistClassicCubeFace Face)
{
	TArray<int32> Result;
	for (int32 i = 0; i < Pieces.Num(); ++i)
	{
		if (!Pieces[i].Mesh)
		{
			continue;
		}
		const FVector& Pos = Pieces[i].GridPos;
		bool bOnFace = false;
		switch (Face)
		{
		case EHyperTwistClassicCubeFace::Up:    bOnFace = FMath::IsNearlyEqual(Pos.Z, 1.0f);  break;
		case EHyperTwistClassicCubeFace::Down:  bOnFace = FMath::IsNearlyEqual(Pos.Z, -1.0f); break;
		case EHyperTwistClassicCubeFace::Front: bOnFace = FMath::IsNearlyEqual(Pos.Y, 1.0f);  break;
		case EHyperTwistClassicCubeFace::Back:  bOnFace = FMath::IsNearlyEqual(Pos.Y, -1.0f); break;
		case EHyperTwistClassicCubeFace::Right: bOnFace = FMath::IsNearlyEqual(Pos.X, 1.0f);  break;
		case EHyperTwistClassicCubeFace::Left:  bOnFace = FMath::IsNearlyEqual(Pos.X, -1.0f); break;
		}
		if (bOnFace)
		{
			Result.Add(i);
		}
	}
	return Result;
}

FVector AHyperTwistClassicCubeActor::GetFaceCenter(EHyperTwistClassicCubeFace Face)
{
	switch (Face)
	{
	case EHyperTwistClassicCubeFace::Up:    return FVector(0, 0, 1);
	case EHyperTwistClassicCubeFace::Down:  return FVector(0, 0, -1);
	case EHyperTwistClassicCubeFace::Front: return FVector(0, 1, 0);
	case EHyperTwistClassicCubeFace::Back:  return FVector(0, -1, 0);
	case EHyperTwistClassicCubeFace::Right: return FVector(1, 0, 0);
	case EHyperTwistClassicCubeFace::Left:  return FVector(-1, 0, 0);
	}
	return FVector::ZeroVector;
}
