import U from 'mapbox-gl-utils';
import * as turf from '@turf/turf';

type EventType = 'Point' | 'LineString' | 'Polygon';
interface GenerateGeoJSONDataInterface {
  lng: number;
  lat: number;
}

interface FeatureCollection {
  type: string;
  features: Array<any>;
}

const CIRCLE_STYLE = {
  'circle-color': '#6495ED',
  'circle-radius': 8,
  'circle-stroke-width': 2,
  'circle-stroke-color': '#ffffff',
};
const LINE_STYLE = {
  'line-color': '#6495ED',
  'line-width': 3,
};
const LINE_IS_DRAW_STYLE = {
  'line-dasharray': [2, 2],
  'line-color': '#00FA9A',
  'line-width': 3,
};
const POLYGON_STYLE = {
  'fill-color': '#FAFAD2',
  'fill-opacity': 0.1,
};

function typeOf(obj: any): any {
  const toString: any = Object.prototype.toString;
  const map: any = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
  };
  return map[toString.call(obj)];
}

function deepClone(data: any): any {
  // 获取传入拷贝函数的数据类型
  const type = typeOf(data);
  // 定义一个返回any类型的数据
  let reData: any;
  // 递归遍历一个array类型数据，
  if (type === 'array') {
    reData = [];
    for (let i = 0; i < data.length; i++) {
      reData.push(deepClone(data[i]));
    }
  } else if (type === 'object') {
    //递归遍历一个object类型数据
    reData = {};
    for (const i in data) {
      reData[i] = deepClone(data[i]);
    }
  } else {
    // 返回基本数据类型
    return data;
  }
  // 将any类型的数据return出去，作为deepClone的结果
  return reData;
}

class BaseMP {
  map: any;
  listeners: {};
  constructor(map: any) {
    this.map = map;
    // 初始化mapbox工具类
    U.init(this.map);
    this.listeners = {};
  }

  //监听
  on(event: EventType, callback: void) {
    console.log('on', event);
    this.listeners[event] = callback;
  }

  off(event: EventType) {
    console.log('off', event);
    if (this.listeners[event]) {
      delete this.listeners[event];
    }
  }

  emit(event: EventType, data: any) {
    if (this.listeners[event]) {
      this.listeners[event](data);
    }
  }

  //防抖函数
  debounce = (func: Function, delay: number) => {
    let timer: any = null;
    return (...args: any) => {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        func.apply(this, args);
      }, delay);
    };
  };

  //切换鼠标样式
  _changeMouseCursor = (cursor: string = 'pointer') => {
    this.map.getCanvas().style.cursor = cursor;
  };

  //生成geoJson
  _generateGeoJSON(data: GenerateGeoJSONDataInterface[], geometryType: EventType) {
    /**
     * // 生成点类型的GeoJSON
     * var data = [{lng: -122.4194, lat: 37.7749}, {lng: -122.408, lat: 37.791}, {lng: -122.431, lat: 37.769}];
     * var geoJSON = generateGeoJSON(data, "Point");
     *
     * // 生成线类型的GeoJSON
     * var data = [{lng: -122.4194, lat: 37.7749}, {lng: -122.408, lat: 37.791}, {lng: -122.431, lat: 37.769}];
     * var geoJSON = generateGeoJSON(data, "LineString");
     *
     * // 生成面类型的GeoJSON
     * var data = [{lng: -122.4194, lat: 37.7749}, {lng: -122.408, lat: 37.791}, {lng: -122.431, lat: 37.769}];
     * var geoJSON = generateGeoJSON(data, "Polygon");
     * **/

    const featureCollection: FeatureCollection = {
      type: 'FeatureCollection',
      features: [],
    };
    switch (geometryType) {
      case 'Point':
        for (let i = 0; i < data.length; i++) {
          const feature = {
            type: 'Feature',
            geometry: {
              type: geometryType,
              coordinates: [data[i].lng, data[i].lat],
            },
            properties: {},
          };
          featureCollection.features.push(feature);
        }
        break;
      case 'LineString':
        const lineFeature = {
          type: 'Feature',
          geometry: {
            type: geometryType,
            coordinates: [],
          },
          properties: {},
        };
        for (let i = 0; i < data.length; i++) {
          lineFeature['geometry']['coordinates'].push([data[i].lng, data[i].lat]);
        }
        featureCollection.features.push(lineFeature);
        break;
      case 'Polygon':
        const polygonFeature = {
          type: 'Feature',
          geometry: {
            type: geometryType,
            coordinates: [[]],
          },
          properties: {},
        };
        for (let i = 0; i < data.length; i++) {
          polygonFeature['geometry']['coordinates'][0].push([data[i].lng, data[i].lat]);
        }
        featureCollection.features.push(polygonFeature);
        break;
    }
    return featureCollection;
  }
}

