/**
 * 坐标轴计算函数
 *
 * 从 u-charts 核心库搬迁的坐标轴计算相关函数
 * 用于计算X轴、Y轴的数据和刻度
 */

import {
  dataCombine,
  dataCombineStack,
  getDataRange,
  type SeriesItem,
  type ChartOptions,
  type ChartExtraOptions,
  type BarOptions,
  type ColumnOptions,
  type TooltipOptions,
  type MountOptions,
  type XAxisOptions,
  type YAxisOptions,
  type YAxisDataItem,
  type ChartData,
  type UChartsConfig
} from './series-calculator';

// 重新导出类型以保持向后兼容
export type {
  SeriesItem,
  ChartOptions,
  ChartExtraOptions,
  BarOptions,
  ColumnOptions,
  TooltipOptions,
  MountOptions,
  XAxisOptions,
  YAxisOptions,
  YAxisDataItem,
  ChartData,
  UChartsConfig
};

// 坐标轴计算结果类型
export interface XAxisDataResult {
  angle: number;
  xAxisHeight: number;
  ranges: number[];
  rangesFormat: string[];
  xAxisPoints: number[];
  startX: number;
  endX: number;
  eachSpacing: number;
}

export interface YAxisDataResult {
  yAxisData: {
    categories?: string[];
    rangesFormat: string[][];
    ranges: number[][];
  }[];
  yAxisWidth: {
    position: string;
    width: number;
  }[];
}

export interface AxisPointsResult {
  xAxisPoints: number[];
  startX: number;
  endX: number;
  eachSpacing: number;
}

// 工具函数
const util = {
  toFixed: function toFixed(num: number, limit: number = 2): number | string {
    if (this.isFloat(num)) {
      num = Number(num.toFixed(limit));
    }
    return num;
  },
  isFloat: function isFloat(num: number): boolean {
    return num % 1 !== 0;
  }
};

function assign(target: any, ...sources: any[]): any {
  if (target == null) {
    throw new TypeError('[uCharts] Cannot convert undefined or null to object');
  }
  const result = { ...target };
  sources.forEach(val => {
    if (val != null) {
      Object.keys(val).forEach(key => {
        if (val[key] !== undefined) {
          result[key] = val[key];
        }
      });
    }
  });
  return result;
}

function measureText(text: string | number, fontSize: number, context?: any): number {
  let width = 0;
  text = String(text);
  if (context !== false && context !== undefined && context.setFontSize && context.measureText) {
    context.setFontSize(fontSize);
    return context.measureText(text).width;
  } else {
    const textArray = text.split('');
    for (let i = 0; i < textArray.length; i++) {
      const item = textArray[i];
      if (/[a-zA-Z]/.test(item)) {
        width += 7;
      } else if (/[0-9]/.test(item)) {
        width += 5.5;
      } else if (/\./.test(item)) {
        width += 2.7;
      } else if (/-/.test(item)) {
        width += 3.25;
      } else if (/:/.test(item)) {
        width += 2.5;
      } else if (/[\u4e00-\u9fa5]/.test(item)) {
        width += 10;
      } else if (/\(|\)/.test(item)) {
        width += 3.73;
      } else if (/\s/.test(item)) {
        width += 2.5;
      } else if (/%/.test(item)) {
        width += 8;
      } else {
        width += 10;
      }
    }
    return width * fontSize / 10;
  }
}

/**
 * 计算X轴数据
 * 包括刻度、标签等
 *
 * @param series - 系列数据数组
 * @param opts - 图表配置选项
 * @param config - uCharts配置对象
 * @param context - Canvas上下文
 * @returns X轴数据结果
 */
export function calXAxisData(
  series: SeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  context?: any
): XAxisDataResult {
  const columnstyle = assign({}, {
    type: ""
  }, opts.extra?.column || {});
  const result: XAxisDataResult = {
    angle: 0,
    xAxisHeight: (opts.xAxis?.lineHeight || 0) * (opts.pix || 1) + (opts.xAxis?.marginTop || 0) * (opts.pix || 1),
    ranges: [],
    rangesFormat: [],
    xAxisPoints: [],
    startX: 0,
    endX: 0,
    eachSpacing: 0
  };
  result.ranges = getXAxisTextList(series, opts, config, columnstyle.type || '');
  result.rangesFormat = result.ranges.map(function(item) {
    const numItem = Number(item);
    return String(util.toFixed(numItem, 2));
  });
  const xAxisScaleValues = result.ranges.map(function(item) {
    const numItem = Number(item);
    return util.toFixed(numItem, 2);
  });
  Object.assign(result, getXAxisPoints(xAxisScaleValues as any, opts, config));
  const eachSpacing = result.eachSpacing;
  const textLength = xAxisScaleValues.map(function(item) {
    return measureText(String(item), (opts.xAxis?.fontSize || 13) * (opts.pix || 1), context);
  });
  if (opts.xAxis?.disabled === true) {
    result.xAxisHeight = 0;
  }
  return result;
}

