#pragma once

// Clean-room implementation for HyperTwist puzzle viewer subsystem — Bound 1
// Lane: cubing/twisty.js (GPL-3.0-or-later — restrictive clean-room lane)
// Implemented from governance docs and abstract idea handoff only.
// No source code inspection. Clean-room workflow only.
// Date: 2026-05-17

#include "CoreMinimal.h"
#include "HyperTwistCore/HyperTwistCoreTypes.h"
#include "HyperTwistReplay/HyperTwistReplayTypes.h"
#include "HyperTwistViewerTypes.generated.h"

// ---------------------------------------------------------------------------
// Playback mode — what the viewer is currently doing
// ---------------------------------------------------------------------------

UENUM(BlueprintType)
enum class EHyperTwistViewerPlaybackMode : uint8
{
	Stopped   UMETA(DisplayName = "Stopped"),
	Playing   UMETA(DisplayName = "Playing"),
	Paused    UMETA(DisplayName = "Paused"),
	Stepping  UMETA(DisplayName = "Stepping")
};

UENUM(BlueprintType)
enum class EHyperTwistViewerPlaybackDirection : uint8
{
	None     UMETA(DisplayName = "None"),
	Forward  UMETA(DisplayName = "Forward"),
	Backward UMETA(DisplayName = "Backward")
};

UENUM(BlueprintType)
enum class EHyperTwistViewerJumpKind : uint8
{
	Seek       UMETA(DisplayName = "Seek"),
	StepForward UMETA(DisplayName = "Step Forward"),
	StepBack   UMETA(DisplayName = "Step Back"),
	Stop       UMETA(DisplayName = "Stop"),
	PlayWrap   UMETA(DisplayName = "Play Wrap")
};

UENUM(BlueprintType)
enum class EHyperTwistViewerEmbeddedHostBindingMode : uint8
{
	InlineCanvas UMETA(DisplayName = "Inline Canvas"),
	ScissoredView UMETA(DisplayName = "Scissored View")
};

UENUM(BlueprintType)
enum class EHyperTwistViewerVisualizationMode : uint8
{
	TwoD UMETA(DisplayName = "2D"),
	ThreeD UMETA(DisplayName = "3D")
};

UENUM(BlueprintType)
enum class EHyperTwistViewerVisualizationPreference : uint8
{
	Prefer2D UMETA(DisplayName = "Prefer 2D"),
	Prefer3D UMETA(DisplayName = "Prefer 3D")
};

UENUM(BlueprintType)
enum class EHyperTwistViewerVisualizationFallbackReason : uint8
{
	None UMETA(DisplayName = "None"),
	HostBindingDoesNotSupport3D UMETA(DisplayName = "Host Binding Does Not Support 3D"),
	RenderProfilePrefers2D UMETA(DisplayName = "Render Profile Prefers 2D"),
	MissingSupportContract UMETA(DisplayName = "Missing Support Contract")
};

UENUM(BlueprintType)
enum class EHyperTwistViewerPresentationMode : uint8
{
	Inline UMETA(DisplayName = "Inline"),
	Expanded UMETA(DisplayName = "Expanded"),
	Fullscreen UMETA(DisplayName = "Fullscreen")
};

// ---------------------------------------------------------------------------
// Control action — what the caller wants the viewer to do
// ---------------------------------------------------------------------------

UENUM(BlueprintType)
enum class EHyperTwistViewerControlAction : uint8
{
	Play          UMETA(DisplayName = "Play"),
	Pause         UMETA(DisplayName = "Pause"),
	Stop          UMETA(DisplayName = "Stop"),
	StepForward   UMETA(DisplayName = "Step Forward"),
	StepBack      UMETA(DisplayName = "Step Back"),
	SeekToIndex   UMETA(DisplayName = "Seek To Index")
};

// ---------------------------------------------------------------------------
// Move entry — a single flat move in the viewer's move list
// HintPositionSeconds is an optional wall-clock hint for scrubber mapping.
// Animation interpretation is deferred to Bound 2.
// ---------------------------------------------------------------------------

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerMoveEntry
{
	GENERATED_BODY()

	// The logical transformation this entry represents.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistTransformation Transform;

	// Short human-readable label shown in the control bar (e.g. "R", "U'", "2F2").
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString DisplayLabel;

	// Optional wall-clock hint for scrubber position mapping.
	// 0.0 means no hint provided. Used by Bound 2 animation observer.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	float HintPositionSeconds = 0.0f;

	bool IsStructurallyValid() const
	{
		return Transform.IsStructurallyValid() && !DisplayLabel.IsEmpty();
	}
};

