import type { SystemPlanet, SystemPointOfInterest, Vec3 } from "./types";

export type PoiOrbitSpec = {
  radius: number;
  y: number;
  initialAngle: number;
  speed: number;
};

export function hashString(value: string) {
  let hash = 0;
  for (let index = 0; index < value.length; index += 1) {
    hash = (hash * 31 + value.charCodeAt(index)) >>> 0;
  }
  return hash;
}

export function getOrbitalSpeed(radius: number, direction = 1) {
  return direction * (0.42 / Math.pow(Math.max(radius, 1), 1.5));
}

export function getSystemPoiOrbitSpec({
  systemId,
  planets,
  pointsOfInterest,
  poiId,
  scale = 1,
  expanded = true,
}: {
  systemId: string;
  planets: SystemPlanet[];
  pointsOfInterest: SystemPointOfInterest[];
  poiId: string;
  scale?: number;
  expanded?: boolean;
}): PoiOrbitSpec {
  const displayPlanets = expanded ? planets : planets.slice(0, 5);
  const displayPois = expanded ? pointsOfInterest : pointsOfInterest.slice(0, 4);
  const displayIndex = displayPois.findIndex((poi) => poi.id === poiId);
  const fullIndex = pointsOfInterest.findIndex((poi) => poi.id === poiId);
  const poiIndex = Math.max(0, displayIndex >= 0 ? displayIndex : fullIndex);
  const maxOrbit = Math.max(4.8, ...displayPlanets.map((planet) => 2 + planet.orbit * 0.42)) * scale;
  const poiRingStart = maxOrbit + (expanded ? 2.2 : 1.45) * scale;
  const poiRingGap = (expanded ? 1.25 : 0.95) * scale;
  const poi = displayPois[displayIndex] ?? pointsOfInterest[fullIndex] ?? pointsOfInterest[poiIndex];
  const seed = hashString(`${systemId}-${poi?.id ?? poiId}`);
  const direction = seed % 7 === 0 ? -1 : 1;
  const baseAngle = ((hashString(`${systemId}-poi-ring`) % 360) / 360) * Math.PI * 2;
  const initialAngle = baseAngle + poiIndex * 2.399963229728653 + ((seed % 41) - 20) * 0.002;
  const radius = poiRingStart + poiIndex * poiRingGap;

  return {
    radius,
    y: 0.35 * scale,
    initialAngle,
    speed: getOrbitalSpeed(radius, direction) * 0.72,
  };
}

export function getSystemPoiPositionAtTime({
  systemId,
  planets,
  pointsOfInterest,
  poiId,
  elapsedTime,
  scale = 1,
  expanded = true,
}: {
  systemId: string;
  planets: SystemPlanet[];
  pointsOfInterest: SystemPointOfInterest[];
  poiId: string;
  elapsedTime: number;
  scale?: number;
  expanded?: boolean;
}): Vec3 {
  const orbit = getSystemPoiOrbitSpec({ systemId, planets, pointsOfInterest, poiId, scale, expanded });
  const angle = orbit.initialAngle + elapsedTime * orbit.speed;

  return [Math.cos(angle) * orbit.radius, orbit.y, -Math.sin(angle) * orbit.radius];
}

export function getSystemPoiPosition(args: {
  systemId: string;
  planets: SystemPlanet[];
  pointsOfInterest: SystemPointOfInterest[];
  poiId: string;
  scale?: number;
  expanded?: boolean;
}): Vec3 {
  return getSystemPoiPositionAtTime({ ...args, elapsedTime: 0 });
}
