#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "HyperTwistSimulation/HyperTwistVirtual3333ProjectionLibrary.h"
#include "HyperTwistVirtual3333ProjectionActor.generated.h"

class UMaterialInterface;
class UProceduralMeshComponent;
class USceneComponent;

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

public:
	AHyperTwistVirtual3333ProjectionActor();

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

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

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333|Rendering")
	float TesseractSize = 42.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333|Rendering")
	float TesseractGap = 8.0f;

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

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

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333|Rendering")
	float FaceOpacity = 0.96f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333|Rendering")
	float EmptyFaceOpacity = 0.18f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Virtual3333|Rendering")
	float ShellOpacity = 0.08f;

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

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

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Virtual3333")
	FHyperTwistVirtual3333RuntimeState CurrentState;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Virtual3333")
	FHyperTwistVirtual3333VisibleProjection CurrentProjection;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Virtual3333")
	EHyperTwistVirtual3333Axis CurrentSliceAxis = EHyperTwistVirtual3333Axis::W;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Virtual3333")
	int32 CurrentSliceCoordinate = 1;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "HyperTwist|Virtual3333")
	EHyperTwistVirtual3333Axis CurrentRotationAxis = EHyperTwistVirtual3333Axis::X;

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

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

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

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

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

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

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool SetPuzzleOrder(int32 NewOrder);

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

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool ApplyCurrentSelectionClockwise();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool ApplyCurrentSelectionCounterClockwise();

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool SetSliceAxis(EHyperTwistVirtual3333Axis SliceAxis);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool SetSliceCoordinate(int32 SliceCoordinate);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool SetSliceLayer(int32 OneBasedLayer);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool CycleSliceCoordinate(int32 DirectionStep = 1);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool SetRotationAxis(EHyperTwistVirtual3333Axis RotationAxis);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	bool CycleRotationAxis(int32 DirectionStep = 1);

	UFUNCTION(BlueprintCallable, Category = "HyperTwist|Virtual3333")
	void ToggleCellShellRendering();

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

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

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

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333|Persistence")
	FString GetRuntimeSaveSlotName() const;

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

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	int32 GetRenderableTesseractCount() const
	{
		return SpawnedTesseractMeshes.Num();
	}

	UFUNCTION(BlueprintPure, Category = "HyperTwist|Virtual3333")
	FString GetSelectionLabel() const;

protected:
	void EnsureSelectionIsValid();
	void ClearRenderedTesseracts();
	void BuildRenderedTesseracts();
	void BuildProjectedTesseract(const FHyperTwistVirtual3333ProjectedTesseract& TesseractProjection);
	void CreateTesseractFace(
		UProceduralMeshComponent* Mesh,
		int32 SectionIndex,
		const FVector& Center,
		float HalfSize,
		int32 DisplayAxisIndex,
		bool bPositiveSide,
		const FLinearColor& FaceColor
	) const;
	static FLinearColor ResolveStickerColor(
		EHyperTwistMelindaStickerColor StickerColor,
		bool bHasSticker,
		float StickerOpacity,
		float EmptyFaceOpacity
	);
	static FLinearColor ResolveShellColor(float Opacity);

	UPROPERTY(Transient)
	TArray<TObjectPtr<UProceduralMeshComponent>> SpawnedTesseractMeshes;
};
