/**
 * 操作符类型运算符
 * +：加法运算
 * -：减法运算
 * *：乘法运算
 * /：除法运算
 */
type OperationType = '+' | '-' | '*' | '/';

class Big {
  /**
   * Big值
   */
  private v: number;
  /**
   * 构造函数
   * @param v 初始值
   */
  constructor(v: number) {
    this.v = v;
  }
  /**
   * 转换整数，返回倍数及整数值，比如
   * 100  >>> { times: 1, num: 100 }   ===> 100
   * 3.14 >>> { times: 100, num: 3.14} ===> 314
   * @param n number
   */
  private toInteger(n: number) {
    const ret = { times: 1, num: 0 };
    if (Number.isInteger(n)) {
      ret.num = n;
      return ret;
    }
    ret.times = Math.pow(10, n.toString().split('.')[1].length);
    ret.num = parseInt((n * ret.times + 0.5).toString(), 10);
    return ret;
  }
  /**
   * 执行运算
   * @param m 数值m
   * @param n 数值n
   * @param key 运算符
   */
  private operation(m: number = 0, n: number = 0, key: OperationType) {
    const o1 = this.toInteger(m);
    const o2 = this.toInteger(n);

    const n1 = o1.num;
    const n2 = o2.num;

    const t1 = o1.times;
    const t2 = o2.times;

    const max = Math.max(t1, t2);
    let result = 0;
    switch (key) {
      case '+':
        if (t1 === t2) {
          // 两个小数位数相同
          result = n1 + n2;
        } else if (t1 > t2) {
          // o1 小数位 大于 o2
          result = n1 + n2 * (t1 / t2);
        } else {
          // o1 小数位 小于 o2
          result = n1 * (t2 / t1) + n2;
        }
        result /= max;
        break;
      case '-':
        if (t1 === t2) {
          result = n1 - n2;
        } else if (t1 > t2) {
          result = n1 - n2 * (t1 / t2);
        } else {
          result = n1 * (t2 / t1) - n2;
        }
        result /= max;
        break;
      case '*':
        result = (n1 * n2) / (t1 * t2);
        break;
      case '/':
        result = (n1 * t2) / (t1 * n2);
        break;
      default:
        result = 0;
    }
    return new Big(result);
  }
  /**
   * 数值化
   * @param n
   */
  private numeric(n: number | Big) {
    return n instanceof Big ? n.v : n;
  }

  /**
   * 加法运算
   * @param n
   */
  public plus(n: number | Big) {
    return this.operation(this.v, this.numeric(n), '+');
  }
  /**
   * 减法运算
   * @param n
   */
  public minus(n: number | Big) {
    return this.operation(this.v, this.numeric(n), '-');
  }
  /**
   * 乘法运算
   * @param n
   */
  public multipliedBy(n: number | Big) {
    return this.operation(this.v, this.numeric(n), '*');
  }
  /**
   * 除法运算
   * @param n
   */
  public dividedBy(n: number | Big) {
    return this.operation(this.v, this.numeric(n), '/');
  }
  /**
   * 解析结果
   */
  public parse() {
    return this.v;
  }

  /**
   * 小数点后固定指定位数，比如固定小数点后5位数字，则有
   * 30 ==> 30.00000
   * 3.14 ===> 3.14000
   * @param n
   */
  public static digits(v: number | Big, len: number = 2) {
    if (Number.isInteger(v)) {
      return `${v}.${Array(len).fill(0).join('')}`;
    } else {
      const [prefix, suffix] = v.toString().split('.');
      const sLen = suffix.length;
      if (sLen > len) {
        return `${prefix}.${suffix.slice(0, len)}`;
      } else if (sLen < len) {
        return `${prefix}.${suffix}${Array(len - sLen)
          .fill(0)
          .join('')}`;
      } else {
        return `${prefix}.${suffix}`;
      }
    }
  }
  public digits(len: number) {
    return Big.digits(this.v, len);
  }

  /**
   * 人民币格式处理
   * - 非数字：返回0
   * - 整数：直接返回
   * - 小数：保留小数点后两位，超出两位则截取
   * @param v
   */
  public static rmb(v: string | number) {
    if (isNaN(Number(v))) {
      return '0';
    } else {
      const foo = v.toString();
      if (/^[0-9]+$/.test(foo)) {
        return foo;
      } else {
        const [prefix, suffix] = foo.split('.');
        const sLen = suffix.length;
        if (sLen > 2) {
          return `${prefix}.${suffix.slice(0, 2)}`;
        } else if (sLen < 2) {
          return `${foo}0`;
        } else {
          return foo;
        }
      }
    }
  }
  public rmb() {
    return Big.rmb(this.v);
  }
  /**
   * 切割数字
   * @param v
   */
  public static split(v: string | number) {
    if (isNaN(Number(v))) {
      return [];
    } else {
      return v
        .toString()
        .split('.')
        .map((item, i) => {
          if (i === 1) {
            return item && item.length < 2 ? `${item}0` : item;
          }
          return item;
        });
    }
  }
  public split() {
    return Big.split(this.v);
  }

  /**
   * 省略 --> 如果超过1万，则返回万制，转换后小数点后保留两位
   * 比如：12345 返回 1.23万
   * @param v
   */
  public static ellipsis(v: string | number) {
    // 如果不是数值类型并且转换之后不为数字，则直接返回传入值
    if (isNaN(Number(v))) {
      return v.toString();
    }
    // 如果传入数值小于1万则没必要转换，直接返回
    if (+v < 10000) {
      return v.toString();
    }
    // 超过1万，处理之后再返回
    return Big.rmb(+v / 10000) + '万';
  }
  public ellipsis(v: string | number) {
    return Big.ellipsis(this.v);
  }
}

export default Big;
