/**
 * uCharts 主类
 *
 * 从 u-charts 核心库搬迁的核心图表类
 * 添加完整类型注解
 */

import { uChartsEvent } from './u-charts-event';
import { config as defaultConfig, assign, util, UChartsConfig } from '../config';
import { getH5Offset, getTouches as getTouchesUtil, DOMEvent } from '../utils/misc';
import { measureText } from '../utils/text';
import { calValidDistance, UChartInstance as CoordinateUChartInstance } from '../utils/coordinate';
import * as helperFunctions from '../helper-functions';
import * as dataProcessing from '../data-processing';
import * as chartsData from '../charts-data';
import * as renderers from '../renderers';
import { drawCharts } from '../draw-controllers';

// 导入所有需要的函数
const {
  // Finders
  findPieChartCurrentIndex,
  findRoseChartCurrentIndex,
  findRadarChartCurrentIndex,
  findFunnelChartCurrentIndex,
  findMapChartCurrentIndex,
  findWordChartCurrentIndex,
  findBarChartCurrentIndex,
  findCurrentIndex,
  findLegendIndex,
  // Data helpers
  getSeriesDataItem,
  getCandleToolTipData,
  // Legend helpers
  calLegendData,
  getPieTextMaxLength
} = helperFunctions;

const {
  fixPieSeries,
  fillSeries,
  fillCustomColor,
  getDataRange,
  dataCombine,
  dataCombineStack,
  calXAxisData,
  getXAxisPoints,
  calYAxisData,
  calCategoriesData,
  getToolTipData,
  getMixToolTipData
} = dataProcessing;

const {
  getDataPoints,
  getLineDataPoints,
  getColumnDataPoints,
  getCandleDataPoints,
  getMountDataPoints,
  getBarDataPoints,
  getStackDataPoints,
  getBarStackDataPoints,
  getPieDataPoints,
  getRoseDataPoints,
  getRadarDataPoints,
  getGaugeDataPoints,
  getGaugeArcbarDataPoints,
  getArcbarDataPoints,
  getGaugeAxisPoints,
  getFunnelDataPoints
} = chartsData;

const {
  drawXAxis,
  drawYAxisGrid,
  drawYAxis,
  drawLegend,
  drawGaugeLabel,
  drawRadarLabel,
  drawColumnDataPoints,
  drawBarDataPoints,
  drawMountDataPoints,
  drawLineDataPoints,
  drawAreaDataPoints,
  drawCandleDataPoints,
  drawPieDataPoints,
  drawRadarDataPoints,
  drawMapDataPoints,
  drawFunnelDataPoints,
  drawWordCloudDataPoints,
  drawMixDataPoints,
  drawScatterDataPoints,
  drawBubbleDataPoints
} = renderers;

// 简化函数调用别名
const getTouches = getTouchesUtil;

/**
 * Canvas 上下文接口
 * 使用 ExtendedCanvasContext，兼容 Taro 跨平台环境
 */
export type CanvasContext = import('../../types').ExtendedCanvasContext;

/**
 * 图表标题配置
 */
export interface ChartsTitle {
  name?: string;
  fontSize?: number;
  color?: string;
  offsetX?: number;
  offsetY?: number;
  [key: string]: any;
}

/**
 * Y轴配置
 */
export interface YAxisConfig {
  data?: any[];
  showTitle?: boolean;
  disabled?: boolean;
  disableGrid?: boolean;
  gridSet?: string;
  splitNumber?: number;
  gridType?: string;
  dashLength?: number;
  gridColor?: string;
  padding?: number;
  fontColor?: string;
  min?: number;
  max?: number;
  format?: (val: number) => string | number;
  [key: string]: any;
}

/**
 * X轴配置
 */
export interface XAxisConfig {
  rotateLabel?: boolean;
  rotateAngle?: number;
  disabled?: boolean;
  disableGrid?: boolean;
  splitNumber?: number;
  calibration?: boolean;
  fontColor?: string;
  fontSize?: number;
  lineHeight?: number;
  marginTop?: number;
  gridType?: string;
  dashLength?: number;
  scrollAlign?: string;
  scrollPosition?: string;
  boundaryGap?: string;
  axisLine?: boolean;
  axisLineColor?: string;
  titleFontSize?: number;
  titleOffsetY?: number;
  titleOffsetX?: number;
  titleFontColor?: string;
  itemCount?: number;
  enableScroll?: boolean;
  [key: string]: any;
}

/**
 * 图例配置
 */
export interface LegendConfig {
  show?: boolean;
  position?: string;
  float?: string;
  backgroundColor?: string;
  borderColor?: string;
  borderWidth?: number;
  padding?: number;
  margin?: number;
  itemGap?: number;
  fontSize?: number;
  lineHeight?: number;
  fontColor?: string;
  formatter?: any;
  hiddenColor?: string;
  [key: string]: any;
}