export class MP extends BaseMP {
  drawModelChoose: { LineString: string; Point: string; Polygon: string; DEFAULT: string };
  drawModel: string;
  drawLocal: never[];
  drawCurrentId: {
    point: string; //点
    line: string; //线
    lastLine: string; //鼠标位置的点
    polygon: string;
  };
  currentMouseLocation: null;
  correctionMouseLocation: any;
  clickCount: number;
  clickTimeout: any;
  constructor(map) {
    super(map);
    this.drawModelChoose = {
      LineString: 'LineString',
      Point: 'Point',
      Polygon: 'Polygon',
      DEFAULT: 'default',
    };
    this.drawModel = this.drawModelChoose.DEFAULT;
    this.drawLocal = [];
    this.drawCurrentId = {
      point: 'current-draw-point', //点
      line: 'current-draw-line', //线
      lastLine: 'current-draw-last-line', //鼠标位置的点
      polygon: 'current-draw-polygon',
    };
    this.currentMouseLocation = null; //当前鼠标位置
    this.correctionMouseLocation = null; //校正后的鼠标位置
    this.clickCount = 0; // 定义一个计数器
    this.clickTimeout = null; // 定义一个超时变量
  }

  //添加或更新source
  _addOrUpdateSource = (gS, id) => {
    if (this.map.getSource(id)) {
      this.map.getSource(id).setData(gS);
    } else {
      this.map.U.addGeoJSON(id, gS);
    }
  };
  // 添加layer，有则不操作
  _addLayer = (layerId, sourceId, type, style) => {
    // 如果图层不存在，添加图层。如果存在，不做操作
    if (!this.map.getLayer(sourceId)) {
      switch (type) {
        case this.drawModelChoose.Point:
          //addCircleLayer
          this.map.U.addCircleLayer(layerId, sourceId, style);
          break;
        case this.drawModelChoose.LineString:
          //addLineLayer
          this.map.U.addLineLayer(layerId, sourceId, style);
          break;
        case this.drawModelChoose.Polygon:
          //addPolygonLayer
          this.map.U.addFillLayer(layerId, sourceId, style);
          break;
        default:
          console.log('layer类型错误');
      }
    }
  };
  _currentDrawSource = () => {
    // currentGSP 点   currentGSL  线  currentGSPL 面
    const currentGSP = this._generateGeoJSON(this.drawLocal, 'Point');
    this._addOrUpdateSource(currentGSP, this.drawCurrentId.point);

    if (
      this.drawModel === this.drawModelChoose.LineString ||
      this.drawModel === this.drawModelChoose.Polygon
    ) {
      const currentGSL = this._generateGeoJSON(this.drawLocal, 'LineString');
      this._addOrUpdateSource(currentGSL, this.drawCurrentId.line);
    }
    if (this.drawModel === this.drawModelChoose.Polygon) {
      const lastGSPL = this._generateGeoJSON(this.drawLocal, 'Polygon');
      this._addOrUpdateSource(lastGSPL, this.drawCurrentId.polygon);
    }
  };
  //绘制当前的layer
  _currentDrawLayer = () => {
    // 如果是点，直接绘制点
    this._addLayer(this.drawCurrentId.point, this.drawCurrentId.point, 'Point', CIRCLE_STYLE);
    // 如果是线，在点的基础上，加上线
    // 如果是面，在线的基础上，加上面
    if (
      this.drawModel === this.drawModelChoose.LineString ||
      this.drawModel === this.drawModelChoose.Polygon
    ) {
      this._addLayer(this.drawCurrentId.line, this.drawCurrentId.line, 'LineString', LINE_STYLE);
    }
    if (this.drawModel === this.drawModelChoose.Polygon) {
      this._addLayer(
        this.drawCurrentId.polygon,
        this.drawCurrentId.polygon,
        'Polygon',
        POLYGON_STYLE,
      );
    }
  };

