/**
 * 地图绘制函数
 *
 * 从 u-charts 核心库搬迁的地图绘制相关函数
 */

// @ts-nocheck - 由于从 u-charts 搬迁，类型系统不兼容，暂时禁用类型检查
import type { ChartOptions, UChartsConfig, SeriesItem } from '../data-processing/series-calculator';
import { measureText } from '../utils/text';
import { hexToRgb } from '../utils/color';
import { assign } from '../config';

export type CanvasContext = any;

export interface Point {
  x: number;
  y: number;
}

export interface MapOption {
  border: boolean;
  mercator: boolean;
  borderWidth: number;
  active: boolean;
  borderColor: string;
  fillOpacity: number;
  activeBorderColor: string;
  activeFillColor: string;
  activeFillOpacity: number;
  activeTextColor?: string;
}

export interface Bounds {
  xMin: number;
  xMax: number;
  yMin: number;
  yMax: number;
}

export interface MapSeriesItem extends SeriesItem {
  geometry: {
    coordinates: any[];
  };
  properties: {
    name?: string;
    centroid?: number[];
  };
  color?: string;
  fillOpacity?: number;
  textSize?: number;
  textColor?: string;
}

// 经纬度转墨卡托
function lonlat2mercator(longitude: number, latitude: number): number[] {
  let mercator = [0, 0];
  let x = longitude * 20037508.34 / 180;
  let y = Math.log(Math.tan((90 + latitude) * Math.PI / 360)) / (Math.PI / 180);
  y = y * 20037508.34 / 180;
  mercator[0] = x;
  mercator[1] = y;
  return mercator;
}

function getBoundingBox(data: MapSeriesItem[]): Bounds {
  let bounds: Bounds = {
    xMin: 180,
    xMax: 0,
    yMin: 90,
    yMax: 0
  };
  let coords: any[];

  for (let i = 0; i < data.length; i++) {
    let coorda = data[i].geometry.coordinates;
    for (let k = 0; k < coorda.length; k++) {
      coords = coorda[k];
      if (coords.length == 1) {
        coords = coords[0];
      }
      for (let j = 0; j < coords.length; j++) {
        let longitude = coords[j][0];
        let latitude = coords[j][1];
        bounds.xMin = bounds.xMin < longitude ? bounds.xMin : longitude;
        bounds.xMax = bounds.xMax > longitude ? bounds.xMax : longitude;
        bounds.yMin = bounds.yMin < latitude ? bounds.yMin : latitude;
        bounds.yMax = bounds.yMax > latitude ? bounds.yMax : latitude;
      }
    }
  }
  return bounds;
}

function coordinateToPoint(latitude: number, longitude: number, bounds: Bounds, scale: number, xoffset: number, yoffset: number): Point {
  return {
    x: (longitude - bounds.xMin) * scale + xoffset,
    y: (bounds.yMax - latitude) * scale + yoffset
  };
}

export function drawMapDataPoints(
  series: MapSeriesItem[],
  opts: ChartOptions,
  config: UChartsConfig,
  context: CanvasContext
): void {
  let mapOption = assign({}, {
    border: true,
    mercator: false,
    borderWidth: 1,
    active: true,
    borderColor: '#666666',
    fillOpacity: 0.6,
    activeBorderColor: '#f04864',
    activeFillColor: '#facc14',
    activeFillOpacity: 1
  }, opts.extra.map) as MapOption;

  let data = series;
  let bounds = getBoundingBox(data);

  if (mapOption.mercator) {
    let max = lonlat2mercator(bounds.xMax, bounds.yMax);
    let min = lonlat2mercator(bounds.xMin, bounds.yMin);
    bounds.xMax = max[0];
    bounds.yMax = max[1];
    bounds.xMin = min[0];
    bounds.yMin = min[1];
  }

  let xScale = opts.width / Math.abs(bounds.xMax - bounds.xMin);
  let yScale = opts.height / Math.abs(bounds.yMax - bounds.yMin);
  let scale = xScale < yScale ? xScale : yScale;
  let xoffset = opts.width / 2 - Math.abs(bounds.xMax - bounds.xMin) / 2 * scale;
  let yoffset = opts.height / 2 - Math.abs(bounds.yMax - bounds.yMin) / 2 * scale;

  for (let i = 0; i < data.length; i++) {
    context.beginPath();
    context.setLineWidth(mapOption.borderWidth * opts.pix);
    context.setStrokeStyle(mapOption.borderColor);
    context.setFillStyle(hexToRgb(series[i].color!, series[i].fillOpacity || mapOption.fillOpacity));

    if (mapOption.active == true && opts.tooltip) {
      if (opts.tooltip.index == i) {
        context.setStrokeStyle(mapOption.activeBorderColor);
        context.setFillStyle(hexToRgb(mapOption.activeFillColor, mapOption.activeFillOpacity));
      }
    }

    let coorda = data[i].geometry.coordinates;
    for (let k = 0; k < coorda.length; k++) {
      let coords = coorda[k];
      if (coords.length == 1) {
        coords = coords[0];
      }
      for (let j = 0; j < coords.length; j++) {
        let gaosi: number[];
        if (mapOption.mercator) {
          gaosi = lonlat2mercator(coords[j][0], coords[j][1]);
        } else {
          gaosi = coords[j];
        }
        let point = coordinateToPoint(gaosi[1], gaosi[0], bounds, scale, xoffset, yoffset);
        if (j === 0) {
          context.beginPath();
          context.moveTo(point.x, point.y);
        } else {
          context.lineTo(point.x, point.y);
        }
      }
      context.fill();
      if (mapOption.border == true) {
        context.stroke();
      }
    }
  }

  if (opts.dataLabel == true) {
    for (let i = 0; i < data.length; i++) {
      let centerPoint = data[i].properties.centroid;
      if (centerPoint) {
        let gaosi: number[];
        if (mapOption.mercator) {
          gaosi = lonlat2mercator(data[i].properties.centroid![0], data[i].properties.centroid![1]);
        } else {
          gaosi = data[i].properties.centroid!;
        }
        let point = coordinateToPoint(gaosi[1], gaosi[0], bounds, scale, xoffset, yoffset);
        let fontSize = data[i].textSize! * opts.pix || config.fontSize;
        let fontColor = data[i].textColor || opts.fontColor;
        if (mapOption.active && mapOption.activeTextColor && opts.tooltip && opts.tooltip.index == i) {
          fontColor = mapOption.activeTextColor;
        }
        let text = data[i].properties.name || '';
        context.beginPath();
        context.setFontSize(fontSize);
        context.setFillStyle(fontColor);
        context.fillText(text, point.x - measureText(text, fontSize, context) / 2, point.y + fontSize / 2);
        context.closePath();
        context.stroke();
      }
    }
  }

  opts.chartData.mapData = {
    bounds: bounds,
    scale: scale,
    xoffset: xoffset,
    yoffset: yoffset,
    mercator: mapOption.mercator
  };
}
