import StringUtils from "./StringUtils";

/**
 * 说明：通用工具类
 */

/* eslint-disable no-undefined */
/**
 * 空的结构
 */
export type TypeEmpty = Record<string, never>;

/**
  * 可保存数据
  */
export type TypeSaveable = TypeEmpty|string|number|null|undefined|bigint|boolean;
/**
  * 可保存数据
  */
// eslint-disable-next-line @typescript-eslint/ban-types
export type TypeAll = TypeEmpty|unknown|object|undefined|string|bigint|number|boolean;

/**
* 任意JSON数据
*/
export interface KeyValue {
   [index: string]: TypeSaveable;
}

/**
 * 数字补0，如果数字转为字符串后不足 `n` 位，则在它前面加 `0`
 * @param num 数字
 * @param n 指定字符串位数
 * @returns 字符串
 */
function pad(num: number|string, n: number) : string {
  let str = num.toString();
  let len = str.length;
  while (len < n) {
    str = "0" + num;
    len++;
  }
  return str;
}

/**
 * 数字保留n位小数
 * @param num 数字
 * @param n 保留小数位数 
 */
function fixedNumber(num: number, n: number) : number {
  return n > 0 ? parseFloat(num.toFixed(n)) : Math.round(num);
}

/**
 * 检查是否定义
 * @param obj 
 */
function isDefined(obj: TypeAll) : boolean {
  return typeof obj !== 'undefined';
}
/**
 * 字符串判空
 * @param str 字符串
 */
function isNullOrEmpty(str: string|null|undefined|unknown) : boolean {
  return StringUtils.isNullOrEmpty(str as string);
}
/**
 * 判断是否定义并且不为 `null`
 * @param v 要判断的数值
 */
function isDefinedAndNotNull(v: TypeAll) : boolean {
  return v != null && typeof v != 'undefined';
}
/**
 * 生成随机字符串
 * @param len 随机字符串长度
 * @returns 随机字符串
 */
function randomString(len?: number) : string {
  len = len || 32;
  const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  const maxPos = $chars.length;
  let pwd = '';
  for (let i = 0; i < len; i++) {
    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
  }
  return pwd;
}

/**
 * 生成随机字符串
 * @param len 随机字符串长度
 * @returns 随机字符串
 */
function randomNumberString(len?: number) : string {
  len = len || 32;
  const $chars = '0123456789';
  const maxPos = $chars.length;
  let pwd = '';
  for (let i = 0; i < len; i++) {
    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
  }
  return pwd;
}

/**
 * 生成指定范围之内的随机数
 * @param minNum 最小值
 * @param maxNum 最大值
 */
function genRandom(minNum: number, maxNum: number) : number {
  return Math.floor(Math.random()*(maxNum-minNum+1)+minNum); 
}
/**
 * 生成不重复随机字符串
 * @param randomLength 字符长度
 */
function genNonDuplicateID(randomLength: number) : string {
  let idStr = Date.now().toString(36)
  idStr += Math.random().toString(36).substr(3,randomLength)
  return idStr
}
/**
 * 生成不重复随机字符串
 * @param randomLength 字符长度
 */
function genNonDuplicateIDHEX(randomLength: number) : string {
  const idStr = genNonDuplicateID(randomLength);
  return StringUtils.strToHexCharCode(idStr, false).substr(idStr.length - randomLength, randomLength);
}

function getDeviceUid() : string {
  let uid = localStorage.getItem('DeviceUid');
  if(!uid || uid === '') {
    uid = genNonDuplicateID(16);
    localStorage.setItem('DeviceUid', uid);
  }
  return uid;
}

/**
 * 深克隆对象，数组
 * @param obj 要克隆的对象
 * @param deepArray 是否要深度克隆数组里的每个对象
 */
function clone(obj: TypeAll, deepArray = false): KeyValue|Array<KeyValue>|null {
  let temp: KeyValue|Array<KeyValue>|null = null;
  if (obj instanceof Array) {
    if(deepArray) temp = (obj as KeyValue[]).map<KeyValue>((item) => clone(item, deepArray) as KeyValue)
    else temp = obj.concat();
  }
  else if (typeof obj === 'object') {
    temp = new Object() as KeyValue;
    for (const item in obj) {
      const val = (obj as KeyValue)[item];
      if(val === null) temp[item] = null;
      else (temp as KeyValue)[item] = clone(val, deepArray) as TypeSaveable;
    }
  }else 
    temp = obj as KeyValue;
  return temp;
}

