/**
 * 特殊图表绘制函数
 *
 * 从 u-charts 核心库搬迁的特殊图表绘制相关函数
 * 包括散点图、气泡图、混合图、词云、漏斗图等
 */
// @ts-nocheck - 由于从 u-charts 搬迁，类型系统不兼容，暂时禁用类型检查

// 动画进度全局变量
declare const process: number;

import type { ChartOptions, UChartsConfig, SeriesItem } from '../data-processing/series-calculator';
import { getDataPoints } from '../charts-data/basic-charts';
import { getFunnelDataPoints } from '../charts-data/funnel-charts';
import { measureText } from '../utils/text';
import { hexToRgb } from '../utils/color';
import { splitPoints, createCurveControlPoints } from '../utils/misc';
import { assign } from '../config';

export type CanvasContext = any;

export interface Point {
  x: number;
  y: number;
  r?: number;
  t?: string;
}

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

function drawPointText(points: Point[], series: SeriesItem, config: UChartsConfig, context: CanvasContext, opts: ChartOptions): void {
  context.beginPath();
  context.setFontSize(series.textSize * opts.pix || config.fontSize);
  context.setFillStyle(series.textColor || opts.fontColor);
  context.setTextAlign('center');
  points.forEach(function(item, index) {
    if (item !== null) {
      context.fillText(String(item.y || item.value || ''), item.x, item.y - 4);
    }
  });
  context.closePath();
  context.stroke();
}

export function drawScatterDataPoints(
  series: SeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  context: CanvasContext
): { xAxisPoints: number[]; calPoints: any[]; eachSpacing: number } {
  let scatterOption = assign({}, {
    type: 'circle'
  }, opts.extra.scatter);

  let xAxisData = opts.chartData.xAxisData;
  let xAxisPoints = xAxisData.xAxisPoints;
  let eachSpacing = xAxisData.eachSpacing;
  let calPoints: any[] = [];

  context.save();
  let leftSpace = 0;
  let rightSpace = opts.width + eachSpacing;
  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
    context.translate(opts._scrollDistance_, 0);
    leftSpace = -opts._scrollDistance_ - eachSpacing * 2 + opts.area[3];
    rightSpace = leftSpace + (opts.xAxis.itemCount + 4) * eachSpacing;
  }

  series.forEach(function(eachSeries, seriesIndex) {
    let ranges, minRange, maxRange;
    ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
    minRange = ranges.pop();
    maxRange = ranges.shift();
    let data = eachSeries.data;
    let points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);

    context.beginPath();
    context.setStrokeStyle(eachSeries.color);
    context.setFillStyle(eachSeries.color);
    context.setLineWidth(1 * opts.pix);
    let shape = eachSeries.pointShape;

    if (shape === 'diamond') {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x, item.y - 4.5);
          context.lineTo(item.x - 4.5, item.y);
          context.lineTo(item.x, item.y + 4.5);
          context.lineTo(item.x + 4.5, item.y);
          context.lineTo(item.x, item.y - 4.5);
        }
      });
    } else if (shape === 'circle') {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x + 2.5 * opts.pix, item.y);
          context.arc(item.x, item.y, 3 * opts.pix, 0, 2 * Math.PI, false);
        }
      });
    } else if (shape === 'square') {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x - 3.5, item.y - 3.5);
          context.rect(item.x - 3.5, item.y - 3.5, 7, 7);
        }
      });
    } else if (shape === 'triangle') {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x, item.y - 4.5);
          context.lineTo(item.x - 4.5, item.y + 4.5);
          context.lineTo(item.x + 4.5, item.y + 4.5);
          context.lineTo(item.x, item.y - 4.5);
        }
      });
    } else {
      points.forEach(function(item, index) {
        if (item !== null) {
          context.moveTo(item.x + 2.5 * opts.pix, item.y);
          context.arc(item.x, item.y, 3 * opts.pix, 0, 2 * Math.PI, false);
        }
      });
    }
    context.closePath();
    context.fill();
    context.stroke();
  });

  if (opts.dataLabel !== false && process === 1) {
    series.forEach(function(eachSeries, seriesIndex) {
      let ranges, minRange, maxRange;
      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
      minRange = ranges.pop();
      maxRange = ranges.shift();
      let data = eachSeries.data;
      let points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
      drawPointText(points, eachSeries, config, context, opts);
    });
  }

  context.restore();

  return {
    xAxisPoints: xAxisPoints,
    calPoints: calPoints,
    eachSpacing: eachSpacing
  };
}

