// 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 "HyperTwistSimulation/HyperTwistViewerLibrary.h"

namespace HyperTwistViewerLibraryInternal
{
	FString FormatTimelineSeconds(const int32 TimeMs)
	{
		return FString::Printf(TEXT("%.2fs"), static_cast<double>(FMath::Max(0, TimeMs)) / 1000.0);
	}
}

// ---------------------------------------------------------------------------
// Control request constructors
// ---------------------------------------------------------------------------

FHyperTwistViewerControlRequest UHyperTwistViewerLibrary::MakePlayRequest()
{
	FHyperTwistViewerControlRequest R;
	R.Action = EHyperTwistViewerControlAction::Play;
	return R;
}

FHyperTwistViewerControlRequest UHyperTwistViewerLibrary::MakePauseRequest()
{
	FHyperTwistViewerControlRequest R;
	R.Action = EHyperTwistViewerControlAction::Pause;
	return R;
}

FHyperTwistViewerControlRequest UHyperTwistViewerLibrary::MakeStopRequest()
{
	FHyperTwistViewerControlRequest R;
	R.Action = EHyperTwistViewerControlAction::Stop;
	return R;
}

FHyperTwistViewerControlRequest UHyperTwistViewerLibrary::MakeStepForwardRequest()
{
	FHyperTwistViewerControlRequest R;
	R.Action = EHyperTwistViewerControlAction::StepForward;
	return R;
}

FHyperTwistViewerControlRequest UHyperTwistViewerLibrary::MakeStepBackRequest()
{
	FHyperTwistViewerControlRequest R;
	R.Action = EHyperTwistViewerControlAction::StepBack;
	return R;
}

FHyperTwistViewerControlRequest UHyperTwistViewerLibrary::MakeSeekRequest(int32 TargetIndex)
{
	FHyperTwistViewerControlRequest R;
	R.Action          = EHyperTwistViewerControlAction::SeekToIndex;
	R.SeekTargetIndex = FMath::Max(0, TargetIndex);
	return R;
}

// ---------------------------------------------------------------------------
// Move entry construction
// ---------------------------------------------------------------------------

FHyperTwistViewerMoveEntry UHyperTwistViewerLibrary::MakeMoveEntry(
	const FHyperTwistTransformation& Transform,
	const FString& DisplayLabel,
	float HintPositionSeconds
)
{
	FHyperTwistViewerMoveEntry Entry;
	Entry.Transform            = Transform;
	Entry.DisplayLabel         = DisplayLabel;
	Entry.HintPositionSeconds  = FMath::Max(0.0f, HintPositionSeconds);
	return Entry;
}

// ---------------------------------------------------------------------------
// Scrubber accessors
// ---------------------------------------------------------------------------

float UHyperTwistViewerLibrary::GetScrubberNormalizedPosition(const FHyperTwistViewerPlaybackState& State)
{
	if (State.TotalTimeMs > 0)
	{
		if (State.bAtEnd)
		{
			return 1.0f;
		}

		return FMath::Clamp(
			static_cast<float>(FMath::Clamp(State.CurrentTimeMs, 0, State.TotalTimeMs))
				/ static_cast<float>(State.TotalTimeMs),
			0.0f,
			1.0f
		);
	}

	if (State.TotalMoveCount <= 1 || State.CurrentMoveIndex == INDEX_NONE)
	{
		return 0.0f;
	}

	const float MaxIndex = static_cast<float>(State.TotalMoveCount - 1);
	return FMath::Clamp(static_cast<float>(State.CurrentMoveIndex) / MaxIndex, 0.0f, 1.0f);
}

// ---------------------------------------------------------------------------
// Label formatters
// ---------------------------------------------------------------------------

FString UHyperTwistViewerLibrary::GetMoveNotationLabel(const FHyperTwistViewerMoveEntry& Entry)
{
	return Entry.DisplayLabel.IsEmpty() ? TEXT("-") : Entry.DisplayLabel;
}

FString UHyperTwistViewerLibrary::GetPlaybackModeLabel(EHyperTwistViewerPlaybackMode Mode)
{
	switch (Mode)
	{
	case EHyperTwistViewerPlaybackMode::Playing:  return TEXT("Playing");
	case EHyperTwistViewerPlaybackMode::Paused:   return TEXT("Paused");
	case EHyperTwistViewerPlaybackMode::Stepping: return TEXT("Stepping");
	default:                                       return TEXT("Stopped");
	}
}

FString UHyperTwistViewerLibrary::GetStepCounterLabel(const FHyperTwistViewerPlaybackState& State)
{
	if (State.TotalMoveCount == 0 || State.CurrentMoveIndex == INDEX_NONE)
	{
		return TEXT("-- / --");
	}
	return FString::Printf(TEXT("%d / %d"), State.CurrentMoveIndex + 1, State.TotalMoveCount);
}

FString UHyperTwistViewerLibrary::GetTimelineLabel(const FHyperTwistViewerPlaybackState& State)
{
	if (!State.bReplayLoaded && State.TotalTimeMs <= 0)
	{
		return TEXT("-- / --");
	}

	return FString::Printf(
		TEXT("%s / %s"),
		*HyperTwistViewerLibraryInternal::FormatTimelineSeconds(State.CurrentTimeMs),
		*HyperTwistViewerLibraryInternal::FormatTimelineSeconds(State.TotalTimeMs)
	);
}

FString UHyperTwistViewerLibrary::GetControlBarReadout(
	const FHyperTwistViewerPlaybackState& State,
	const FHyperTwistViewerMoveEntry& CurrentEntry
)
{
	const FString ModeLabel    = GetPlaybackModeLabel(State.Mode);
	const FString StepLabel    = GetStepCounterLabel(State);
	const FString TimelineLabel = GetTimelineLabel(State);
	const FString NotationLabel = GetMoveNotationLabel(CurrentEntry);

	return FString::Printf(TEXT("%s - %s - %s - %s"), *ModeLabel, *StepLabel, *TimelineLabel, *NotationLabel);
}

// ---------------------------------------------------------------------------
// Boundary queries
// ---------------------------------------------------------------------------

bool UHyperTwistViewerLibrary::IsPlaybackAtStart(const FHyperTwistViewerPlaybackState& State)
{
	return State.bAtStart;
}

bool UHyperTwistViewerLibrary::IsPlaybackAtEnd(const FHyperTwistViewerPlaybackState& State)
{
	return State.bAtEnd;
}

bool UHyperTwistViewerLibrary::IsPlaybackActive(const FHyperTwistViewerPlaybackState& State)
{
	return State.Mode == EHyperTwistViewerPlaybackMode::Playing;
}