/**
 * 额外配置
 */
export interface ExtraConfig {
  tooltip?: {
    legendShape?: string;
    [key: string]: any;
  };
  pie?: {
    labelWidth?: number;
    [key: string]: any;
  };
  ring?: {
    labelWidth?: number;
    [key: string]: any;
  };
  rose?: {
    labelWidth?: number;
    [key: string]: any;
  };
  candle?: {
    [key: string]: any;
  };
  [key: string]: any;
}

/**
 * 图表配置项
 */
export interface ChartsConfig {
  pixelRatio?: number;
  fontSize?: number;
  fontColor?: string;
  background?: string;
  title?: ChartsTitle;
  subtitle?: ChartsTitle;
  duration?: number;
  yAxis?: YAxisConfig;
  xAxis?: XAxisConfig;
  legend?: LegendConfig;
  extra?: ExtraConfig;
  rotate?: boolean;
  animation?: boolean;
  canvas2d?: boolean;
  width?: number;
  height?: number;
  padding?: number[];
  context?: CanvasContext;
  type?: string;
  categories?: any[];
  series?: any[];
  seriesMA?: any[];
  color?: any[];
  dataLabel?: boolean;
  enableScroll?: boolean;
  touchMoveLimit?: number;
  area?: number[];
  chartData?: any;
  updateData?: boolean;
  _scrollDistance_?: number;
  _series_?: any[];
  pix?: number;
  [key: string]: any;
}

/**
 * 滚动选项
 */
export interface ScrollOption {
  currentOffset: number;
  startTouchX: number;
  distance: number;
  lastMoveTime: number;
  moveCount?: number;
  moveCurrent1?: number;
  moveCurrent2?: number;
}

/**
 * 触摸事件接口
 */
export interface TouchEvent {
  changedTouches?: TouchPoint[];
  mp?: {
    changedTouches?: TouchPoint[];
  };
  [key: string]: any;
}

/**
 * 触摸点接口
 */
export interface TouchPoint {
  x?: number;
  y?: number;
  clientX?: number;
  clientY?: number;
  [key: string]: any;
}

/**
 * 提示框选项
 */
export interface ToolTipOption {
  textList?: any[];
  offset?: {
    x: number;
    y: number;
  };
  index?: number | number[];
  group?: string[];
  formatter?: (item: any, category: any, index: number, opts: any) => string;
  [key: string]: any;
}

/**
 * uCharts 主类
 *
 * 核心图表类，提供所有图表操作方法
 */
export class uCharts extends uChartsEvent {
  /** 图表配置 */
  opts: ChartsConfig;

  /** Canvas 上下文 */
  context: CanvasContext;

  /** 内部配置 */
  config: any;

  /** 事件处理器 */
  uevent: uChartsEvent;

  /** 滚动选项 */
  scrollOption: ScrollOption;

  /** 动画实例 */
  animationInstance?: any;