export function drawBubbleDataPoints(
  series: SeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  context: CanvasContext
): { xAxisPoints: number[]; calPoints: any[]; eachSpacing: number } {
  let bubbleOption = assign({}, {
    opacity: 1,
    border: 2
  }, opts.extra.bubble);

  let xAxisData = opts.chartData.xAxisData;
  let xAxisPoints = xAxisData.xAxisPoints;
  let eachSpacing = xAxisData.eachSpacing;
  let calPoints: any[] = [];

  context.save();
  let leftSpace = 0;
  let rightSpace = opts.width + eachSpacing;
  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
    context.translate(opts._scrollDistance_, 0);
    leftSpace = -opts._scrollDistance_ - eachSpacing * 2 + opts.area[3];
    rightSpace = leftSpace + (opts.xAxis.itemCount + 4) * eachSpacing;
  }

  series.forEach(function(eachSeries, seriesIndex) {
    let ranges, minRange, maxRange;
    ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
    minRange = ranges.pop();
    maxRange = ranges.shift();
    let data = eachSeries.data;
    let points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);

    context.beginPath();
    context.setStrokeStyle(eachSeries.color);
    context.setLineWidth(bubbleOption.border * opts.pix);
    context.setFillStyle(hexToRgb(eachSeries.color!, bubbleOption.opacity));

    points.forEach(function(item: any, index) {
      context.moveTo(item.x + item.r, item.y);
      context.arc(item.x, item.y, item.r * opts.pix, 0, 2 * Math.PI, false);
    });

    context.closePath();
    context.fill();
    context.stroke();

    if (opts.dataLabel !== false && process === 1) {
      points.forEach(function(item: any, index) {
        context.beginPath();
        let fontSize = eachSeries.textSize * opts.pix || config.fontSize;
        context.setFontSize(fontSize);
        context.setFillStyle(eachSeries.textColor || "#FFFFFF");
        context.setTextAlign('center');
        context.fillText(String(item.t), item.x, item.y + fontSize / 2);
        context.closePath();
        context.stroke();
        context.setTextAlign('left');
      });
    }
  });

  context.restore();

  return {
    xAxisPoints: xAxisPoints,
    calPoints: calPoints,
    eachSpacing: eachSpacing
  };
}