/**
 * 获取X轴文本列表
 * 计算X轴刻度值范围
 *
 * @param series - 系列数据数组
 * @param opts - 图表配置选项
 * @param config - uCharts配置对象
 * @param stack - 堆叠类型
 * @param index - Y轴索引
 * @returns 刻度值范围数组
 */
function getXAxisTextList(
  series: SeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  stack: string = '',
  index: number = -1
): number[] {
  let data: (number | null)[];
  if (stack == 'stack') {
    data = dataCombineStack(series, opts.categories?.length || 0);
  } else {
    data = dataCombine(series);
  }
  const sorted: number[] = [];
  data = data.filter(function(item) {
    if (typeof item === 'object' && item !== null) {
      if (Array.isArray(item)) {
        return item !== null;
      } else {
        return (item as any).value !== null;
      }
    } else {
      return item !== null;
    }
  });
  data.forEach(function(item) {
    if (typeof item === 'object' && item !== null) {
      if (Array.isArray(item)) {
        if (opts.type == 'candle') {
          (item as any).forEach(function(subitem: any) {
            sorted.push(subitem as number);
          });
        } else {
          sorted.push((item as any)[0] as number);
        }
      } else {
        sorted.push((item as any).value);
      }
    } else if (item !== null) {
      sorted.push(item);
    }
  });

  let minData = 0;
  let maxData = 0;
  if (sorted.length > 0) {
    minData = Math.min(...sorted);
    maxData = Math.max(...sorted);
  }
  if (index > -1) {
    const xAxisData = (opts.xAxis as any)?.data;
    if (xAxisData && typeof xAxisData[index]?.min === 'number') {
      minData = Math.min(xAxisData[index].min, minData);
    }
    if (xAxisData && typeof xAxisData[index]?.max === 'number') {
      maxData = Math.max(xAxisData[index].max, maxData);
    }
  } else {
    if (typeof opts.xAxis?.min === 'number') {
      minData = Math.min(opts.xAxis.min, minData);
    }
    if (typeof opts.xAxis?.max === 'number') {
      maxData = Math.max(opts.xAxis.max, maxData);
    }
  }
  if (minData === maxData) {
    const rangeSpan = maxData || 10;
    maxData += rangeSpan;
  }
  const minRange = minData;
  const maxRange = maxData;
  const splitNumber = opts.xAxis?.splitNumber || 5;
  const range: number[] = [];
  const eachRange = (maxRange - minRange) / splitNumber;
  for (let i = 0; i <= splitNumber; i++) {
    range.push(minRange + eachRange * i);
  }
  return range;
}

/**
 * 获取X轴数据点坐标
 *
 * @param categories - 分类数据
 * @param opts - 图表配置选项
 * @param config - uCharts配置对象
 * @returns X轴数据点结果
 */
export function getXAxisPoints(
  categories: (string | number)[],
  opts: ChartOptions,
  config: UChartsConfig
): AxisPointsResult {
  const spacingValid = (opts.width || 0) - (opts.area?.[1] || 0) - (opts.area?.[3] || 0);
  const dataCount = opts.enableScroll
    ? Math.min(opts.xAxis?.itemCount || categories.length, categories.length)
    : categories.length;
  const type = opts.type;
  let modifiedDataCount = dataCount;
  if ((type == 'line' || type == 'area' || type == 'scatter' || type == 'bubble' || type == 'bar') &&
      dataCount > 1 &&
      opts.xAxis?.boundaryGap == 'justify') {
    modifiedDataCount -= 1;
  }
  let widthRatio = 0;
  if (type == 'mount' &&
      opts.extra?.mount &&
      opts.extra.mount.widthRatio &&
      opts.extra.mount.widthRatio > 1) {
    if (opts.extra.mount.widthRatio > 2) {
      (opts.extra.mount.widthRatio as any) = 2;
    }
    widthRatio = opts.extra.mount.widthRatio - 1;
    modifiedDataCount += widthRatio;
  }
  const eachSpacing = spacingValid / modifiedDataCount;
  const xAxisPoints: number[] = [];
  const startX = opts.area?.[3] || 0;
  const endX = (opts.width || 0) - (opts.area?.[1] || 0);
  categories.forEach(function(_item, index) {
    xAxisPoints.push(startX + widthRatio / 2 * eachSpacing + index * eachSpacing);
  });
  if (opts.xAxis?.boundaryGap !== 'justify') {
    if (opts.enableScroll === true) {
      xAxisPoints.push(startX + widthRatio * eachSpacing + categories.length * eachSpacing);
    } else {
      xAxisPoints.push(endX);
    }
  }
  return {
    xAxisPoints,
    startX,
    endX,
    eachSpacing
  };
}

/**
 * 计算Y轴数据
 * 包括刻度、标签等，支持多Y轴
 *
 * @param series - 系列数据数组
 * @param opts - 图表配置选项
 * @param config - uCharts配置对象
 * @param context - Canvas上下文
 * @returns Y轴数据结果
 */
