/**
 * It takes an array of items, an array of values, and a capacity, and returns an array of arrays of
 * items, where each array of items has a total value less than or equal to the capacity
 */
export class ValuePaginator {
  /**
   * It takes an array of items, a function that returns a number for each item, and a number or a
   * function that returns a number, and returns an array of arrays of items
   *
   * @typedef T generic type  parameter
   * @param {T[]} items - T[] - the items to paginate
   * @param {(input: T, index: number) => number} valueGetter - (input: T, index: number) => number
   * @param {number | ((startIndex: number, values: number[]) => number)} currentPageCapacity - number |
   * ((startIndex: number, values: number[]) => number)
   * @returns {T[][]} An array of arrays of items.
   */
  paginate(items, valueGetter, currentPageCapacity) {
    const values = items.map(valueGetter);
    return this.doPaginateByCapacity(items, values, currentPageCapacity);
  }
  /**
   * It takes an array of items, an array of values, and a capacity, and returns an array of arrays of
   * items, where each array of items is a page, and the number of items in each page is determined by
   * the capacity
   *
   * @typedef T generic type  parameter
   * @param {T[]} items - T[]
   * @param {number[]} values - number[]
   * @param {number | ((startIndex: number, values: number[]) => number)} currentPageCapacity - number |
   * ((startIndex: number, values: number[]) => number)
   * @returns {T[][]} An array of arrays of items.
   */
  doPaginateByCapacity(items, values, currentPageCapacity) {
    const pages = [];
    let startIndex = 0;
    while (startIndex < values.length) {
      const pageElementNumber = this.valueSum(startIndex, values, currentPageCapacity);
      if (pageElementNumber === 0) {
        break;
      }
      pages.push(items.slice(startIndex, startIndex + pageElementNumber));
      startIndex = startIndex + pageElementNumber;
    }
    return pages;
  }
  /**
   * "Given a start index, an array of values, and a capacity, return the number of elements that can fit
   * in the capacity."
   *
   * The function is a bit more complicated than that, but that's the gist of it
   *
   * @param {number} startIndex - The index of the first element in the page.
   * @param {number[]} values - number[] - an array of numbers that represent the height of each element
   * in the list.
   * @param {number | ((startIndex: number, values: number[]) => number)} currentPageCapacity - number |
   * ((startIndex: number, values: number[]) => number)
   * @returns {number} The number of elements that can fit on a page.
   */
  valueSum(startIndex, values, currentPageCapacity) {
    let totalValues = 0;
    let flag = false;
    let pageElementNumber = 0;
    const dynamicCapacity = typeof currentPageCapacity === 'function'
      ? currentPageCapacity(startIndex, values)
      : currentPageCapacity;
    values.slice(startIndex).forEach((value, index) => {
      totalValues += value;
      if (totalValues > dynamicCapacity && !flag) {
        flag = true;
        pageElementNumber = index;
      }
    });
    if (!flag) {
      pageElementNumber = values.slice(startIndex).length;
    }
    return pageElementNumber;
  }
}