export function drawMixDataPoints(
  series: SeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  context: CanvasContext
): { xAxisPoints: number[]; calPoints: any[]; eachSpacing: number } {
  let xAxisData = opts.chartData.xAxisData;
  let xAxisPoints = xAxisData.xAxisPoints;
  let eachSpacing = xAxisData.eachSpacing;

  let columnOption = assign({}, {
    width: eachSpacing / 2,
    barBorderCircle: false,
    barBorderRadius: [],
    seriesGap: 2,
    linearType: 'none',
    linearOpacity: 1,
    customColor: [],
    colorStop: 0,
  }, opts.extra.mix.column);

  let areaOption = assign({}, {
    opacity: 0.2,
    gradient: false
  }, opts.extra.mix.area);

  let lineOption = assign({}, {
    width: 2
  }, opts.extra.mix.line);

  let endY = opts.height - opts.area[2];
  let calPoints: any[] = [];
  let columnIndex = 0;
  let columnLength = 0;

  series.forEach(function(eachSeries, seriesIndex) {
    if (eachSeries.type == 'column') {
      columnLength += 1;
    }
  });

  context.save();
  let leftNum = -2;
  let rightNum = xAxisPoints.length + 2;
  let leftSpace = 0;
  let rightSpace = opts.width + eachSpacing;

  if (opts._scrollDistance_ && opts._scrollDistance_ !== 0 && opts.enableScroll === true) {
    context.translate(opts._scrollDistance_, 0);
    leftNum = Math.floor(-opts._scrollDistance_ / eachSpacing) - 2;
    rightNum = leftNum + opts.xAxis.itemCount + 4;
    leftSpace = -opts._scrollDistance_ - eachSpacing * 2 + opts.area[3];
    rightSpace = leftSpace + (opts.xAxis.itemCount + 4) * eachSpacing;
  }

  // fillCustomColor implementation would be here
  columnOption.customColor = columnOption.customColor || [];

  series.forEach(function(eachSeries, seriesIndex) {
    let ranges, minRange, maxRange;
    ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
    minRange = ranges.pop();
    maxRange = ranges.shift();
    let data = eachSeries.data;
    let points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
    calPoints.push(points);

    // 绘制柱状数据图
    if (eachSeries.type == 'column') {
      // Simplified column drawing
      for (let i = 0; i < points.length; i++) {
        let item = points[i];
        if (item !== null && i > leftNum && i < rightNum) {
          let startX = item.x - eachSpacing / columnLength / 2;
          let itemWidth = eachSpacing / columnLength - columnOption.seriesGap;
          context.beginPath();
          context.setFillStyle(eachSeries.color);
          context.rect(startX, item.y, itemWidth, opts.height - opts.area[2] - item.y);
          context.closePath();
          context.fill();
        }
      }
      columnIndex += 1;
    }

    // 绘制区域图数据
    if (eachSeries.type == 'area') {
      let splitPointList = splitPoints(points, eachSeries);
      for (let i = 0; i < splitPointList.length; i++) {
        let points = splitPointList[i];
        context.beginPath();
        context.setStrokeStyle(eachSeries.color);
        context.setFillStyle(hexToRgb(eachSeries.color!, areaOption.opacity));
        context.setLineWidth(2 * opts.pix);

        if (points.length > 1) {
          let firstPoint = points[0];
          let lastPoint = points[points.length - 1];
          context.moveTo(firstPoint.x, firstPoint.y);
          let startPoint = 0;

          if (eachSeries.style === 'curve') {
            for (let j = 0; j < points.length; j++) {
              let item = points[j];
              if (startPoint == 0 && item.x > leftSpace) {
                context.moveTo(item.x, item.y);
                startPoint = 1;
              }
              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                let ctrlPoint = createCurveControlPoints(points, j - 1);
                context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y);
              }
            }
          } else {
            for (let j = 0; j < points.length; j++) {
              let item = points[j];
              if (startPoint == 0 && item.x > leftSpace) {
                context.moveTo(item.x, item.y);
                startPoint = 1;
              }
              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                context.lineTo(item.x, item.y);
              }
            }
          }
          context.lineTo(lastPoint.x, endY);
          context.lineTo(firstPoint.x, endY);
          context.lineTo(firstPoint.x, firstPoint.y);
        }
        context.closePath();
        context.fill();
      }
    }

    // 绘制折线数据图
    if (eachSeries.type == 'line') {
      let splitPointList = splitPoints(points, eachSeries);
      splitPointList.forEach(function(points, index) {
        if (eachSeries.lineType == 'dash') {
          let dashLength = eachSeries.dashLength ? eachSeries.dashLength : 8;
          dashLength *= opts.pix;
          context.setLineDash([dashLength, dashLength]);
        }
        context.beginPath();
        context.setStrokeStyle(eachSeries.color);
        context.setLineWidth(lineOption.width * opts.pix);

        if (points.length === 1) {
          context.moveTo(points[0].x, points[0].y);
        } else {
          context.moveTo(points[0].x, points[0].y);
          let startPoint = 0;

          if (eachSeries.style == 'curve') {
            for (let j = 0; j < points.length; j++) {
              let item = points[j];
              if (startPoint == 0 && item.x > leftSpace) {
                context.moveTo(item.x, item.y);
                startPoint = 1;
              }
              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                let ctrlPoint = createCurveControlPoints(points, j - 1);
                context.bezierCurveTo(ctrlPoint.ctrA.x, ctrlPoint.ctrA.y, ctrlPoint.ctrB.x, ctrlPoint.ctrB.y, item.x, item.y);
              }
            }
          } else {
            for (let j = 0; j < points.length; j++) {
              let item = points[j];
              if (startPoint == 0 && item.x > leftSpace) {
                context.moveTo(item.x, item.y);
                startPoint = 1;
              }
              if (j > 0 && item.x > leftSpace && item.x < rightSpace) {
                context.lineTo(item.x, item.y);
              }
            }
          }
        }
        context.stroke();
        context.setLineDash([]);
      });
    }

    // 绘制点数据图
    if (eachSeries.type == 'point') {
      eachSeries.addPoint = true;
    }
  });

  if (opts.dataLabel !== false && process === 1) {
    series.forEach(function(eachSeries, seriesIndex) {
      let ranges, minRange, maxRange;
      ranges = [].concat(opts.chartData.yAxisData.ranges[eachSeries.index]);
      minRange = ranges.pop();
      maxRange = ranges.shift();
      let data = eachSeries.data;
      let points = getDataPoints(data, minRange, maxRange, xAxisPoints, eachSpacing, opts, config, process);
      if (eachSeries.type !== 'column') {
        drawPointText(points, eachSeries, config, context, opts);
      }
    });
  }

  context.restore();

  return {
    xAxisPoints: xAxisPoints,
    calPoints: calPoints,
    eachSpacing: eachSpacing
  };
}