  /**
   * 构造函数
   * @param opts - 图表配置项
   */
  constructor(opts: ChartsConfig) {
    super();

    // 确保必需的属性存在
    if (!opts.height) opts.height = 300;
    if (!opts.width) opts.width = 300;
    if (!opts.pix) opts.pix = opts.pixelRatio ? opts.pixelRatio : 1;
    opts.fontSize = opts.fontSize ? opts.fontSize : 13;
    opts.fontColor = opts.fontColor ? opts.fontColor : defaultConfig.fontColor;
    if (opts.background == "" || opts.background == "none") {
      opts.background = "#FFFFFF"
    }
    opts.title = assign({}, opts.title || {});
    opts.subtitle = assign({}, opts.subtitle || {});
    opts.duration = opts.duration ? opts.duration : 1000;
    opts.yAxis = assign({}, {
      data: [],
      showTitle: false,
      disabled: false,
      disableGrid: false,
      gridSet: 'number',
      splitNumber: 5,
      gridType: 'solid',
      dashLength: 4 * opts.pix,
      gridColor: '#cccccc',
      padding: 10,
      fontColor: '#666666'
    }, opts.yAxis || {});
    opts.xAxis = assign({}, {
      rotateLabel: false,
      rotateAngle: 45,
      disabled: false,
      disableGrid: false,
      splitNumber: 5,
      calibration: false,
      fontColor: '#666666',
      fontSize: 13,
      lineHeight: 20,
      marginTop: 0,
      gridType: 'solid',
      dashLength: 4,
      scrollAlign: 'left',
      boundaryGap: 'center',
      axisLine: true,
      axisLineColor: '#cccccc',
      titleFontSize: 13,
      titleOffsetY: 0,
      titleOffsetX: 0,
      titleFontColor: '#666666'
    }, opts.xAxis || {});
    opts.xAxis.scrollPosition = opts.xAxis.scrollAlign;
    opts.legend = assign({}, {
      show: true,
      position: 'bottom',
      float: 'center',
      backgroundColor: 'rgba(0,0,0,0)',
      borderColor: 'rgba(0,0,0,0)',
      borderWidth: 0,
      padding: 5,
      margin: 5,
      itemGap: 10,
      fontSize: opts.fontSize,
      lineHeight: opts.fontSize,
      fontColor: opts.fontColor,
      formatter: {},
      hiddenColor: '#CECECE'
    }, opts.legend || {});
    opts.extra = {
      tooltip: {
        legendShape: 'auto'
      },
      ...(opts.extra || {})
    } as ExtraConfig;
    opts.rotate = opts.rotate ? true : false;
    opts.animation = opts.animation ? true : false;
    opts.rotate = opts.rotate ? true : false;
    opts.canvas2d = opts.canvas2d ? true : false;

    let config: UChartsConfig = { ...defaultConfig };
    config.color = opts.color ? opts.color : config.color;
    if (opts.type == 'pie') {
      const pieLabelWidth = opts.extra?.pie?.labelWidth ?? 0;
      config.pieChartLinePadding = opts.dataLabel === false ? 0 : pieLabelWidth * opts.pix || config.pieChartLinePadding * opts.pix;
    }
    if (opts.type == 'ring') {
      const ringLabelWidth = opts.extra?.ring?.labelWidth ?? 0;
      config.pieChartLinePadding = opts.dataLabel === false ? 0 : ringLabelWidth * opts.pix || config.pieChartLinePadding * opts.pix;
    }
    if (opts.type == 'rose') {
      const roseLabelWidth = opts.extra?.rose?.labelWidth ?? 0;
      config.pieChartLinePadding = opts.dataLabel === false ? 0 : roseLabelWidth * opts.pix || config.pieChartLinePadding * opts.pix;
    }
    config.pieChartTextPadding = opts.dataLabel === false ? 0 : config.pieChartTextPadding * opts.pix;

    // 屏幕旋转
    config.rotate = opts.rotate;
    if (opts.rotate) {
      let tempWidth = opts.width;
      let tempHeight = opts.height;
      opts.width = tempHeight;
      opts.height = tempWidth;
    }

    // 适配高分屏
    opts.padding = opts.padding ? opts.padding : config.padding;
    config.yAxisWidth = config.yAxisWidth * opts.pix;
    config.fontSize = opts.fontSize * opts.pix;
    config.titleFontSize = config.titleFontSize * opts.pix;
    config.subtitleFontSize = config.subtitleFontSize * opts.pix;
    if (!opts.context) {
      throw new Error('[uCharts] 未获取到context！注意：v2.0版本后，需要自行获取canvas的绘图上下文并传入opts.context！');
    }
    this.context = opts.context;
    if (!this.context.setTextAlign) {
      this.context.setStrokeStyle = function(e: string) {
        return this.strokeStyle = e;
      }
      this.context.setLineWidth = function(e: number) {
        return this.lineWidth = e;
      }
      this.context.setLineCap = function(e: string) {
        return this.lineCap = e;
      }
      this.context.setFontSize = function(e: number) {
        return this.font = e + "px sans-serif";
      }
      this.context.setFillStyle = function(e: string) {
        return this.fillStyle = e;
      }
      this.context.setTextAlign = function(e: string) {
        return this.textAlign = e;
      }
      this.context.setTextBaseline = function(e: string) {
        return this.textBaseline = e;
      }
      this.context.setShadow = function(offsetX: number, offsetY: number, blur: number, color: string) {
        this.shadowColor = color;
        this.shadowOffsetX = offsetX;
        this.shadowOffsetY = offsetY;
        this.shadowBlur = blur;
      }
      this.context.draw = function() {}
    }
    // 兼容NVUEsetLineDash
    if (!this.context.setLineDash) {
      this.context.setLineDash = function(e: number[]) {}
    }
    opts.chartData = {};
    this.uevent = new uChartsEvent();
    this.scrollOption = {
      currentOffset: 0,
      startTouchX: 0,
      distance: 0,
      lastMoveTime: 0
    };
    this.opts = opts;
    this.config = config;
    drawCharts.call(this, opts.type || '', opts as any, config, this.context);
  }

