/*!
 * helper-js v3.1.6
 * Author: phphe <phphe@outlook.com> (https://github.com/phphe)
 * Homepage: null
 * Released under the MIT License.
 */
// ## base functions
// ## 基础方法
/**
 * get global, such as window in browser.
 * 返回顶级全局变量. 例如浏览器的`window`
 * @returns
 */
/**
 * If n less than `min`, return `min`, else n.
 * 如果n小于min, 返回min, 否则n.
 * @param n
 * @param min
 * @returns
 */
function notLessThan(n, min) {
  return n < min ? min : n;
}
/**
 * If n greater than `max`, return `max`, else n.
 * 如果n大于max, 返回max, 否则n.
 * @param n
 * @param max
 * @returns
 */
function notGreaterThan(n, max) {
  return n < max ? n : max;
}
/**
 * limit min and max of value.
 * 限制值的最小和最大值.
 * @param n
 * @param min
 * @param max
 * @returns
 */
function between(n, min, max) {
  return notGreaterThan(notLessThan(n, min), max);
}
/**
 * get first or array
 * 返回数组首项
 * @param arr
 * @returns
 */
function arrayFirst(arr) {
  return arr[0];
}
/**
 * get last of array
 * 返回数组末项
 * @param arr
 * @returns
 */
function arrayLast(arr) {
  return arr[arr.length - 1];
}
function findParent(el, callback) {
  let opt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  let cur = opt && opt.withSelf ? el : el.parentElement;
  while (cur) {
    const shouldBreak = opt.until && cur === opt.until;
    if (shouldBreak && !opt.withUntil) {
      return;
    }
    const r = callback(cur);
    if (r === "break") {
      return;
    } else if (r) {
      return cur;
    } else if (shouldBreak) {
      return;
    } else {
      cur = cur.parentElement;
    }
  }
}
/**
 * binarySearch, 二分查找
 * @param arr
 * @param callback return `mid - your_value` for ascending array
 * @param opt
 * @returns
 */
function binarySearch(arr, callback) {
  let opt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  opt = {
    start: 0,
    end: arr.length - 1,
    maxTimes: 1000,
    ...opt
  };
  let {
    start,
    end
  } = opt;
  const {
    returnNearestIfNoHit,
    maxTimes
  } = opt;
  let midNum;
  let mid;
  if (start == null) {
    start = 0;
    end = arr.length - 1;
  }
  let i = 0;
  let r;
  while (start >= 0 && start <= end) {
    if (i >= maxTimes) {
      throw Error(`binarySearch: loop times is over ${maxTimes}, you can increase the limit.`);
    }
    midNum = Math.floor((end - start) / 2 + start);
    mid = arr[midNum];
    const count = i + 1;
    r = callback(mid, midNum, count);
    if (r > 0) {
      end = midNum - 1;
    } else if (r < 0) {
      start = midNum + 1;
    } else {
      return {
        index: midNum,
        value: mid,
        count,
        hit: true
      };
    }
    i++;
  }
  return returnNearestIfNoHit ? {
    index: midNum,
    value: mid,
    count: i + 1,
    hit: false,
    greater: r > 0
  } : null;
}

export { arrayFirst, arrayLast, between, binarySearch, findParent, notGreaterThan, notLessThan };