// ---------------------------------------------------------------------------
// Playback state — the viewer's current observable state
// Produced by UHyperTwistPuzzleViewerComponent and consumed by UMG / HUD.
// ---------------------------------------------------------------------------

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerPlaybackState
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerPlaybackMode Mode = EHyperTwistViewerPlaybackMode::Stopped;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerPlaybackDirection Direction = EHyperTwistViewerPlaybackDirection::None;

	// Zero-based index of the move currently at the playhead.
	// INDEX_NONE (-1) when the move list is empty.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 CurrentMoveIndex = INDEX_NONE;

	// Total number of moves in the loaded list.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 TotalMoveCount = 0;

	// Replay-backed current timeline time in milliseconds.
	// Falls back to 0 when no replay packet is loaded.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 CurrentTimeMs = 0;

	// Replay-backed total timeline duration in milliseconds.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 TotalTimeMs = 0;

	// Replay-backed inspection time in milliseconds when available.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 InspectionTimeMs = 0;

	// True when CurrentMoveIndex == 0 (or list is empty).
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAtStart = true;

	// True when CurrentMoveIndex == TotalMoveCount - 1 (or list is empty).
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAtEnd = true;

	// Whether a valid scene context is currently loaded.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bSceneContextLoaded = false;

	// Whether the current shell state came from a replay packet.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bReplayLoaded = false;

	// Replay identity carried through the shell for host readout.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString ReplayId;

	// Puzzle identity carried through the shell for host readout.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString PuzzleId;

	// Replay result label derived from the canonical replay summary.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString ReplayResult;

	// Short status label for HUD/debug display.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString StatusLabel;

	bool IsStructurallyValid() const
	{
		if (TotalMoveCount < 0 || CurrentTimeMs < 0 || TotalTimeMs < 0 || InspectionTimeMs < 0)
		{
			return false;
		}
		if (TotalTimeMs > 0 && CurrentTimeMs > TotalTimeMs)
		{
			return false;
		}
		if (bReplayLoaded && ReplayId.IsEmpty())
		{
			return false;
		}
		if (TotalMoveCount == 0)
		{
			return CurrentMoveIndex == INDEX_NONE && bAtStart && bAtEnd;
		}
		return CurrentMoveIndex >= 0 && CurrentMoveIndex < TotalMoveCount;
	}
};

// ---------------------------------------------------------------------------
// Control request — a single caller-driven instruction to the viewer
// ---------------------------------------------------------------------------

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerControlRequest
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerControlAction Action = EHyperTwistViewerControlAction::Stop;

	// Only meaningful when Action == SeekToIndex.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 SeekTargetIndex = 0;

	bool IsStructurallyValid() const
	{
		if (Action == EHyperTwistViewerControlAction::SeekToIndex)
		{
			return SeekTargetIndex >= 0;
		}
		return true;
	}
};

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerInFlightMoveProgress
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 MoveIndex = INDEX_NONE;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistViewerMoveEntry MoveEntry;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerPlaybackDirection Direction = EHyperTwistViewerPlaybackDirection::None;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	float Fraction = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 StartedAtMs = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 EndsAtMs = 0;

	bool IsStructurallyValid() const
	{
		return MoveIndex >= 0
			&& MoveEntry.IsStructurallyValid()
			&& FMath::IsFinite(Fraction)
			&& Fraction >= 0.0f
			&& Fraction <= 1.0f
			&& StartedAtMs >= 0
			&& EndsAtMs >= StartedAtMs;
	}
};

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerPositionSnapshot
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 CurrentTimeMs = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 BaseCompletedMoveIndex = INDEX_NONE;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 BaseBoundaryTimeMs = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	TArray<FHyperTwistViewerInFlightMoveProgress> InFlightMoves;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAtBoundary = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAtStart = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAtEnd = false;

	bool IsStructurallyValid() const
	{
		if (CurrentTimeMs < 0 || BaseBoundaryTimeMs < 0)
		{
			return false;
		}

		if (BaseCompletedMoveIndex < INDEX_NONE)
		{
			return false;
		}

		for (const FHyperTwistViewerInFlightMoveProgress& Progress : InFlightMoves)
		{
			if (!Progress.IsStructurallyValid())
			{
				return false;
			}
		}

		return true;
	}
};

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerDirectionState
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerPlaybackMode Mode = EHyperTwistViewerPlaybackMode::Stopped;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerPlaybackDirection Direction = EHyperTwistViewerPlaybackDirection::None;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bIsContinuousPlayback = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bIsPaused = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bIsStepping = false;

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

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerJumpEvent
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerJumpKind JumpKind = EHyperTwistViewerJumpKind::Seek;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 PreviousTimeMs = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 NewTimeMs = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 PreviousMoveIndex = INDEX_NONE;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 NewMoveIndex = INDEX_NONE;

	bool IsStructurallyValid() const
	{
		return PreviousTimeMs >= 0 && NewTimeMs >= 0;
	}
};