  /**
   * 更新图表数据
   * @param data - 新的数据
   */
  updateData(data: any = {}): void {
    this.opts = assign({}, this.opts, data);
    this.opts.updateData = true;
    let scrollPosition = data.scrollPosition || 'current';
    switch (scrollPosition) {
      case 'current':
        this.opts._scrollDistance_ = this.scrollOption.currentOffset;
        break;
      case 'left':
        this.opts._scrollDistance_ = 0;
        this.scrollOption = {
          currentOffset: 0,
          startTouchX: 0,
          distance: 0,
          lastMoveTime: 0
        };
        break;
      case 'right':
        let _calYAxisData = calYAxisData(this.opts.series || [], this.opts as any, this.config, this.context);
        let yAxisWidth = _calYAxisData.yAxisWidth;
        this.config.yAxisWidth = yAxisWidth;
        let offsetLeft = 0;
        let _getXAxisPoints0 = getXAxisPoints(this.opts.categories || [], this.opts as any, this.config);
        let xAxisPoints = _getXAxisPoints0.xAxisPoints;
        let startX = _getXAxisPoints0.startX;
        let endX = _getXAxisPoints0.endX;
        let eachSpacing = _getXAxisPoints0.eachSpacing;
        let totalWidth = eachSpacing * (xAxisPoints.length - 1);
        let screenWidth = endX - startX;
        offsetLeft = screenWidth - totalWidth;
        this.scrollOption = {
          currentOffset: offsetLeft,
          startTouchX: offsetLeft,
          distance: 0,
          lastMoveTime: 0
        };
        this.opts._scrollDistance_ = offsetLeft;
        break;
    }
    drawCharts.call(this, this.opts.type || '', this.opts as any, this.config, this.context);
  }

  /**
   * 缩放图表
   * @param val - 缩放配置
   */
  zoom(val: any = this.opts.xAxis?.itemCount): void {
    if (this.opts.enableScroll !== true) {
      console.log('[uCharts] 请启用滚动条后使用')
      return;
    }
    // 当前屏幕中间点
    if (!this.opts.chartData || !this.opts.chartData.eachSpacing) {
      console.log('[uCharts] 图表数据未初始化')
      return;
    }
    let centerPoint = Math.round(Math.abs(this.scrollOption.currentOffset) / this.opts.chartData.eachSpacing) + Math.round((this.opts.xAxis?.itemCount || 0) / 2);
    this.opts.animation = false;
    if (!this.opts.xAxis) return;
    this.opts.xAxis.itemCount = val.itemCount;
    // 重新计算x轴偏移距离
    let _calYAxisData = calYAxisData(this.opts.series || [], this.opts as any, this.config, this.context);
    let yAxisWidth = _calYAxisData.yAxisWidth;
    this.config.yAxisWidth = yAxisWidth;
    let offsetLeft = 0;
    let _getXAxisPoints0 = getXAxisPoints(this.opts.categories || [], this.opts as any, this.config);
    let xAxisPoints = _getXAxisPoints0.xAxisPoints;
    let startX = _getXAxisPoints0.startX;
    let endX = _getXAxisPoints0.endX;
    let eachSpacing = _getXAxisPoints0.eachSpacing;
    let centerLeft = eachSpacing * centerPoint;
    let screenWidth = endX - startX;
    let MaxLeft = screenWidth - eachSpacing * (xAxisPoints.length - 1);
    offsetLeft = screenWidth / 2 - centerLeft;
    if (offsetLeft > 0) {
      offsetLeft = 0;
    }
    if (offsetLeft < MaxLeft) {
      offsetLeft = MaxLeft;
    }
    this.scrollOption = {
      currentOffset: offsetLeft,
      startTouchX: 0,
      distance: 0,
      lastMoveTime: 0
    };
    const self = this as unknown as CoordinateUChartInstance;
    self.scrollOption = { position: this.scrollOption.currentOffset };
    calValidDistance(self, offsetLeft, this.opts.chartData || { eachSpacing: 0 }, this.config as any, this.opts as any);
    this.opts._scrollDistance_ = offsetLeft;
    drawCharts.call(this, this.opts.type || '', this.opts as any, this.config, this.context);
  }