  //最后动态的一笔
  _currentDrawLastLine = () => {
    if (
      this.drawModel === this.drawModelChoose.LineString ||
      this.drawModel === this.drawModelChoose.Polygon
    ) {
      if (!this.drawLocal.length) {
        this.map.U.removeLayer(this.drawCurrentId.lastLine);
        this.map.U.removeSource(this.drawCurrentId.lastLine);
        return false;
      }
      const startPoint = this.drawLocal[this.drawLocal.length - 1];
      const endPoint = this.getDrawEndPoint();
      
      // 添加动态线
      const lastGSL = this._generateGeoJSON([startPoint, endPoint], 'LineString');
      this.crossesLine(this.drawLocal, [startPoint, endPoint]);
      this._addOrUpdateSource(lastGSL, this.drawCurrentId.lastLine);
      this._addLayer(
        this.drawCurrentId.lastLine,
        this.drawCurrentId.lastLine,
        'LineString',
        LINE_IS_DRAW_STYLE,
      );
    }
    if (this.drawModel === this.drawModelChoose.Polygon) {
      this._currentDrawLastPolygon();
    }
  };
  //获取矫正的最后点
  getDrawEndPoint = () => {
    let endPoint = null;
    if (this.nearlyClientDistant()) {
      endPoint = this.correctionMouseLocation;
    } else {
      endPoint = this.currentMouseLocation;
    }
    return endPoint;
  };
  //最后动态的一笔,跟随面生成
  _currentDrawLastPolygon = () => {
    // 添加动态面
    const endPoint = this.getDrawEndPoint();
    const drawLocalCopy = deepClone(this.drawLocal);
    drawLocalCopy.push(endPoint);
    drawLocalCopy.push(drawLocalCopy[0]);
    const lastGSPL = this._generateGeoJSON(drawLocalCopy, 'Polygon');
    this._addOrUpdateSource(lastGSPL, this.drawCurrentId.polygon);
    this._addLayer(
      this.drawCurrentId.polygon,
      this.drawCurrentId.polygon,
      'Polygon',
      POLYGON_STYLE,
    );
  };

