import { createSignal } from "solid-js";

export function random(a: number, b: number) {
  const intervalSize = b - a;
  return a + Math.random()*intervalSize;
}

export function projectToInterval(a: number, x: number, b: number) {
  return Math.min(Math.max(a, x), b)
}

export function mapRange(value: number, inMin: number, inMax: number, outMin: number, outMax: number) {
  const t = (value - inMin) / (inMax - inMin);
  const clampedT = projectToInterval(0, t, 1);
  return outMin + clampedT * (outMax - outMin);
}

// Snaps microscopic floating point errors to 0, and rounds to 2 decimals
export function formatNumber(x: number): string {
  if (Math.abs(x) < 1e-10) return "0.00";
  return x.toFixed(2);
}

// === Time ===

export type MicroDuration = number;
export type Duration = number;


// === Events ===
export function usePointer(ref: () => HTMLCanvasElement) {
  const [position, setPosition] = createSignal([0, 0]);
  const [isDown, setIsDown] = createSignal(false);

  const handle = (e: PointerEvent) => {
    const rect = ref().getBoundingClientRect();
    setPosition([e.clientX - rect.left, e.clientY - rect.top]);
    setIsDown(e.buttons === 1);
  };

  return { position, isDown, handle };
}


//  === Generic System ===

// Observables contain derived attributes that can be computed purely from the `State`.
export interface System<State, Observables, Capabilities> {
  state: State,
  observables: Observables,
  flow(t: Duration): void, // Updates just the state, not observables!
  refreshObservables(): void, // Updates just the observables from the current state.
  totalTime(): Duration, // gets the time from the start of the simulation
  reset(): void,
  capabilities: Capabilities,
}

