import type { DataXY } from 'cheminfo-types';

import { xyArrayWeightedMerge } from '../xyArrayWeightedMerge.ts';

export interface GetSlotsToFirstOptions {
  /**
   * The range in which the two x values of the data/spectra must be to be placed on the same line. It may also be a function that allows to change `delta` depending on the X values of the spectrum
   * @default 1
   */
  delta?: ((arg: number) => number) | number;
}

export interface Slot {
  from: number;
  to: number;
  value: number;
}

/**
 * Computes slots aligned to the first spectrum's x values, adding slots for any x values not covered.
 * @param data - data.
 * @param options - options.
 */
export function getSlotsToFirst(
  data: DataXY[],
  options: GetSlotsToFirstOptions = {},
): Slot[] {
  const { delta = 1 } = options;
  const deltaIsFunction = typeof delta === 'function';

  const firstXs = data[0].x;
  const slots: Slot[] = [];
  // we first create the slots based on the first spectrum
  for (const element of firstXs) {
    const currentDelta = deltaIsFunction ? delta(element) : delta;
    slots.push({
      from: element - currentDelta,
      to: element + currentDelta,
      value: element,
    });
  }

  const otherXs = xyArrayWeightedMerge(data.slice(1), options).x;
  let currentPosition = 0;
  for (const slot of slots) {
    while (
      otherXs[currentPosition] < slot.to &&
      currentPosition < otherXs.length
    ) {
      if (otherXs[currentPosition] < slot.from) {
        const currentDelta = deltaIsFunction
          ? delta(otherXs[currentPosition])
          : delta;
        slots.push({
          from: otherXs[currentPosition] - currentDelta,
          to: otherXs[currentPosition] + currentDelta,
          value: otherXs[currentPosition],
        });
      }
      currentPosition++;
    }
  }
  for (let i = currentPosition; i < otherXs.length; i++) {
    const currentDelta = deltaIsFunction ? delta(otherXs[i]) : delta;
    slots.push({
      from: otherXs[i] - currentDelta,
      to: otherXs[i] + currentDelta,
      value: otherXs[i],
    });
  }

  slots.sort((a, b) => a.value - b.value);

  // we prevent slots overlap in the first spectrum
  for (let i = 0; i < slots.length - 1; i++) {
    if (slots[i].to > slots[i + 1].from) {
      const middle = (slots[i].value + slots[i + 1].value) / 2;
      slots[i].to = middle;
      slots[i + 1].from = middle;
    }
  }
  return slots;
}