  //鼠标点击
  clickHandler = (e) => {
    const _this = this;
    this.clickCount++; // 每次单击事件计数器加1
    if (this.clickCount === 1) {
      // 如果是第一次单击
      this.clickTimeout = setTimeout(function () {
        // 启动超时变量
        // 单击事件
        _this.clickCount = 0; // 计数器清零
        if (_this.nearlyClientDistant()) {
          _this.drawLocal.push(_this.correctionMouseLocation);
          // 如果是第一个点，那么就结束绘制
          // todo 画面第一点后结束
          if (
            _this.drawModel === _this.drawModelChoose.Polygon &&
            _this.correctionMouseLocation === _this.drawLocal[0]
          ) {
            _this._currentDrawSource();
            _this._currentDrawLayer();
            _this.emit(_this.drawModel, _this.drawLocal);
            _this.finishDraw();
            return false;
          } else {
            _this.drawLocal.pop();
          }
        } else {
          _this.drawLocal.push(e.lngLat);
        }
        _this._currentDrawSource();
        _this._currentDrawLayer();
        if (_this.drawModel === _this.drawModelChoose.Point) {
          _this.drawIsFinish();
        }
      }, 200); // 设置超时时间为300毫秒
    } else {
      // 如果不是第一次单击
      clearTimeout(this.clickTimeout); // 清除超时变量
      this.clickCount = 0; // 计数器清零
      // 在这里编写双击事件的操作
      
      // 鼠标双击事件

      if (this.drawModel === this.drawModelChoose.LineString) {
        _this.drawLocal.push(e.lngLat);
        _this._currentDrawSource();
        _this._currentDrawLayer();
        this.drawIsFinish();
      }

      if (this.drawModel === this.drawModelChoose.Polygon) {
        _this.drawLocal.push(e.lngLat);
        _this._currentDrawSource();
        _this._currentDrawLayer();
        this.drawIsFinish();
      }
    }
  };
  //右键点击
  contextmenuHandler = (e) => {
    if (this.drawLocal.length < 1) {
      return false;
    }
    this.drawLocal.pop();
    this._currentDrawSource();
    this._currentDrawLayer();
    this._currentDrawLastLine();
  };
  // 鼠标移动
  mousemoveHandler = this.debounce((e) => {
    this.currentMouseLocation = e.lngLat;
    this._currentDrawLastLine();
  }, 5);
  // 判断是否吸附，距离12像素
  nearlyClientDistant = () => {
    // 判断屏幕距离，鼠标吸附
    let _this = this;
    let mousePoint = [this.currentMouseLocation.lng, this.currentMouseLocation.lat];
    let closestFeature = null;
    let closestDistance = Infinity;
    let threshold = 10; // 阈值，单位为像素
    // 遍历所有特征，找到距离最近的特征
    let clientPosition = _this.map.project(mousePoint);
    this.drawLocal.forEach(function (feature) {
      _this.map.project([feature.lng, feature.lat]);
      let featurePosition = _this.map.project([feature.lng, feature.lat]);
      // 计算两个点在屏幕上的像素距离
      let distance = Math.sqrt(
        Math.pow(clientPosition.x - featurePosition.x, 2) +
          Math.pow(clientPosition.y - featurePosition.y, 2),
      );
      if (distance < closestDistance && distance < threshold) {
        closestFeature = feature;
        closestDistance = distance;
      }
    });
    // 如果距离小于阈值，则将标注吸附到特征上
    if (closestFeature) {
      this._changeMouseCursor('pointer');
      this.correctionMouseLocation = closestFeature;
      return true;
    } else {
      // console.log('范围外')
      this._changeMouseCursor('crosshair');
      return false;
    }
  };
  // 判断是否相交 true 相交  false 不相交
  crossesLine = (line1, line2) => {
    let _this = this;
    if (this.drawLocal.length >= 3) {
      let _line1 = line1.map((e) => {
        return [e.lng, e.lat];
      });
      _line1.pop();
      let _line2 = line2.map((e) => {
        return [e.lng, e.lat];
      });
      //判断是否有交叉
      let intersect = turf.lineIntersect(turf.lineString(_line1), turf.lineString(_line2));
      if (intersect.features.length > 0) {
        let coordinates = intersect.features[0].geometry.coordinates;
        //判断是否交叉在点上
        const isExist = _line1.some(
          (item) => item[0] === coordinates[0] && item[1] === coordinates[1],
        );
        if (isExist) {
          // console.log('no cross,穿过了交点')
          _this.map.U.setProperty(this.drawCurrentId.lastLine, 'line-color', '#00FA9A');
          _this.map.on('click', this.clickHandler);
        } else {
          // console.log('cross')
          _this.map.U.setProperty(this.drawCurrentId.lastLine, 'line-color', '#DC143C');
          _this._changeMouseCursor('no-drop');
          _this.map.off('click', this.clickHandler);
        }
      } else {
        // console.log('no cross,不沾边')
        _this.map.U.setProperty(this.drawCurrentId.lastLine, 'line-color', '#00FA9A');
        _this.map.on('click', this.clickHandler);
      }
    } else {
      return false;
    }
  };
  drawStart = () => {
    //每次绘制都要初始化数据
    this.drawLocal = [];
    this.deleteDraw();
    //禁用鼠标双击放大事件
    this.map.doubleClickZoom.disable();
    this._changeMouseCursor('crosshair');
    this.map.on('click', this.clickHandler);
    this.map.on('contextmenu', this.contextmenuHandler);
    this.map.on('mousemove', this.mousemoveHandler);
  };
  draw = (shape) => {
    if(this.drawModelChoose[shape]) {
      this.drawModel = this.drawModelChoose[shape];
      this.drawStart();
    } else {
      console.log(`暂无${shape}类型`);
    }
  };
  drawIsFinish = () => {
    let _this = this;
    this.emit(this.drawModel, this.drawLocal);
    this.finishDraw();
    //恢复双击放大功能
    setTimeout(() => {
      _this.map.doubleClickZoom.enable();
    }, 10);
  };
  // 结束绘制线和面
  finishDraw = () => {
    this.map.U.removeSource(this.drawCurrentId.lastLine);
    this.map.U.removeLayer(this.drawCurrentId.lastLine);
    this.drawModel = this.drawModelChoose.DEFAULT;
    this.unDraw();
  };
  unDraw = () => {
    // console.log('推出绘制模式');
    this.map.off('click', this.clickHandler);
    this.map.off('contextmenu', this.contextmenuHandler);
    this.map.off('mousemove', this.mousemoveHandler);
    this._changeMouseCursor('pointer');
    this.drawLocal = [];
  };
  //删除绘制的内容
  deleteDraw = () => {
    this.map.U.removeSource([
      this.drawCurrentId.line,
      this.drawCurrentId.point,
      this.drawCurrentId.lastLine,
      this.drawCurrentId.polygon,
    ]);
    this.map.U.removeLayer([
      this.drawCurrentId.line,
      this.drawCurrentId.point,
      this.drawCurrentId.lastLine,
      this.drawCurrentId.polygon,
    ]);
    this.unDraw();
  };
}