  /**
   * 双指缩放
   * @param e - 触摸事件
   */
  dobuleZoom(e: TouchEvent): void {
    if (this.opts.enableScroll !== true) {
      console.log('[uCharts] 请启用滚动条后使用')
      return;
    }
    const tcs = e.changedTouches;
    if (!tcs || tcs.length < 2) {
      return;
    }
    for (let i = 0; i < tcs.length; i++) {
      tcs[i].x = tcs[i].x ? tcs[i].x : tcs[i].clientX;
      tcs[i].y = tcs[i].y ? tcs[i].y : tcs[i].clientY;
    }
    const ntcs = [getTouches(tcs[0], this.opts as any, e as any), getTouches(tcs[1], this.opts as any, e as any)];
    const xlength = Math.abs((ntcs[0].x || 0) - (ntcs[1].x || 0));
    // 记录初始的两指之间的数据
    if (!this.scrollOption.moveCount) {
      if (!this.opts.area || !this.opts.height) return;
      let cts0 = { changedTouches: [{ x: tcs[0].x || 0, y: this.opts.area[0] / (this.opts.pix || 1) + 2 }] };
      let cts1 = { changedTouches: [{ x: tcs[1].x || 0, y: this.opts.area[0] / (this.opts.pix || 1) + 2 }] };
      if (this.opts.rotate) {
        cts0 = { changedTouches: [{ x: this.opts.height / (this.opts.pix || 1) - this.opts.area[0] / (this.opts.pix || 1) - 2, y: tcs[0].y || 0 }] };
        cts1 = { changedTouches: [{ x: this.opts.height / (this.opts.pix || 1) - this.opts.area[0] / (this.opts.pix || 1) - 2, y: tcs[1].y || 0 }] };
      }
      const moveCurrent1 = this.getCurrentDataIndex(cts0).index;
      const moveCurrent2 = this.getCurrentDataIndex(cts1).index;
      const moveCount = Math.abs(moveCurrent1 - moveCurrent2);
      this.scrollOption.moveCount = moveCount;
      this.scrollOption.moveCurrent1 = Math.min(moveCurrent1, moveCurrent2);
      this.scrollOption.moveCurrent2 = Math.max(moveCurrent1, moveCurrent2);
      return;
    }

    let currentEachSpacing = xlength / (this.scrollOption.moveCount || 1);
    if (!this.opts.area || !this.opts.width || !this.opts.xAxis) return;
    let itemCount = (this.opts.width - this.opts.area[1] - this.opts.area[3]) / currentEachSpacing;
    itemCount = itemCount <= 2 ? 2 : itemCount;
    if (!this.opts.categories) return;
    itemCount = itemCount >= this.opts.categories.length ? this.opts.categories.length : itemCount;
    this.opts.animation = false;
    this.opts.xAxis.itemCount = itemCount;
    // 重新计算滚动条偏移距离
    let offsetLeft = 0;
    let _getXAxisPoints0 = getXAxisPoints(this.opts.categories || [], this.opts as any, this.config);
    let xAxisPoints = _getXAxisPoints0.xAxisPoints;
    let startX = _getXAxisPoints0.startX;
    let endX = _getXAxisPoints0.endX;
    let eachSpacing = _getXAxisPoints0.eachSpacing;
    let currentLeft = eachSpacing * (this.scrollOption.moveCurrent1 || 0);
    let screenWidth = endX - startX;
    let MaxLeft = screenWidth - eachSpacing * (xAxisPoints.length - 1);
    offsetLeft = -currentLeft + Math.min(ntcs[0].x || 0, ntcs[1].x || 0) - (this.opts.area[3] || 0) - eachSpacing;
    if (offsetLeft > 0) {
      offsetLeft = 0;
    }
    if (offsetLeft < MaxLeft) {
      offsetLeft = MaxLeft;
    }
    this.scrollOption.currentOffset = offsetLeft;
    this.scrollOption.startTouchX = 0;
    this.scrollOption.distance = 0;
    const self = this as unknown as CoordinateUChartInstance;
    self.scrollOption = { position: this.scrollOption.currentOffset };
    calValidDistance(self, offsetLeft, this.opts.chartData || { eachSpacing: 0 }, this.config as any, this.opts as any);
    this.opts._scrollDistance_ = offsetLeft;
    drawCharts.call(this, this.opts.type || '', this.opts as any, this.config, this.context);
  }

  /**
   * 停止动画
   */
  stopAnimation(): void {
    this.animationInstance && this.animationInstance.stop();
  }

  /**
   * 获取当前数据索引
   * @param e - 触摸事件
   * @returns 当前数据索引
   */
  getCurrentDataIndex(e: TouchEvent): any {
    let touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else if (e.mp && e.mp.changedTouches) {
      touches = e.mp.changedTouches[0];
    }
    if (touches) {
      let _touches$ = getTouches(touches, this.opts as any, e as any);
      if (!this.opts.chartData) return { index: -1 };
      if (this.opts.type === 'pie' || this.opts.type === 'ring') {
        return findPieChartCurrentIndex({
          x: _touches$.x || 0,
          y: _touches$.y || 0
        }, this.opts.chartData.pieData || [], this.opts as any);
      } else if (this.opts.type === 'rose') {
        return findRoseChartCurrentIndex({
          x: _touches$.x || 0,
          y: _touches$.y || 0
        }, this.opts.chartData.pieData || [], this.opts as any);
      } else if (this.opts.type === 'radar') {
        return findRadarChartCurrentIndex({
          x: _touches$.x || 0,
          y: _touches$.y || 0
        }, this.opts.chartData.radarData || [], this.opts.categories?.length || 0);
      } else if (this.opts.type === 'funnel') {
        return findFunnelChartCurrentIndex({
          x: _touches$.x || 0,
          y: _touches$.y || 0
        }, this.opts.chartData.funnelData || []);
      } else if (this.opts.type === 'map') {
        return findMapChartCurrentIndex({
          x: _touches$.x || 0,
          y: _touches$.y || 0
        }, this.opts as any);
      } else if (this.opts.type === 'word') {
        return findWordChartCurrentIndex({
          x: _touches$.x || 0,
          y: _touches$.y || 0
        }, this.opts.chartData.wordCloudData || []);
      } else if (this.opts.type === 'bar') {
        return findBarChartCurrentIndex({
          x: _touches$.x || 0,
          y: _touches$.y || 0
        }, this.opts.chartData.calPoints || [], this.opts as any, this.config, Math.abs(this.scrollOption.currentOffset));
      } else {
        return findCurrentIndex({
          x: _touches$.x || 0,
          y: _touches$.y || 0
        }, this.opts.chartData.calPoints || [], this.opts as any, this.config, Math.abs(this.scrollOption.currentOffset));
      }
    }
    return -1;
  }

