import { canvas2d, h } from "./lib/html";
import { Cancel } from "./lib/source";
import { tick } from "./lib/tick";

type Mass = {
  x: number;
  y: number;
  vx: number;
  vy: number;
  radius: number;
  mass: number;
  color: string;
};

export function OrbitDemo() {
  const [canvas, cx] = canvas2d({
    width: 512,
    height: 512,
  });
  cx.scale(1 / 2, 1 / 2);
  cx.translate(512, 512);

  const G = 8000;
  const PHYSICS_TICKS = 30;
  const masses: Mass[] = [];

  const tickSource = tick(PHYSICS_TICKS);
  let cancel: Cancel;

  const begin = () => {
    // reset / init
    cancel?.();
    masses.length = 0;
    masses.push(
      { x: 0, y: 0, vx: -1, vy: -2, radius: 10, mass: 1000, color: "yellow" },
      { x: -150, y: 150, vx: 130, vy: 130, radius: 9, mass: 10, color: "red" },
      { x: -450, y: 0, vx: 0, vy: 120, radius: 4, mass: 5, color: "#0f8" }
    );
    // subscribe to source, stash cancellation func
    cancel = tickSource((tick) => {
      switch (tick[0]) {
        case "physics":
          // apply velocities
          masses.forEach((mass) => {
            mass.x += mass.vx * (1 / PHYSICS_TICKS);
            mass.y += mass.vy * (1 / PHYSICS_TICKS);
          });
          // apply accelerations
          masses.forEach((mass) => {
            masses.forEach((other) => {
              if (mass != other) {
                const dx = other.x - mass.x;
                const dy = other.y - mass.y;

                const rSquared = dx ** 2 + dy ** 2;
                const f = (G * other.mass) / rSquared;
                const d = Math.sqrt(rSquared);
                const fx = (f * dx) / d;
                const fy = (f * dy) / d;

                mass.vx += fx / PHYSICS_TICKS;
                mass.vy += fy / PHYSICS_TICKS;
              }
            });
          });
          break;
        case "render":
          const [, dt] = tick;
          cx.fillStyle = "black";
          cx.fillRect(-512, -512, 1024, 1024);
          masses.forEach(({ x, y, vx, vy, radius, color }) => {
            cx.fillStyle = color;
            cx.beginPath();
            cx.arc(x + vx * dt, y + vy * dt, radius, 0, Math.PI * 2);
            cx.fill();
          });
          break;
      }
    });
  };

  const end = () => cancel?.();

  const start = h("button", { onclick: begin }, "Begin / Reset");
  const stop = h("button", { onclick: end }, "Cease");

  return [h("h1", {}, "Orbit Simulation"), h("p", {}, start, stop), canvas];
}

Object.assign(globalThis, { OrbitDemo });