/**
 * 对象类
 */
export class ObjectManager {
  constructor() {}

  /**
   * 对象转url参数
   * @param {*} data,对象
   * @param {*} isPrefix,是否自动加上"?"
   * 参考：uview
   **/
  queryParams = (data = {}, isPrefix = true, arrayFormat = 'brackets') => {
    let prefix = isPrefix ? '?' : ''
    let _result = []
    if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) arrayFormat = 'brackets';
    for (let key in data) {
      let value = data[key]
      // 去掉为空的参数
      if (['', undefined, null].indexOf(value) >= 0) {
        continue;
      }
      // 如果值为数组，另行处理
      if (value.constructor === Array) {
        // e.g. {ids: [1, 2, 3]}
        switch (arrayFormat) {
          case 'indices':
            // 结果: ids[0]=1&ids[1]=2&ids[2]=3
            for (let i = 0; i < value.length; i++) {
              _result.push(key + '[' + i + ']=' + value[i])
            }
            break;
          case 'brackets':
            // 结果: ids[]=1&ids[]=2&ids[]=3
            value.forEach(_value => {
              _result.push(key + '[]=' + _value)
            })
            break;
          case 'repeat':
            // 结果: ids=1&ids=2&ids=3
            value.forEach(_value => {
              _result.push(key + '=' + _value)
            })
            break;
          case 'comma':
            // 结果: ids=1,2,3
            let commaStr = "";
            value.forEach(_value => {
              commaStr += (commaStr ? "," : "") + _value;
            })
            _result.push(key + '=' + commaStr)
            break;
          default:
            value.forEach(_value => {
              _result.push(key + '[]=' + _value)
            })
        }
      } else {
        _result.push(key + '=' + value)
      }
    }
    return _result.length ? prefix + _result.join('&') : ''
  }

  /**
   * @description 克隆对象
   * @param {object} obj 需要深度克隆的对象
   * @returns {*} 克隆后的对象或者原值（不是对象）
   */
  clone = (obj, cache = new WeakMap()) => {
    if (obj === null || typeof obj !== 'object') return obj;
    if (cache.has(obj)) return cache.get(obj);
    let clone;
    if (obj instanceof Date) {
      clone = new Date(obj.getTime());
    } else if (obj instanceof RegExp) {
      clone = new RegExp(obj);
    } else if (obj instanceof Map) {
      clone = new Map(Array.from(obj, ([key, value]) => [key, this.clone(value, cache)]));
    } else if (obj instanceof Set) {
      clone = new Set(Array.from(obj, value => this.clone(value, cache)));
    } else if (Array.isArray(obj)) {
      clone = obj.map(value => this.clone(value, cache));
    } else if (Object.prototype.toString.call(obj) === '[object Object]') {
      clone = Object.create(Object.getPrototypeOf(obj));
      cache.set(obj, clone);
      for (const [key, value] of Object.entries(obj)) {
        clone[key] = this.clone(value, cache);
      }
    } else {
      clone = Object.assign({}, obj);
    }
    cache.set(obj, clone);
    return clone;
  }

  /**
   * @description JS对象深度合并
   * @param {object} target 需要拷贝的对象
   * @param {object} source 拷贝的来源对象
   * @returns {object|boolean} 深度合并后的对象或者false（入参有不是对象）
   */
  merge = (target = {}, source = {}) => {
    target = this.clone(target)
    if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) return target;
    const merged = Array.isArray(target) ? target.slice() : Object.assign({}, target);
    for (const prop in source) {
      if (!source.hasOwnProperty(prop)) continue;
      const sourceValue = source[prop];
      const targetValue = merged[prop];
      if (sourceValue instanceof Date) {
        merged[prop] = new Date(sourceValue);
      } else if (sourceValue instanceof RegExp) {
        merged[prop] = new RegExp(sourceValue);
      } else if (sourceValue instanceof Map) {
        merged[prop] = new Map(sourceValue);
      } else if (sourceValue instanceof Set) {
        merged[prop] = new Set(sourceValue);
      } else if (typeof sourceValue === 'object' && sourceValue !== null) {
        merged[prop] = deepMerge(targetValue, sourceValue);
      } else {
        merged[prop] = sourceValue;
      }
    }
    return merged;
  }

  /**
   * @description 获取某个对象下的属性，用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式
   * @param {object} obj 对象
   * @param {string} key 需要获取的属性字段
   * @returns {*}
   */
  getProperty = (obj, key) => {
    if (!obj) {
      return
    }
    if (typeof key !== 'string' || key === '') {
      return ''
    }
    if (key.indexOf('.') !== -1) {
      const keys = key.split('.')
      let firstObj = obj[keys[0]] || {}

      for (let i = 1; i < keys.length; i++) {
        if (firstObj) {
          firstObj = firstObj[keys[i]]
        }
      }
      return firstObj
    }
    return obj[key]
  }

  /**
   * @description 设置对象的属性值，如果'a.b.c'的形式进行设置
   * @param {object} obj 对象
   * @param {string} key 需要设置的属性
   * @param {string} value 设置的值
   */
  setProperty = (obj, key, value) => {
    if (!obj) {
      return
    }
    // 递归赋值
    const inFn = function(_obj, keys, v) {
      // 最后一个属性key
      if (keys.length === 1) {
        _obj[keys[0]] = v
        return
      }
      // 0~length-1个key
      while (keys.length > 1) {
        const k = keys[0]
        if (!_obj[k] || (typeof _obj[k] !== 'object')) {
          _obj[k] = {}
        }
        const key = keys.shift()
        // 自调用判断是否存在属性，不存在则自动创建对象
        inFn(_obj[k], keys, v)
      }
    }

    if (typeof key !== 'string' || key === '') {

    } else if (key.indexOf('.') !== -1) { // 支持多层级赋值操作
      const keys = key.split('.')
      inFn(obj, keys, value)
    } else {
      obj[key] = value
    }
  }

  /**
   * 是否是对象
   * @param {Object} value
   */
  isObject(value) {
    return Object.prototype.toString.call(value) === '[object Object]'
  }

  /**
   * 移除空值
   */
  trim = (obj) => {
    let map = {};
    for (const key in obj) {
      if (Object.hasOwnProperty.call(obj, key)) {
        // 如果是字符串要 trim
        const value = (typeof obj[key] === 'string') ? obj[key].trim() : obj[key];
        if (!!value || value === 0) {
          map[key] = value;
        }
      }
    }
    return map;
  }

  /**
   * 将对象按照键名排序
   * @param obj 要处理的对象
   * @param type  排序方式，asc：升序，desc：降序
   * v1.0.1
   */
  sort = (obj, type = "asc") => {
    // 获取键名
    let newParams = {};
    let keys = Object.keys(obj).sort();
    if (type == "desc") {
      keys.reverse();
    }
    for (let i = 0; i < keys.length; i++) {
      let value = obj[keys[i]];
      newParams[keys[i]] = value;
    }
    return newParams;
  }
}