/**
 * JavaScript Exercise 1
 * vim: set ts=2 sw=2 et tw=80:
 */

/**
 * @param {number[]} a - The array of numbers.
 * @param {number} c - The scalar multiplier.
 * @return {number[]} An array computed by multiplying each element of the
 * input array `a` with the input scalar value `c`.
 */
function scalar_product(a, c) {
  if (!Array.isArray(a)) return undefined;
  if (!c && c != 0) return a;
  return a.map(e => e * c);
}

/**
 * @param {number[]} a - The first array of numbers.
 * @param {number[]} b - The second array of numbers.
 * @return {number} A value computed by summing the products of each pair
 * of elements of its input arrays `a`, `b` in the same position.
 */
function inner_product(a, b) {
  if (a.length != b.length) return undefined;
  return a.map((e, i) => e * b[i]).reduce((a, e) => a + e, 0);
}

/**
 * @param {*[]} a - The array.
 * @param {function} mapfn - The function for the map step.
 * @param {function} [reducefn= function(x,y) { return x+y; }] - The
 * function for the reduce step.
 * @param {string} [seed=""] - The accumulator for the reduce step.
 * @return {*} The reduced value after the map and reduce steps.
 */
function mapReduce(a, mapfn, reducefn, seed = "") {
  if (!Array.isArray(a)) return undefined;
  if (typeof mapfn != 'function') return undefined;
  reducefn = reducefn || ((x, y) => x + y);

  for (let i = 0; i < a.length; i++) {
    seed = reducefn(seed, mapfn(a[i]));
  }
  return seed;
}

/**
 * @param {number[]} a - The first sorted array of numbers.
 * @param {number[]} b - The second sorted array of numbers.
 * @return {number[]} A sorted array with all the elements from
 * both `a` and `b`.
 */
function mergeSortedArrays(a, b) {
  const m = new Array(a.length + b.length);
  let ac = a.length - 1, bc = b.length - 1, i = ac + bc + 1;

  while (i >= 0) {
    if (ac < 0 || b[bc] > a[ac]) m[i--] = b[bc--];
    else m[i--] = a[ac--];
  }

  return m;
}

/**
 * @param {integer} x - The first integer.
 * @param {integer} y - The second integer.
 * @param {integer} [step=1] - The value to add at each step.
 * @return {integer[]} An array containing numbers x, x+step, … last, where:
 *    - last equals x + n*step for some n,
 *    - last <= y < last + step if step > 0 and
 *    - last + step < y <= last if step < 0.
 */
function range(x, y, step = 1) {
  if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(step)
    || step == 0) {
    return undefined;
  }

  console.log(x, y, step);
  if ((x > y && step > 0) || (x < y && step < 0)) {
    return [];
  }

  const a = [];

  while (x < y) {
    a.push(x);
    x += step;
  }

  return a;
}

/**
 * @param {*[]} a - The array to flatten.
 * @return {*[]} A flattened array.
 */
function flatten(arr) {
  if (!Array.isArray(arr)) {
    return arr;
  }

  const a = [];
  arr.forEach(e => {
    e = flatten(e); Array.isArray(e) ? a.push(...e) : a.push(e); });
  return a;
}

/**
 * @param {integer} [line_size=72] - The line size.
 * @return {function} A function that takes a string as an argument
 * and returns an array of strings where:
 *   a. each string length is no more than `line_size` and
 *   b. doesn't contain line breaks, tabs, double spaces and initial/trailing
 *   white spaces.
 */
function mkPrettyPrinter(line_size = 72) {
  if (!Number.isFinite(line_size)) {
    return undefined;
  }

  return (s) => {
    if (typeof s !== 'string') {
      return undefined;
    }
    s = s.replace('\n', ' ').trim().replace(/\s+/g, ' ');

    const a = [];
    let i = -1;
    const words = s.split(' ');
    for (const word of words) {
      if (i == -1 || a[i].length + 1 + word.length > line_size) {
        a[++i] = word;
      } else {
        a[i] += ' ' + word;
      }
    }

    return a;
  }
}

/**
 * @param {integer} line_size - The line size.
 * @param {integer} [level=0] level - The indentation level.
 * @return {function} A function twith the following behavior:
 *     - If called with an integer `n`, change the indentation level by
 *       adding `n`to the current indentation level.
 *     - If called with `true`, return the current indentation level.
 *     - If called with a string:
 *         - break it into lines with length (after adding the indentation)
 *           no more than `line_size`,
 *         - add spaces in front of each line according to the current
 *           indentation level and
 *         - store the resulting lines internally.
 *     - [optional] If called with an array of strings, create an
 *       bullet list (using `*`) taking current indentation level into
 *       account. Also, each element should be properly broken into lines and indented.
 *     - If called with no arguments, produce an array with the lines stored so far.
 *       Internal storage must be emptied. Indentation level must not be changed.
 */
function mkIndenter(line_size, level=0) {
  if (!Number.isFinite(line_size) || !Number.isFinite(level)) {
    return undefined;
  }

  let state = [];
  return (param) => {
    if (Number.isInteger(param)) {
      level += param;
    } else if (param === true) {
      return level;
    } else if (typeof param === 'string') {
      console.log(param);

      let words = mkPrettyPrinter(line_size - level)(param);
      words = words.map(e => ' ' * level + e);
      state.push(...words);

      console.log(line_size, level, param, state);
    } else if (Array.isArray(param)) {
      for (const e of param) {
        if (typeof e !== 'string') {
          return;
        }
        const words = mkPrettyPrinter(line_size - level - 2)(param);
        state.push('* ' + words[0]);

        for (let i = 1; i < words.length; i++) {
          state.push('  ' + words[i]);
        }
      }
    } else if (param === void(0)) {
      const a = state;
      state = [];
      return a;
    }
  }
}

/**
 * JavaScript Exercise 2
 */

/**
 * Calculates the number of occurrences of letters in a given string
 * @param {string} a - The input string.
 * @return {number[]} An array indexed by the letter characters found in the string.
 */
function letter_frequency(s) {
}

/**
 * Displays the output of the letter frequency analysis in an HTML table
 * generated within the `dom` element passed as parameter
 * @param {number[]} a - The array indexed by the letter characters as returned
 * from `letter_frequency()`.
 * @param {Object} dom - The DOM element that will contain the resulting table.
 * @return {undefined}
 */
function display_letter_frequency(a, dom) {
}

/**
 * Links the provided input text field in the test page with the table.
 * @param {Object} inputEl - The DOM object of the input element in test.html.
 * @return {undefined}
 */
function online_frequency_analysis(inputEl) {
}

/**
 * JavaScript Exercise 3
 */
function clock(){

}
