#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "HyperTwistSimulation/HyperTwistClassicCubeTypes.h"
#include "ProceduralMeshComponent.h"
#include "HyperTwistClassicCubeActor.generated.h"

UCLASS()
class UNREALHYPERTWIST_API AHyperTwistClassicCubeActor : public AActor
{
	GENERATED_BODY()

public:
	AHyperTwistClassicCubeActor();

	/** Size of each cubelet (one small cube) in unreal units. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Cube")
	float CubeletSize = 10.0f;

	/** Gap between cubelets in unreal units. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Cube")
	float Gap = 0.5f;

	/** If true, generate cube geometry in OnConstruction (visible in editor). */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Cube")
	bool bGenerateOnConstruction = true;

	/**
	 * Override materials for each outward face.
	 * Index order matches EHyperTwistClassicCubeFace: Up, Down, Front, Back, Left, Right.
	 * If null, the default engine material is used.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Cube")
	TArray<UMaterialInterface*> FaceMaterials;

	/** Material for internal (non-visible) faces. If null, the default engine material is used. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Cube")
	UMaterialInterface* InternalMaterial;

	/** Duration of a quarter-turn animation in seconds. */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Cube")
	float TurnDuration = 0.15f;

	/** Rotate one face of the cube. Queued if already animating. */
	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Cube")
	void RotateFace(EHyperTwistClassicCubeFace Face, EHyperTwistRotationDirection Direction);

	/** True if a face rotation animation is in progress. */
	UFUNCTION(BlueprintPure, Category = "HyperTwist|Cube")
	bool IsAnimating() const;

	/** Reset the cube to solved state. */
	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Cube")
	void ResetCube();

	/**
	 * Process a click ray. If it hits a visible cube face, rotates that face.
	 * @param RayOrigin    World-space ray origin (e.g. camera location)
	 * @param RayDirection World-space ray direction (normalized)
	 * @param bCounterClockwise If true, rotate counter-clockwise; else clockwise
	 * @return True if a face was hit and rotation was queued
	 */
	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Cube")
	bool ProcessClick(const FVector& RayOrigin, const FVector& RayDirection, bool bCounterClockwise = false);

	/** Generate a random scramble of the given length (WCA-style notation). */
	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Cube")
	TArray<FString> GenerateScramble(int32 Length = 20);

	/** Apply a scramble from WCA-style move strings (e.g. ["R", "U'", "F2", ...]). */
	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Cube")
	void ApplyScramble(const TArray<FString>& MoveStrings);

	virtual void OnConstruction(const FTransform& Transform) override;

protected:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;

private:
	struct FTrackedPiece
	{
		UProceduralMeshComponent* Mesh = nullptr;
		FVector GridPos = FVector::ZeroVector;
		FQuat Orientation = FQuat::Identity;
		TArray<EHyperTwistClassicCubeFace> IdentityFaces;
	};

	struct FActiveRotation
	{
		USceneComponent* Pivot = nullptr;
		TArray<int32> PieceIndices;
		FQuat PivotStartRot;
		FQuat PivotTargetRot;
		float StartTime = 0.0f;
		float Duration = 0.15f;
		EHyperTwistClassicCubeFace Face = EHyperTwistClassicCubeFace::Up;
		EHyperTwistRotationDirection Direction = EHyperTwistRotationDirection::Clockwise;
	};

	void GenerateCube();
	void ClearPieces();
	void CreatePiece(const FVector& GridPos, const TArray<EHyperTwistClassicCubeFace>& IdentityFaces);
	void CreateCubeletFace(UProceduralMeshComponent* Mesh, int32 SectionIndex,
		const FVector& Center, float HalfSize, EHyperTwistClassicCubeFace Face,
		UMaterialInterface* Material);

	void StartFaceRotation(EHyperTwistClassicCubeFace Face, EHyperTwistRotationDirection Direction);
	void FinalizeRotation();
	void ProcessRotationQueue();

	static FVector RotateGridPosition(const FVector& Pos, EHyperTwistClassicCubeFace Face, EHyperTwistRotationDirection Direction);
	static FQuat GetFaceRotationQuat(EHyperTwistClassicCubeFace Face, EHyperTwistRotationDirection Direction);
	static TArray<int32> GetFacePieceIndices(const TArray<FTrackedPiece>& Pieces, EHyperTwistClassicCubeFace Face);
	static FVector GetFaceCenter(EHyperTwistClassicCubeFace Face);

	int32 GridToIndex(const FVector& GridPos) const;
	FVector IndexToGrid(int32 Index) const;

	TArray<FTrackedPiece> TrackedPieces;
	TArray<TPair<EHyperTwistClassicCubeFace, EHyperTwistRotationDirection>> RotationQueue;
	FActiveRotation ActiveRotation;
	bool bIsAnimating = false;
};