/* eslint-disable */

/**
 * 深克隆对象
 * @param obj 要克隆的对象（不能是数组，数组请使用 `clone` 方法）
 * @param deepArray 是否要深度克隆数组属性里的每个对象
 */
function cloneObject<T>(obj: T, deepArray = false): T {
  let temp = {};
  for (const item in obj) {
    const val = obj[item];
    if(val === null) (temp as KeyValue)[item] = null;
    else if (typeof val === 'object') (temp as KeyValue)[item] = clone(val, deepArray) as TypeSaveable;
    else (temp as KeyValue)[item] = val as any;
  }
  return temp as T;
}

/**
 * 合并两个对象（仅合并一级属性）
 * @param obj1 
 * @param obj2 
 */
function mergeObject(obj1 : Record<string, unknown>, obj2 : Record<string, unknown>) : Record<string, unknown> { 
  for(const k in obj2)
    obj1[k] = obj2[k];
  return obj1;
}
/**
 * 合并多个对象（仅合并一级属性）
 * @param objs
 */
function mergeObjects(...objs : Record<string, unknown>[]) : Record<string, unknown> { 
  if(objs.length < 1) 
    return {};
  if(objs.length < 2) 
    return objs[0];
  const o = objs[0];
  for(let i = objs.length - 1; i > 0; i--)
    mergeObject(o, objs[i]);
  return o;
}

/* eslint-enable */

export default {
  pad,
  isDefined,
  isNullOrEmpty,
  isDefinedAndNotNull,
  /**
   * 如果字符串为空，则返回undefined，否则返回字符串
   * @param val 
   */
  emptyToUndefined(val: string) : undefined|string {
    // eslint-disable-next-line no-undefined
    return isNullOrEmpty(val) ? undefined : val;
  },
  /**
   * 如果数字为null或小于等于0，则返回undefined，否则返回数字
   * @param val 
   */
  zeroOrNullToUndefined(val?: number|null) : undefined|number {
    // eslint-disable-next-line no-undefined
    return (!val || val == 0) ? undefined : val;
  },
  clone,
  /**
   * 将源对象每个属性都复制到目标对象（不管目标对象有没有对应属性）
   * @param setObj 目标对象
   * @param sourceObj 源对象
   */
  cloneValue(setObj: { [index: string]: TypeAll}, sourceObj: { [index:  string]: TypeAll}) : void {
    if(!setObj || !sourceObj) return;
    Object.keys(setObj).forEach(function(key){
      if(typeof sourceObj[key] != 'undefined') {
        setObj[key] = sourceObj[key];
      }
    });
  },
  cloneObject,
  /**
   * 检查数组中是否全部是空字符串或null
   * @param arr 要检查的数组
   * @returns 
   */
  isArrayAllNullOrEmpty(arr : Array<unknown>) : boolean {
    if(!arr)
      return true;
    for (let i = arr.length - 1; i >= 0; i--) {
      if(arr[i] !== null && arr[i] !== "") {
        return false;
      }
    }
    return true;
  },
  /**
   * 检查数组中是否有空值或字符串
   * @param arr 要检查的数组
   * @returns 
   */
  isArrayContainsNullOrEmpty(arr : Array<unknown>) : boolean {
    if(!arr)
      return false;
    for (let i = arr.length - 1; i >= 0; i--) {
      if(isNullOrEmpty(arr[i])) {
        return true;
      }
    }
    return false;
  },
  randomString,
  randomNumberString,
  getDeviceUid,
  genRandom,
  genNonDuplicateID,
  genNonDuplicateIDHEX,
  mergeObject,
  mergeObjects,
  fixedNumber,
  /**
   * 等待延时
   */
  waitTimeOut(timeOut: number) : Promise<void> {
    return new Promise<void>((resolve) => {
      setTimeout(() => resolve(), timeOut);
    });
  },
}