export function drawWordCloudDataPoints(
  series: SeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  context: CanvasContext
): void {
  let wordOption = assign({}, {
    type: 'normal',
    autoColors: true
  }, opts.extra.word);

  if (!opts.chartData.wordCloudData) {
    opts.chartData.wordCloudData = getWordCloudPoint(opts, wordOption.type, context);
  }

  context.beginPath();
  context.setFillStyle(opts.background);
  context.rect(0, 0, opts.width, opts.height);
  context.fill();

  context.save();
  let points = opts.chartData.wordCloudData;
  context.translate(opts.width / 2, opts.height / 2);

  for (let i = 0; i < points.length; i++) {
    context.save();
    if ((points[i] as any).rotate) {
      context.rotate(90 * Math.PI / 180);
    }
    let text = (points[i] as any).name;
    let tHeight = (points[i] as any).textSize * opts.pix;
    let tWidth = measureText(text, tHeight, context);

    context.beginPath();
    context.setStrokeStyle((points[i] as any).color);
    context.setFillStyle((points[i] as any).color);
    context.setFontSize(tHeight);

    if ((points[i] as any).rotate) {
      if ((points[i] as any).areav[0] > 0) {
        if (opts.tooltip) {
          if (opts.tooltip.index == i) {
            context.strokeText(text, ((points[i] as any).areav[0] + 5 - opts.width / 2) * process - tWidth * (1 - process) / 2, ((points[i] as any).areav[1] + 5 + tHeight - opts.height / 2) * process);
          } else {
            context.fillText(text, ((points[i] as any).areav[0] + 5 - opts.width / 2) * process - tWidth * (1 - process) / 2, ((points[i] as any).areav[1] + 5 + tHeight - opts.height / 2) * process);
          }
        } else {
          context.fillText(text, ((points[i] as any).areav[0] + 5 - opts.width / 2) * process - tWidth * (1 - process) / 2, ((points[i] as any).areav[1] + 5 + tHeight - opts.height / 2) * process);
        }
      }
    } else {
      if ((points[i] as any).area[0] > 0) {
        if (opts.tooltip) {
          if (opts.tooltip.index == i) {
            context.strokeText(text, ((points[i] as any).area[0] + 5 - opts.width / 2) * process - tWidth * (1 - process) / 2, ((points[i] as any).area[1] + 5 + tHeight - opts.height / 2) * process);
          } else {
            context.fillText(text, ((points[i] as any).area[0] + 5 - opts.width / 2) * process - tWidth * (1 - process) / 2, ((points[i] as any).area[1] + 5 + tHeight - opts.height / 2) * process);
          }
        } else {
          context.fillText(text, ((points[i] as any).area[0] + 5 - opts.width / 2) * process - tWidth * (1 - process) / 2, ((points[i] as any).area[1] + 5 + tHeight - opts.height / 2) * process);
        }
      }
    }
    context.stroke();
    context.restore();
  }

  context.restore();
}

function getWordCloudPoint(opts: ChartOptions, type: string, context: CanvasContext): any[] {
  let points = opts.series;
  // Simplified implementation
  return points.map((p: any) => ({
    ...p,
    area: [p.x || 0, p.y || 0, (p.x || 0) + 100, (p.y || 0) + 20],
    rotate: false
  }));
}

export interface FunnelSeriesItem extends SeriesItem {
  _proportion_?: number;
  radius?: number;
  funnelArea?: [number, number, number, number];
  labelShow?: boolean;
  labelText?: string;
  formatter?: (item: any, index: number, series: any[], opts: any) => string;
  textColor?: string;
  textSize?: number;
  centerText?: string;
  centerTextColor?: string;
  centerTextSize?: number;
  linearIndex?: number;
}