// ---------------------------------------------------------------------------
// Bound 3 adapter and embedded-host bootstrap records
// ---------------------------------------------------------------------------

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerAdapterProjectionStep
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 MoveIndex = INDEX_NONE;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistTransformation Transform;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString DisplayLabel;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 StartsAtMs = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 CompletesAtMs = 0;

	bool IsStructurallyValid() const
	{
		return MoveIndex >= 0
			&& Transform.IsStructurallyValid()
			&& !DisplayLabel.IsEmpty()
			&& StartsAtMs >= 0
			&& CompletesAtMs >= StartsAtMs;
	}
};

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerClassicCubingAdapterProjection
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString ProjectionId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SemanticContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString GeometryContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString ViewerAdapterContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SceneContextId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString RenderStateProfile;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistPuzzleState BaseState;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 CurrentDisplayMoveIndex = INDEX_NONE;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	int32 TotalTimelineTimeMs = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistViewerPositionSnapshot PositionSnapshot;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	TArray<FHyperTwistViewerAdapterProjectionStep> ProjectedMoves;

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

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

	bool IsStructurallyValid() const
	{
		if (ProjectionId.IsEmpty()
			|| SemanticContractId.IsEmpty()
			|| GeometryContractId.IsEmpty()
			|| ViewerAdapterContractId.IsEmpty()
			|| SceneContextId.IsEmpty()
			|| RenderStateProfile.IsEmpty()
			|| !BaseState.IsStructurallyValid()
			|| !PositionSnapshot.IsStructurallyValid()
			|| TotalTimelineTimeMs < 0)
		{
			return false;
		}

		for (const FHyperTwistViewerAdapterProjectionStep& Step : ProjectedMoves)
		{
			if (!Step.IsStructurallyValid())
			{
				return false;
			}
		}

		return true;
	}
};

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerEmbeddedHostBootstrapRequest
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString HostInstanceId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerEmbeddedHostBindingMode BindingMode = EHyperTwistViewerEmbeddedHostBindingMode::InlineCanvas;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bBindTimelineSurface = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bBindScrubberSurface = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bBindStatusSurface = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAllowPointerInput = true;

	bool IsStructurallyValid() const
	{
		return !HostInstanceId.IsEmpty();
	}
};

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerEmbeddedHostBootstrapState
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString BootstrapId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString HostInstanceId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString AdapterProjectionId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString ViewerAdapterContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString BrowserEmbedContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SpatialSceneContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SceneContextId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString RenderStateProfile;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerEmbeddedHostBindingMode BindingMode = EHyperTwistViewerEmbeddedHostBindingMode::InlineCanvas;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bReadyForEmbed = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bTimelineSurfaceBound = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bScrubberSurfaceBound = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bStatusSurfaceBound = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bPointerInputEnabled = false;

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

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

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

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

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

	bool IsStructurallyValid() const
	{
		if (!bReadyForEmbed)
		{
			return BootstrapId.IsEmpty()
				&& HostInstanceId.IsEmpty()
				&& AdapterProjectionId.IsEmpty()
				&& ViewerAdapterContractId.IsEmpty()
				&& BrowserEmbedContractId.IsEmpty()
				&& SpatialSceneContractId.IsEmpty()
				&& SceneContextId.IsEmpty();
		}

		return !BootstrapId.IsEmpty()
			&& !HostInstanceId.IsEmpty()
			&& !AdapterProjectionId.IsEmpty()
			&& !ViewerAdapterContractId.IsEmpty()
			&& !BrowserEmbedContractId.IsEmpty()
			&& !SpatialSceneContractId.IsEmpty()
			&& !SceneContextId.IsEmpty()
			&& !RenderStateProfile.IsEmpty()
			&& BrowserLifecycleHookIds.Num() > 0
			&& SpatialLifecycleStages.Num() > 0;
	}
};

