#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "HyperTwistSimulation/HyperTwistMelindaProjectionLibrary.h"
#include "HyperTwistMelindaProjectionActor.generated.h"

class UProceduralMeshComponent;
class USceneComponent;
class UMaterialInterface;

UCLASS(BlueprintType, Blueprintable)
class UNREALHYPERTWIST_API AHyperTwistMelindaProjectionActor : public AActor
{
	GENERATED_BODY()

public:
	AHyperTwistMelindaProjectionActor();

	virtual void OnConstruction(const FTransform& Transform) override;
	virtual void BeginPlay() override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Melinda|Rendering")
	float CellCenterSpacing = 120.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Melinda|Rendering")
	float CubieSize = 14.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Melinda|Rendering")
	float CubieGap = 1.75f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Melinda|Rendering")
	bool bGenerateOnConstruction = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Melinda|Rendering")
	bool bUsePreviewRandomState = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Melinda|Rendering")
	int32 PreviewRandomSeed = 2026;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Melinda")
	TObjectPtr<USceneComponent> SceneRoot = nullptr;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Melinda|Rendering")
	TObjectPtr<UMaterialInterface> VertexColorMaterial = nullptr;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Melinda")
	FHyperTwistPuzzleState CurrentState;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Melinda")
	FHyperTwistMelindaCellFirstProjection CurrentProjection;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Melinda")
	FString LastAppliedNotation;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Melinda")
	TArray<FString> LastWarnings;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Melinda|Persistence")
	FString LastPersistenceStatus = TEXT("not loaded");

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Melinda")
	void ResetToSolvedState();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Melinda")
	bool GenerateRandomState(int32 RandomSeed);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Melinda")
	bool ApplyTurnRequest(const FHyperTwistMelindaCellTurnRequest& Request);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Melinda")
	bool ProcessClick(
		const FVector& RayOrigin,
		const FVector& RayDirection,
		bool bCounterClockwise = false
	);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Melinda")
	bool RefreshProjection();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Melinda|Persistence")
	bool SaveRuntimeState();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Melinda|Persistence")
	bool LoadRuntimeState();

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Melinda|Persistence")
	static FString GetRuntimeSaveSlotName();

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Melinda")
	bool HasValidProjection() const
	{
		return CurrentProjection.IsStructurallyValid();
	}

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Melinda")
	int32 GetRenderableCubieViewCount() const
	{
		return SpawnedCubieMeshes.Num();
	}

protected:
	struct FDisplayedCubieMetadata
	{
		EHyperTwistMelindaCell Cell = EHyperTwistMelindaCell::Outer;
		TArray<EHyperTwistMelindaCellAxis> LocalAxes;
	};

	void ClearRenderedCells();
	void BuildRenderedCells();
	void BuildProjectedCellRoot(const FHyperTwistMelindaProjectedCell& CellProjection);
	void BuildProjectedCubie(
		USceneComponent* CellRoot,
		const FHyperTwistMelindaProjectedCell& CellProjection,
		const FHyperTwistMelindaProjectedCubie& CubieProjection
	);
	void CreateCubieFace(
		UProceduralMeshComponent* Mesh,
		int32 SectionIndex,
		const FVector& Center,
		float HalfSize,
		int32 LocalAxisIndex,
		bool bPositiveSide,
		const FLinearColor& FaceColor
	);
	static bool TryResolveLocalAxisFromImpactNormal(
		const FVector& ImpactNormal,
		int32& OutLocalAxisIndex
	);
	static FLinearColor ResolveStickerColor(EHyperTwistMelindaStickerColor StickerColor);

	UPROPERTY(Transient)
	TArray<TObjectPtr<USceneComponent>> SpawnedCellRoots;

	UPROPERTY(Transient)
	TArray<TObjectPtr<UProceduralMeshComponent>> SpawnedCubieMeshes;

	TMap<UProceduralMeshComponent*, FDisplayedCubieMetadata> DisplayedCubieMetadata;
};
