/**
 * Pick a certain amount of unique objects from sourceList.
 * If the amount requested is more than total number of objects in sourceList,
 * only everything available in sourceList and nothing more will be picked.
 * @param  {list} sourceList
 * @param  {number} amount
 * @return {object} - {sample: list, remainder: list}
                      sample is the list of selected objects while
                      remainder is the list of those unselected.
 */
export function takeSampleFrom(sourceList, amount) {
  let randElements = [],
      max = sourceList.length,
      swap = false;

  if (amount >= max) {
    return {sample: sourceList, remainder: {}};
  }

  // If picking more than half of the entire set, random to pick the remainder instead
  if (amount > Math.ceil(max/2)){
    amount = max - amount;
    swap = true;
  }

  for (let i = 0; i < amount; ++i) {
    let rand = sourceList[Math.floor((Math.random() * max))];
    // Re-random until suitable value is found.
    while (randElements.includes(rand)) {
      rand = sourceList[Math.floor((Math.random() * max))];
    }
    randElements.push(rand);
  }
  let remainder = sourceList.filter(function (obj) {
    return !randElements.includes(obj);
  });

  if(swap) {
    return {
      sample: remainder,
      remainder: randElements
    };
  }
  else {
    return {
      sample: randElements,
      remainder: remainder
    };
  }
}

/**
 * With a circle radius r, and center at (h,k),
 * Find the coordinate of on the circle with the specified angle degrees
 * @param {number} h
 * @param {number} k
 * @param {number} angle (degree)
 * @param {number} r
 * @return {object} - coordinate {x: number, y: number} of the point
 */
export function pointOnCircle(h, k, angle, r) {
  return {
    x: h + r*Math.cos(toRadians(angle)),
    y: k + r*Math.sin(toRadians(angle))
  };
}

function toRadians(degrees) {
  return degrees * (Math.PI / 180);
}

/**
 * Find the sum of ( difference between high and low dimensional distance )
 * between a node and each of the samples. The high dimensional distances are
 * precalculated and stored in realDistances.
 * @param {object} node        - a single node, must have x and y properties
 * @param {list} samples       - list of other node objects to compare against
 * @param {list} realDistances - list of number distances between the node to
                                 each of the samples. Index must correspond to
                                 that of samples.
 * @return {number} - Sum of distances differences
 */
export function sumDistError(node, samples, realDistances) {
  let total = 0.0;
  for (let i = 0; i < samples.length; i++) {
    let sample = samples[i];
    let lowDDistance = Math.hypot(sample.x - node.x, sample.y - node.y);
    total += Math.abs(lowDDistance - realDistances[i]);
  }
  return total;
}
