namespace DeepDrftPublic.Client.Services;
///
/// Holds the Mix visualizer's eight continuous-control positions for the lifetime of the WASM app
/// instance. Scoped in DI, so it lives across SPA navigations within one listening session — open a
/// second mix and the knobs keep where you left them — but a fresh page load (F5) constructs a new
/// instance, resetting to defaults. That matches the spec's "persist within session, reset on fresh
/// load" without any cookie/localStorage round-trip (lava reframe §7c).
///
/// One state object, eight properties — not eight sibling holders, and (deliberately) NO constructor
/// parameters: this is a plain scoped value holder, so widening it (the Phase 10 split of the single
/// density knob into fluid-amount + fluid-viscosity) adds fields + defaults only and never forces a
/// consumer constructor to grow. Each C#-side default mirrors a TS-side tuning anchor in
/// MixVisualizer.ts; keep the two in sync, as the DefaultVisibleSeconds /
/// DEFAULT_VISIBLE_SECONDS pair does.
///
///
/// is the decoupling seam between the controls bar and the visualizer bridge.
/// The controls component (a sibling of the backdrop in the page tree) only mutates this shared state
/// and raises ; the bridge component (MixWaveformVisualizer) subscribes
/// and pushes the affected uniform/dial to the JS module. This keeps the JS module handle single-owned
/// by the bridge — no handle sharing, no service-locator, no cross-component interop.
///
///
public sealed class MixVisualizerControlState
{
// ── The eight control defaults (Phase 10). Each mirrors a DEFAULT_* anchor in
// MixVisualizer.ts; keep the two in sync, as the default-sync discipline requires.
// Feel-anchors only — Daniel tunes on screen; the ~20% gravity / ~100% heat pair is his stated
// sweet spot (§4c).
///
/// Default scroll-speed dial. Mirrors DEFAULT_SCROLL_SPEED in MixVisualizer.ts. Normalized
/// [0,1] → mapped to the visible time-span via (0 = slow/wide window,
/// 1 = fast/tight window). Opens mid-range.
///
public const double DefaultScrollSpeed = 0.5;
///
/// Default gradient-rotation-speed dial. Mirrors DEFAULT_GRADIENT_ROTATION_SPEED in
/// MixVisualizer.ts. Normalized [0,1] → slow→fast anchor-rotation; drives the live OKLab
/// three-colour gradient. 0.45 opens with a clearly-visible ~7 s colour cycle (Phase 10 §3.2).
///
public const double DefaultGradientRotationSpeed = 0.45;
///
/// Default lava-gravity dial. Mirrors DEFAULT_LAVA_GRAVITY in MixVisualizer.ts. Normalized
/// [0,1]; 0 = near-weightless float, 1 = wax falls + settles fast. Tuned to Daniel's ~20% sweet spot.
///
public const double DefaultLavaGravity = 0.2;
///
/// Default lava-heat dial. Mirrors DEFAULT_LAVA_HEAT in MixVisualizer.ts. Normalized [0,1];
/// 0 = wax rests at the bottom (collision-only), 1 = many small turbulent rising bubbles. Tuned to
/// Daniel's ~100% sweet spot.
///
public const double DefaultLavaHeat = 1.0;
///
/// Default fluid-amount dial. Mirrors DEFAULT_FLUID_AMOUNT in MixVisualizer.ts. The first
/// half of the Phase 10 "bubbles" split. Normalized [0,1]; 0 = few small blobs, 1 = many larger
/// blobs (more wax in the container — blob count + per-blob volume).
///
public const double DefaultFluidAmount = 0.4;
///
/// Default fluid-viscosity / cohesion dial. Mirrors DEFAULT_FLUID_VISCOSITY in
/// MixVisualizer.ts. The second half of the Phase 10 "bubbles" split. Normalized [0,1]; 1 = high
/// cohesion (crisp spheres that snap back), 0 = low cohesion (deforms freely, stays gooey/merged).
///
public const double DefaultFluidViscosity = 0.6;
///
/// Default collision-strength dial. Mirrors DEFAULT_COLLISION_STRENGTH in MixVisualizer.ts.
/// Normalized [0,1]; 0 = soft mush, 1 = hard elastic up-and-out throw.
///
public const double DefaultCollisionStrength = 0.5;
///
/// Default waveform-width dial. Mirrors DEFAULT_WAVEFORM_WIDTH in MixVisualizer.ts.
/// Normalized [0,1], mapped onto the useful 10%–95% ribbon-extent band (Phase 10 §3.7); 0.5 opens
/// mid-band. Narrowing clears room for the lava.
///
public const double DefaultWaveformWidth = 0.5;
/// Apparent bottom-to-top scroll rate, normalized [0,1]. Bridge maps it to a visible
/// time-span via ; the standalone resolution/zoom control is gone.
public double ScrollSpeed { get; set; } = DefaultScrollSpeed;
/// Gradient anchor-rotation rate, normalized [0,1]. Drives the live OKLab gradient.
public double GradientRotationSpeed { get; set; } = DefaultGradientRotationSpeed;
/// Downward force on the wax, normalized [0,1].
public double LavaGravity { get; set; } = DefaultLavaGravity;
/// Energy into the lava system, normalized [0,1]. 0 = rest-at-bottom, 1 = roiling.
public double LavaHeat { get; set; } = DefaultLavaHeat;
/// Amount of wax (blob count + per-blob volume), normalized [0,1]. Phase 10 split, part 1.
public double FluidAmount { get; set; } = DefaultFluidAmount;
/// Fluid viscosity / cohesion, normalized [0,1]. 1 = crisp spheres, 0 = gooey/deformed.
/// Phase 10 split, part 2.
public double FluidViscosity { get; set; } = DefaultFluidViscosity;
/// Collision hardness, normalized [0,1]. 0 = soft mush, 1 = hard up-and-out throw.
public double CollisionStrength { get; set; } = DefaultCollisionStrength;
/// Waveform-band horizontal extent, normalized [0,1]. Narrowing clears room for the lava.
public double WaveformWidth { get; set; } = DefaultWaveformWidth;
///
/// Raised whenever any control value changes. The visualizer bridge subscribes to push the
/// affected dial(s). Mutators set the property then raise this; subscribers re-read the values.
///
public event Action? Changed;
/// Raise . Called by the controls component after mutating a value.
public void NotifyChanged() => Changed?.Invoke();
}