export function calYAxisData(
  series: SeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  context?: any
): YAxisDataResult {
  const columnstyle = assign({}, {
    type: ""
  }, opts.extra?.column || {});
  const YLength = opts.yAxis?.data?.length || 0;
  const newSeries: SeriesItem[][] = new Array(YLength);
  if (YLength > 0) {
    for (let i = 0; i < YLength; i++) {
      newSeries[i] = [];
      for (let j = 0; j < series.length; j++) {
        if (series[j].index == i) {
          newSeries[i].push(series[j]);
        }
      }
    }
  }

  const rangesArr: (number[] | string[])[] = new Array(YLength);
  const rangesFormatArr: string[][] = new Array(YLength);
  const yAxisWidthArr: { position: string; width: number }[] = new Array(YLength);

  for (let i = 0; i < YLength; i++) {
    let yData = opts.yAxis?.data?.[i] as YAxisDataItem | undefined;
    if (!yData) {
      yData = {} as YAxisDataItem;
    }
    if (opts.yAxis?.disabled == true) {
      yData.disabled = true;
    }
    if (yData.type === 'categories') {
      if (!yData.formatter) {
        yData.formatter = (val: any, _index: number, _opts: ChartOptions) => {
          return String(val) + (yData?.unit || '');
        };
      }
      yData.categories = yData.categories || opts.categories;
      rangesArr[i] = yData.categories || [];
    } else {
      if (!yData.formatter) {
        yData.formatter = (val: any, _index: number, _opts: ChartOptions) => {
          return String(util.toFixed(Number(val), yData?.tofix || 0)) + (yData?.unit || '');
        };
      }
      rangesArr[i] = getYAxisTextList(newSeries[i] || [], opts, config, columnstyle.type || '', yData, i);
    }
    const yAxisFontSizes = (yData.fontSize || opts.yAxis?.fontSize || config.fontSize) * (opts.pix || 1);
    yAxisWidthArr[i] = {
      position: yData.position ? yData.position : 'left',
      width: 0
    };
    rangesFormatArr[i] = (rangesArr[i] as number[]).map(function(items, index) {
      const formatted = yData.formatter!(items, index, opts);
      yAxisWidthArr[i].width = Math.max(yAxisWidthArr[i].width, measureText(formatted, yAxisFontSizes, context) + 5);
      return formatted;
    });
    const calibration = yData.calibration ? 4 * (opts.pix || 1) : 0;
    yAxisWidthArr[i].width += calibration + 3 * (opts.pix || 1);
    if (yData.disabled === true) {
      yAxisWidthArr[i].width = 0;
    }
  }

  return {
    yAxisData: rangesArr.map((ranges, i) => {
      const isCategories = typeof ranges[0] === 'string';
      return {
        ranges: !isCategories ? [ranges as number[]] : [],
        rangesFormat: [rangesFormatArr[i]],
        categories: isCategories ? ranges as string[] : undefined
      };
    }),
    yAxisWidth: yAxisWidthArr
  };
}

/**
 * 获取Y轴文本列表
 * 计算Y轴刻度值范围
 *
 * @param series - 系列数据数组
 * @param opts - 图表配置选项
 * @param config - uCharts配置对象
 * @param stack - 堆叠类型
 * @param yData - Y轴数据配置
 * @param index - Y轴索引
 * @returns 刻度值范围数组
 */
function getYAxisTextList(
  series: SeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  stack: string = '',
  yData: YAxisDataItem = {},
  index: number = -1
): number[] {
  let data: (number | null)[];
  if (stack == 'stack') {
    data = dataCombineStack(series, opts.categories?.length || 0);
  } else {
    data = dataCombine(series);
  }
  const sorted: number[] = [];
  data = data.filter(function(item) {
    if (typeof item === 'object' && item !== null) {
      if (Array.isArray(item)) {
        return item !== null;
      } else {
        return (item as any).value !== null;
      }
    } else {
      return item !== null;
    }
  });
  data.forEach(function(item) {
    if (typeof item === 'object' && item !== null) {
      if (Array.isArray(item)) {
        if (opts.type == 'candle') {
          (item as any).forEach(function(subitem: any) {
            sorted.push(subitem as number);
          });
        } else {
          sorted.push((item as any)[1] as number);
        }
      } else {
        sorted.push((item as any).value);
      }
    } else if (item !== null) {
      sorted.push(item);
    }
  });
  let minData = yData.min || 0;
  let maxData = yData.max || 0;
  if (sorted.length > 0) {
    minData = Math.min(...sorted);
    maxData = Math.max(...sorted);
  }
  if (minData === maxData) {
    if (maxData == 0) {
      maxData = 10;
    } else {
      minData = 0;
    }
  }
  const dataRange = getDataRange(minData, maxData);
  const minRange = (yData.min === undefined || yData.min === null) ? dataRange.minRange : yData.min;
  const maxRange = (yData.max === undefined || yData.max === null) ? dataRange.maxRange : yData.max;
  const splitNumber = opts.yAxis?.splitNumber || 5;
  const eachRange = (maxRange - minRange) / splitNumber;
  const range: number[] = [];
  for (let i = 0; i <= splitNumber; i++) {
    range.push(minRange + eachRange * i);
  }
  return range.reverse();
}