  /**
   * 获取图例数据索引
   * @param e - 触摸事件
   * @returns 图例索引
   */
  getLegendDataIndex(e: TouchEvent): number {
    let touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else if (e.mp && e.mp.changedTouches) {
      touches = e.mp.changedTouches[0];
    }
    if (touches) {
      let _touches$ = getTouches(touches, this.opts as any, e as any);
      return findLegendIndex({
        x: _touches$.x || 0,
        y: _touches$.y || 0
      }, this.opts.chartData?.legendData || [], this.opts as any);
    }
    return -1;
  }

  /**
   * 触摸图例事件
   * @param e - 触摸事件
   * @param option - 选项
   */
  touchLegend(e: TouchEvent, option: any = {}): void {
    let touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else if (e.mp && e.mp.changedTouches) {
      touches = e.mp.changedTouches[0];
    }
    if (touches) {
      let _touches$ = getTouches(touches, this.opts as any, e as any);
      let index = this.getLegendDataIndex(e);
      if (index >= 0) {
        if (!this.opts.series) return;
        if (this.opts.type == 'candle') {
          if (!this.opts.seriesMA) return;
          this.opts.seriesMA[index].show = !this.opts.seriesMA[index].show;
        } else {
          this.opts.series[index].show = !this.opts.series[index].show;
        }
        this.opts.animation = option.animation ? true : false;
        this.opts._scrollDistance_ = this.scrollOption.currentOffset;
        drawCharts.call(this, this.opts.type || '', this.opts as any, this.config, this.context);
      }
    }
  }

