import { binarySearchUpperBound } from './binarySearch';
import { AutomationPoint } from './derivedTiming';

export default function getAutomationValue(
  automationPoints: AutomationPoint[],
  beat: number,
  fallback: number
): number {
  if (automationPoints.length === 0) {
    return fallback;
  }

  if (automationPoints[0]!.beats >= beat) {
    return automationPoints[0]!.value;
  }

  const nextPointIndex = binarySearchUpperBound(automationPoints, beat);
  const prevPointIndex = nextPointIndex - 1;

  if (nextPointIndex >= automationPoints.length) {
    return automationPoints[automationPoints.length - 1].value;
  }

  const prevPoint = automationPoints[prevPointIndex];
  const nextPoint = automationPoints[nextPointIndex];
  const beatsDelta = nextPoint.beats - prevPoint.beats;
  if (beatsDelta === 0) {
    return nextPoint.value;
  }
  if (prevPoint.value === nextPoint.value) {
    return prevPoint.value;
  }
  const valueDelta = nextPoint.value - prevPoint.value;
  const beatsDeltaFromLastPoint = beat - prevPoint.beats;
  let valueDeltaFromLastPoint = 0;
  if (prevPoint.curve === 1) {
    valueDeltaFromLastPoint =
      valueDelta * (beatsDeltaFromLastPoint / beatsDelta);
  } else if (prevPoint.curve !== 0) {
    valueDeltaFromLastPoint =
      valueDelta *
      (1 - Math.pow(1 - beatsDeltaFromLastPoint / beatsDelta, prevPoint.curve));
  }
  return prevPoint.value + valueDeltaFromLastPoint;
}
