// 配置对象和工具函数
// 来源: u-charts.ts

/**
 * uCharts 默认配置接口
 */
export interface UChartsConfig {
  /** 版本号 */
  version: string;
  /** Y 轴宽度 */
  yAxisWidth: number;
  /** X 轴高度 */
  xAxisHeight: number;
  /** 内边距 [上, 右, 下, 左] */
  padding: [number, number, number, number];
  /** 是否旋转图表 */
  rotate: boolean;
  /** 默认字体大小 */
  fontSize: number;
  /** 默认字体颜色 */
  fontColor: string;
  /** 数据点形状列表 */
  dataPointShape: string[];
  /** 主题颜色列表 */
  color: string[];
  /** 渐变色列表 */
  linearColor: string[];
  /** 饼图折线间距 */
  pieChartLinePadding: number;
  /** 饼图文字间距 */
  pieChartTextPadding: number;
  /** 标题字体大小 */
  titleFontSize: number;
  /** 副标题字体大小 */
  subtitleFontSize: number;
  /** 雷达图标签文字边距 */
  radarLabelTextMargin: number;
}

/**
 * uCharts 默认配置对象
 */
export const config: UChartsConfig = {
  version: 'v2.5.0-20230101',
  yAxisWidth: 15,
  xAxisHeight: 22,
  padding: [10, 10, 10, 10],
  rotate: false,
  fontSize: 13,
  fontColor: '#666666',
  dataPointShape: ['circle', 'circle', 'circle', 'circle'],
  color: ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4', '#ea7ccc'],
  linearColor: ['#0EE2F8', '#2BDCA8', '#FA7D8D', '#EB88E2', '#2AE3A0', '#0EE2F8', '#EB88E2', '#6773E3', '#F78A85'],
  pieChartLinePadding: 15,
  pieChartTextPadding: 5,
  titleFontSize: 20,
  subtitleFontSize: 15,
  radarLabelTextMargin: 13,
};

/**
 * 深度合并对象
 * @param target - 目标对象
 * @param varArgs - 源对象数组
 * @returns 合并后的对象
 * @throws TypeError 如果 target 为 null 或 undefined
 */
export function assign<T>(target: T, ...varArgs: Partial<T>[]): T {
  if (target == null) {
    throw new TypeError('[uCharts] Cannot convert undefined or null to object');
  }
  if (!varArgs || varArgs.length <= 0) {
    return target;
  }
  // 深度合并对象
  function deepAssign(obj1: any, obj2: any): any {
    for (let key in obj2) {
      obj1[key] = obj1[key] && obj1[key].toString() === "[object Object]" ?
        deepAssign(obj1[key], obj2[key]) : obj1[key] = obj2[key];
    }
    return obj1;
  }
  varArgs.forEach(val => {
    target = deepAssign(target, val);
  });
  return target;
}

/**
 * 坐标点接口
 */
export interface Point {
  x: number;
  y: number;
}

/**
 * 碰撞对象接口
 */
export interface CollisionObject {
  start: Point;
  end?: Point;
  width: number;
  height: number;
  center?: Point;
  area?: {
    start: Point;
    end: Point;
    width: number;
    height: number;
  };
}

/**
 * 工具函数集合接口
 */
export interface Util {
  /** 保留小数位数 */
  toFixed(num: number, limit?: number): number | string;
  /** 判断是否为浮点数 */
  isFloat(num: number): boolean;
  /** 判断两个数是否近似相等 */
  approximatelyEqual(num1: number, num2: number): boolean;
  /** 判断两个数是否同号 */
  isSameSign(num1: number, num2: number): boolean;
  /** 判断两个点是否在相同的 X 坐标区域 */
  isSameXCoordinateArea(p1: Point, p2: Point): boolean;
  /** 检测两个对象是否碰撞 */
  isCollision(obj1: CollisionObject, obj2: CollisionObject): boolean;
}

/**
 * 工具函数集合
 */
export const util: Util = {
  toFixed: function toFixed(num: number, limit: number = 2): number | string {
    limit = limit || 2;
    if (this.isFloat(num)) {
      num = Number(num.toFixed(limit));
    }
    return num;
  },
  isFloat: function isFloat(num: number): boolean {
    return num % 1 !== 0;
  },
  approximatelyEqual: function approximatelyEqual(num1: number, num2: number): boolean {
    return Math.abs(num1 - num2) < 1e-10;
  },
  isSameSign: function isSameSign(num1: number, num2: number): boolean {
    return Math.abs(num1) === num1 && Math.abs(num2) === num2 || Math.abs(num1) !== num1 && Math.abs(num2) !== num2;
  },
  isSameXCoordinateArea: function isSameXCoordinateArea(p1: Point, p2: Point): boolean {
    return this.isSameSign(p1.x, p2.x);
  },
  isCollision: function isCollision(obj1: CollisionObject, obj2: CollisionObject): boolean {
    obj1.end = {} as Point;
    obj1.end.x = obj1.start.x + obj1.width;
    obj1.end.y = obj1.start.y - obj1.height;
    obj2.end = {} as Point;
    obj2.end.x = obj2.start.x + obj2.width;
    obj2.end.y = obj2.start.y - obj2.height;
    let flag = obj2.start.x > obj1.end.x || obj2.end.x < obj1.start.x || obj2.end.y > obj1.start.y || obj2.start.y < obj1.end.y;
    return !flag;
  }
};