// ---------------------------------------------------------------------------
// Bound 4 visualization-shell records
// ---------------------------------------------------------------------------

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerVisualizationShellRequest
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistViewerEmbeddedHostBootstrapRequest HostBootstrapRequest;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerVisualizationPreference VisualizationPreference =
		EHyperTwistViewerVisualizationPreference::Prefer3D;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerPresentationMode InitialPresentationMode = EHyperTwistViewerPresentationMode::Inline;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAllow2DFallback = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAllowExpandedView = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bAllowFullscreen = true;

	bool IsStructurallyValid() const
	{
		return HostBootstrapRequest.IsStructurallyValid();
	}
};

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerVisualizationShellState
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString VisualizationId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString BootstrapId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString AdapterProjectionId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString ViewerAdapterContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString BrowserEmbedContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SpatialSceneContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SpatialUiContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SpatialEffectBoundaryId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SpatialStateMotionContractId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString SceneContextId;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString RenderStateProfile;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerVisualizationMode PreferredMode = EHyperTwistViewerVisualizationMode::ThreeD;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerVisualizationMode ActiveMode = EHyperTwistViewerVisualizationMode::TwoD;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerVisualizationFallbackReason FallbackReason =
		EHyperTwistViewerVisualizationFallbackReason::None;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	EHyperTwistViewerPresentationMode PresentationMode = EHyperTwistViewerPresentationMode::Inline;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bReadyForVisualization = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bUsingFallback = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bSupportsExpandedView = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bSupportsFullscreen = false;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	bool bUsesSpatialRendering = false;

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

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

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

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

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

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

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

	bool IsStructurallyValid() const
	{
		if (!bReadyForVisualization)
		{
			return VisualizationId.IsEmpty()
				&& BootstrapId.IsEmpty()
				&& AdapterProjectionId.IsEmpty()
				&& ViewerAdapterContractId.IsEmpty()
				&& BrowserEmbedContractId.IsEmpty()
				&& SpatialSceneContractId.IsEmpty()
				&& SpatialUiContractId.IsEmpty()
				&& SpatialEffectBoundaryId.IsEmpty()
				&& SpatialStateMotionContractId.IsEmpty()
				&& SceneContextId.IsEmpty()
				&& RenderStateProfile.IsEmpty();
		}

		return !VisualizationId.IsEmpty()
			&& !BootstrapId.IsEmpty()
			&& !AdapterProjectionId.IsEmpty()
			&& !ViewerAdapterContractId.IsEmpty()
			&& !BrowserEmbedContractId.IsEmpty()
			&& !SpatialSceneContractId.IsEmpty()
			&& !SpatialUiContractId.IsEmpty()
			&& !SpatialEffectBoundaryId.IsEmpty()
			&& !SpatialStateMotionContractId.IsEmpty()
			&& !SceneContextId.IsEmpty()
			&& !RenderStateProfile.IsEmpty()
			&& ViewerBehaviorTags.Num() > 0
			&& ViewerAnnotationSurfaceTags.Num() > 0
			&& SpatialUiComponentFamilies.Num() > 0
			&& SpatialUiStateSurfaceTags.Num() > 0
			&& SpatialSceneEventSurfaceTags.Num() > 0
			&& SpatialEffectFamilies.Num() > 0
			&& MotionKinds.Num() > 0;
	}
};

// ---------------------------------------------------------------------------
// Runtime surface - minimal compact replay-player bundle exposed through the
// training runtime library without moving shell ownership out of simulation.
// ---------------------------------------------------------------------------

USTRUCT(BlueprintType)
struct UNREALHYPERTWIST_API FHyperTwistViewerRuntimeSurface
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistViewerPlaybackState PlaybackState;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistViewerMoveEntry CurrentMoveEntry;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FHyperTwistDerivedReplaySummary ReplaySummary;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	float ScrubberNormalizedPosition = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|Viewer")
	FString ControlBarReadout;

	bool IsStructurallyValid() const
	{
		return PlaybackState.IsStructurallyValid()
			&& FMath::IsFinite(ScrubberNormalizedPosition)
			&& ScrubberNormalizedPosition >= 0.0f
			&& ScrubberNormalizedPosition <= 1.0f;
	}
};