export function drawFunnelDataPoints(
  series: FunnelSeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  context: CanvasContext
): { center: any; radius: number; series: FunnelSeriesItem[] } {
  let funnelOption = assign({}, {
    type: 'funnel',
    activeWidth: 10,
    activeOpacity: 0.3,
    border: false,
    borderWidth: 2,
    borderColor: '#FFFFFF',
    fillOpacity: 1,
    minSize: 0,
    labelAlign: 'right',
    linearType: 'none',
    customColor: [],
  }, opts.extra.funnel);

  let eachSpacing = (opts.height - opts.area[0] - opts.area[2]) / series.length;
  let centerPosition = {
    x: opts.area[3] + (opts.width - opts.area[1] - opts.area[3]) / 2,
    y: opts.height - opts.area[2]
  };
  let activeWidth = funnelOption.activeWidth * opts.pix;
  let radius = Math.min((opts.width - opts.area[1] - opts.area[3]) / 2 - activeWidth, (opts.height - opts.area[0] - opts.area[2]) / 2 - activeWidth);

  let seriesNew = getFunnelDataPoints(series, radius, funnelOption, eachSpacing);

  context.save();
  context.translate(centerPosition.x, centerPosition.y);

  // fillCustomColor implementation would be here
  funnelOption.customColor = funnelOption.customColor || [];

  if (funnelOption.type == 'pyramid') {
    // Pyramid drawing
    for (let i = 0; i < seriesNew.length; i++) {
      // Simplified implementation
      context.beginPath();
      context.setFillStyle(seriesNew[i].color);
      context.arc(0, 0, seriesNew[i].radius || 0, 0, 2 * Math.PI);
      context.fill();
      context.translate(0, eachSpacing);
    }
  } else {
    // Funnel drawing
    context.translate(0, -(seriesNew.length - 1) * eachSpacing);
    for (let i = 0; i < seriesNew.length; i++) {
      // Simplified implementation
      context.beginPath();
      context.setFillStyle(seriesNew[i].color);
      context.arc(0, 0, seriesNew[i].radius || 0, 0, 2 * Math.PI);
      context.fill();
      context.translate(0, eachSpacing);
    }
  }

  context.restore();

  if (opts.dataLabel !== false && process === 1) {
    drawFunnelText(seriesNew, opts, context, eachSpacing, funnelOption.labelAlign, activeWidth, centerPosition);
  }

  if (process === 1) {
    drawFunnelCenterText(seriesNew, opts, context, eachSpacing, funnelOption.labelAlign, activeWidth, centerPosition);
  }

  return {
    center: centerPosition,
    radius: radius,
    series: seriesNew
  };
}

export function drawFunnelText(
  series: FunnelSeriesItem[],
  opts: ChartOptions,
  context: CanvasContext,
  eachSpacing: number,
  labelAlign: string,
  activeWidth: number,
  centerPosition: any
): void {
  for (let i = 0; i < series.length; i++) {
    let item = series[i];
    if (item.labelShow === false) {
      continue;
    }
    let startX, endX, startY, fontSize;
    let text = item.formatter ? item.formatter(item, i, series, opts) : util.toFixed((item._proportion_ || 0) * 100, 2) + '%';
    text = item.labelText ? item.labelText : text;

    if (labelAlign == 'right') {
      if (i == series.length - 1) {
        startX = (item.funnelArea![2] + centerPosition.x) / 2;
      } else {
        startX = (item.funnelArea![2] + series[i + 1].funnelArea![2]) / 2;
      }
      endX = startX + activeWidth * 2;
      startY = item.funnelArea![1] + eachSpacing / 2;
      fontSize = item.textSize! * opts.pix || opts.fontSize! * opts.pix;

      context.setLineWidth(1 * opts.pix);
      context.setStrokeStyle(item.color);
      context.setFillStyle(item.color);
      context.beginPath();
      context.moveTo(startX, startY);
      context.lineTo(endX, startY);
      context.stroke();
      context.closePath();

      context.beginPath();
      context.moveTo(endX, startY);
      context.arc(endX, startY, 2 * opts.pix, 0, 2 * Math.PI);
      context.closePath();
      context.fill();

      context.beginPath();
      context.setFontSize(fontSize);
      context.setFillStyle(item.textColor || opts.fontColor);
      context.fillText(text, endX + 5, startY + fontSize / 2 - 2);
      context.closePath();
      context.stroke();
    }
  }
}

export function drawFunnelCenterText(
  series: FunnelSeriesItem[],
  opts: ChartOptions,
  context: CanvasContext,
  eachSpacing: number,
  labelAlign: string,
  activeWidth: number,
  centerPosition: any
): void {
  for (let i = 0; i < series.length; i++) {
    let item = series[i];
    let startY, fontSize;
    if (item.centerText) {
      startY = item.funnelArea![1] + eachSpacing / 2;
      fontSize = (item.centerTextSize || opts.fontSize) * opts.pix;
      context.beginPath();
      context.setFontSize(fontSize);
      context.setFillStyle(item.centerTextColor || "#FFFFFF");
      context.fillText(item.centerText, centerPosition.x - measureText(item.centerText, fontSize, context) / 2, startY + fontSize / 2 - 2);
      context.closePath();
      context.stroke();
    }
  }
}