  /**
   * 显示提示框
   * @param e - 触摸事件
   * @param option - 提示框选项
   */
  showToolTip(e: TouchEvent, option: ToolTipOption = {}): void {
    let touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else if (e.mp && e.mp.changedTouches) {
      touches = e.mp.changedTouches[0];
    }
    if (!touches) {
      console.log("[uCharts] 未获取到event坐标信息");
      return;
    }
    let _touches$ = getTouches(touches, this.opts as any, e as any);
    let currentOffset = this.scrollOption.currentOffset;
    let opts = {
      ...this.opts,
      _scrollDistance_: currentOffset,
      animation: false
    } as ChartsConfig;
    if (this.opts.type === 'line' || this.opts.type === 'area' || this.opts.type === 'column' || this.opts.type === 'scatter' || this.opts.type === 'bubble') {
      let current = this.getCurrentDataIndex(e);
      let index = option.index == undefined ? current.index : option.index;
      if (index > -1 || index.length > 0) {
        let seriesData = getSeriesDataItem(this.opts.series || [], index, current.group);
        if (seriesData.length !== 0) {
          let _getToolTipData = getToolTipData(seriesData as any, this.opts as any, index, current.group, this.opts.categories || [], option);
          let textList = _getToolTipData.textList;
          let offset = _getToolTipData.offset;
          offset.y = _touches$.y || 0;
          (opts as any).tooltip = {
            textList: option.textList !== undefined ? option.textList : textList,
            offset: option.offset !== undefined ? option.offset : offset,
            option: option,
            index: index,
            group: current.group
          };
        }
      }
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
    if (this.opts.type === 'mount') {
      let index = option.index == undefined ? this.getCurrentDataIndex(e).index : option.index;
      if (index > -1) {
        let opts = { ...this.opts, animation: false } as ChartsConfig;
        let seriesData = { ...opts._series_?.[index] || {} };
        let textList = [{
          text: option.formatter ? option.formatter(seriesData, undefined, index, opts) : (seriesData.name || '') + ': ' + (seriesData.data || ''),
          color: seriesData.color || '',
          legendShape: this.opts.extra?.tooltip?.legendShape == 'auto' ? seriesData.legendShape : this.opts.extra?.tooltip?.legendShape
        }];
        let offset = {
          x: opts.chartData?.calPoints?.[index]?.x || 0,
          y: _touches$.y || 0
        };
        opts.tooltip = {
          textList: option.textList ? option.textList : textList,
          offset: option.offset !== undefined ? option.offset : offset,
          option: option,
          index: index
        };
      }

      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
    if (this.opts.type === 'bar') {
      let current = this.getCurrentDataIndex(e);
      let index = option.index == undefined ? current.index : option.index;
      if (index > -1 || index.length > 0) {
        let seriesData = getSeriesDataItem(this.opts.series || [], index, current.group);
        if (seriesData.length !== 0) {
          let _getToolTipData = getToolTipData(seriesData as any, this.opts as any, index, current.group, this.opts.categories || [], option);
          let textList = _getToolTipData.textList;
          let offset = _getToolTipData.offset;
          offset.x = _touches$.x || 0;
          (opts as any).tooltip = {
            textList: option.textList !== undefined ? option.textList : textList,
            offset: option.offset !== undefined ? option.offset : offset,
            option: option,
            index: index
          };
        }
      }
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
    if (this.opts.type === 'mix') {
      let current = this.getCurrentDataIndex(e);
      let index = option.index == undefined ? current.index : option.index;
      if (index > -1) {
        let currentOffset = this.scrollOption.currentOffset;
        let opts = {
          ...this.opts,
          _scrollDistance_: currentOffset,
          animation: false
        } as ChartsConfig;
        let seriesData = getSeriesDataItem(this.opts.series || [], index, []);
        if (seriesData.length !== 0) {
          let _getMixToolTipData = getMixToolTipData(seriesData as any, this.opts as any, index, this.opts.categories || [], option);
          let textList = _getMixToolTipData.textList;
          let offset = _getMixToolTipData.offset;
          offset.y = _touches$.y || 0;
          (opts as any).tooltip = {
            textList: option.textList ? option.textList : textList,
            offset: option.offset !== undefined ? option.offset : offset,
            option: option,
            index: index
          };
        }
      }
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
    if (this.opts.type === 'candle') {
      let current = this.getCurrentDataIndex(e);
      let index = option.index == undefined ? current.index : option.index;
      if (index > -1) {
        let currentOffset = this.scrollOption.currentOffset;
        let opts = {
          ...this.opts,
          _scrollDistance_: currentOffset,
          animation: false
        } as ChartsConfig;
        let seriesData = getSeriesDataItem(this.opts.series || [], index, []);
        if (seriesData.length !== 0) {
          let _getToolTipData = getCandleToolTipData(this.opts.series?.[0]?.data || [], seriesData, this.opts as any, index, this.opts.categories || [], this.opts.extra?.candle, option);
          let textList = _getToolTipData.textList;
          let offset = _getToolTipData.offset;
          offset.y = _touches$.y || 0;
          (opts as any).tooltip = {
            textList: option.textList ? option.textList : textList,
            offset: option.offset !== undefined ? option.offset : offset,
            option: option,
            index: index
          };
        }
      }
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
    if (this.opts.type === 'pie' || this.opts.type === 'ring' || this.opts.type === 'rose' || this.opts.type === 'funnel') {
      let index = option.index == undefined ? this.getCurrentDataIndex(e) : option.index;
      if (index > -1) {
        let opts = { ...this.opts, animation: false } as ChartsConfig;
        let seriesData = { ...opts._series_?.[index] || {} };
        let textList = [{
          text: option.formatter ? option.formatter(seriesData, undefined, index, opts) : (seriesData.name || '') + ': ' + (seriesData.data || ''),
          color: seriesData.color || '',
          legendShape: this.opts.extra?.tooltip?.legendShape == 'auto' ? seriesData.legendShape : this.opts.extra?.tooltip?.legendShape
        }];
        let offset = {
          x: _touches$.x,
          y: _touches$.y
        };
        opts.tooltip = {
          textList: option.textList ? option.textList : textList,
          offset: option.offset !== undefined ? option.offset : offset,
          option: option,
          index: index
        };
      }
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
    if (this.opts.type === 'map') {
      let index = option.index == undefined ? this.getCurrentDataIndex(e) : option.index;
      if (index > -1) {
        let opts = { ...this.opts, animation: false } as ChartsConfig;
        if (!this.opts.series) return;
        let seriesData = { ...this.opts.series[index] };
        seriesData.name = (seriesData as any).properties?.name || seriesData.name || '';
        let textList = [{
          text: option.formatter ? option.formatter(seriesData, undefined, index, this.opts) : (seriesData.name || ''),
          color: seriesData.color || '',
          legendShape: this.opts.extra?.tooltip?.legendShape == 'auto' ? seriesData.legendShape : this.opts.extra?.tooltip?.legendShape
        }];
        let offset = {
          x: _touches$.x || 0,
          y: _touches$.y || 0
        };
        (opts as any).tooltip = {
          textList: option.textList ? option.textList : textList,
          offset: option.offset !== undefined ? option.offset : offset,
          option: option,
          index: index
        };
      }
      opts.updateData = false;
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
    if (this.opts.type === 'word') {
      let index = option.index == undefined ? this.getCurrentDataIndex(e) : option.index;
      if (index > -1) {
        let opts = { ...this.opts, animation: false } as ChartsConfig;
        if (!this.opts.series) return;
        let seriesData = { ...this.opts.series[index] };
        let textList = [{
          text: option.formatter ? option.formatter(seriesData, undefined, index, this.opts) : (seriesData.name || ''),
          color: seriesData.color || '',
          legendShape: this.opts.extra?.tooltip?.legendShape == 'auto' ? seriesData.legendShape : this.opts.extra?.tooltip?.legendShape
        }];
        let offset = {
          x: _touches$.x || 0,
          y: _touches$.y || 0
        };
        (opts as any).tooltip = {
          textList: option.textList ? option.textList : textList,
          offset: option.offset !== undefined ? option.offset : offset,
          option: option,
          index: index
        };
      }
      opts.updateData = false;
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
    if (this.opts.type === 'radar') {
      let index = option.index == undefined ? this.getCurrentDataIndex(e) : option.index;
      if (index > -1) {
        let opts = { ...this.opts, animation: false } as ChartsConfig;
        let seriesData = getSeriesDataItem(this.opts.series || [], index, []);
        if (seriesData.length !== 0) {
          let textList = seriesData.map((item) => {
            return {
              text: option.formatter ? option.formatter(item, this.opts.categories?.[index], index, this.opts) : (item.name || '') + ': ' + (item.data || ''),
              color: item.color || '',
              legendShape: this.opts.extra?.tooltip?.legendShape == 'auto' ? item.legendShape : this.opts.extra?.tooltip?.legendShape
            };
          });
          let offset = {
            x: _touches$.x || 0,
            y: _touches$.y || 0
          };
          (opts as any).tooltip = {
            textList: option.textList ? option.textList : textList,
            offset: option.offset !== undefined ? option.offset : offset,
            option: option,
            index: index
          };
        }
      }
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
    }
  }

  /**
   * 平移图表
   * @param distance - 平移距离
   */
  translate(distance: number): void {
    this.scrollOption = {
      currentOffset: distance,
      startTouchX: distance,
      distance: 0,
      lastMoveTime: 0
    };
    let opts = {
      ...this.opts,
      _scrollDistance_: distance,
      animation: false
    } as ChartsConfig;
    drawCharts.call(this, this.opts.type || '', opts as any, this.config, this.context);
  }

  /**
   * 滚动开始事件
   * @param e - 触摸事件
   */
  scrollStart(e: TouchEvent): void {
    let touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else if (e.mp && e.mp.changedTouches) {
      touches = e.mp.changedTouches[0];
    }
    if (touches && this.opts.enableScroll === true) {
      let _touches$ = getTouches(touches, this.opts as any, e as any);
      this.scrollOption.startTouchX = _touches$.x || 0;
    }
  }

  /**
   * 滚动事件
   * @param e - 触摸事件
   * @returns 当前偏移量
   */
  scroll(e: TouchEvent): number {
    if (this.scrollOption.lastMoveTime === 0) {
      this.scrollOption.lastMoveTime = Date.now();
    }
    let Limit = this.opts.touchMoveLimit || 60;
    let currMoveTime = Date.now();
    let duration = currMoveTime - this.scrollOption.lastMoveTime;
    if (duration < Math.floor(1000 / Limit)) return 0;
    if (this.scrollOption.startTouchX == 0) return 0;
    this.scrollOption.lastMoveTime = currMoveTime;
    let touches = null;
    if (e.changedTouches) {
      touches = e.changedTouches[0];
    } else if (e.mp && e.mp.changedTouches) {
      touches = e.mp.changedTouches[0];
    }
    if (touches && this.opts.enableScroll === true) {
      let _touches$ = getTouches(touches, this.opts as any, e as any);
      let _distance;
      _distance = (_touches$.x || 0) - this.scrollOption.startTouchX;
      let currentOffset = this.scrollOption.currentOffset;
      const self = this as unknown as CoordinateUChartInstance;
      self.scrollOption = { position: this.scrollOption.currentOffset };
      let validDistance = calValidDistance(self, currentOffset + _distance, this.opts.chartData || { eachSpacing: 0 }, this.config as any, this.opts as any);
      this.scrollOption.distance = _distance = validDistance - currentOffset;
      let opts = {
        ...this.opts,
        _scrollDistance_: currentOffset + _distance,
        animation: false
      } as ChartsConfig;
      this.opts = opts;
      drawCharts.call(this, opts.type || '', opts as any, this.config, this.context);
      return currentOffset + _distance;
    }
    return 0;
  }

  /**
   * 滚动结束事件
   * @param e - 触摸事件
   */
  scrollEnd(e: TouchEvent): void {
    if (this.opts.enableScroll === true) {
      let _scrollOption = this.scrollOption;
      let currentOffset = _scrollOption.currentOffset;
      let distance = _scrollOption.distance;
      this.scrollOption.currentOffset = currentOffset + distance;
      this.scrollOption.distance = 0;
      this.scrollOption.moveCount = 0;
    }
  }
}

export default uCharts;
