/**
 * Which of the two points are closer to the target?
 * Returns index of the closer point
 *
 * IMPORTANT! Keep this above findClosestPoint, for worklet/hoisting reasons
 */
function getClosest(
  [idx1, val1]: [number, number],
  [idx2, val2]: [number, number],
  targetX: number,
) {
  "worklet";
  return targetX - val1 >= val2 - targetX ? idx2 : idx1;
}

/**
 * Takes in array of numbers and a targetX and returns the _index_ of closest number
 */
export function findClosestPoint(
  xValues: number[],
  targetX: number,
): number | null {
  "worklet";

  const n = xValues.length;
  if (!n) return null;

  const firstX = xValues[0],
    lastX = xValues[n - 1];
  if (firstX !== undefined && targetX <= firstX) return 0;
  if (lastX !== undefined && targetX >= lastX) return n - 1;

  // binary search
  let i = 0,
    j = n,
    mid = 0;
  while (i < j) {
    mid = Math.floor((i + j) / 2);

    if (xValues[mid] === targetX) return mid;

    // search left
    if (targetX < xValues[mid]!) {
      if (mid > 0 && targetX > xValues[mid - 1]!)
        return getClosest(
          [mid - 1, xValues[mid - 1]!],
          [mid, xValues[mid]!],
          targetX,
        );

      // Repeat for left half
      j = mid;
    }

    // search right
    else {
      if (mid < n - 1 && targetX < xValues[mid + 1]!)
        return getClosest(
          [mid, xValues[mid]!],
          [mid + 1, xValues[mid + 1]!],
          targetX,
        );
      i = mid + 1;
    }
  }

  return mid;
}
