import * as turf from "@turf/turf";
import DataUtils from '../utils/DataUtils';
import GeoJsonProvider from '../geog/GeoJsonProvider';
import LayerProvider from '../geog/LayerProvider';

/**
 * 多边形绘制类
 * @alias PolygonDrawing
 *
 * @summary <用于动态绘制多边形>
 */
export default class PolygonDrawing {
  /**
   * 构造函数
   * @param {Object} options -图层选项
   */
  constructor(options) {
    /**
     * 地图对象
     * @type {Object}
     */
    this._map = options.map;

    /**
     * 最小缩放层级
     * @type {Number}
     */
    this._minzoom = DataUtils.default(options.minzoom, 10);

    this._isMeasure = false; // 是否测量
    this._isDrawing = false;
    /* 事件 */
    this._clickEvent = null;     // 单击事件
    this._dbclickEvent = null;   // 双击事件
    this._mouseMoveEvent = null; // 鼠标移动事件

    /* 数据源ID */
    this._sourceId = 'drawing_polygon';
    this._lineSourceId = this._sourceId + '-line';
    this._pointSourceId = this._sourceId + '-point';
    this._labelSourceId = this._sourceId + '-label'; // 标注

    /* 图层ID */
    this._layerId = this._sourceId;
    this._lineLayerId = this._lineSourceId;
    this._pointLayerId = this._pointSourceId;
    this._labelAreaLayerId = this._pointLayerId + '-label-area';
    this._labelLengthLayerId = this._pointLayerId + '-label-length';

    /* 要素集 */
    // 多边形GeoJSON对象
    this._fc = GeoJsonProvider.getEmptyFC();
    // 多边形边界GeoJSON对象
    this._lineFC = GeoJsonProvider.getEmptyFC();

    // 多边形顶点GeoJSON对象
    this._pointFC = GeoJsonProvider.getEmptyFC();
    // 标注点GeoJSON对象
    this._labelFC = GeoJsonProvider.getEmptyFC();
  }

  /** 公有方法 **/
  /**
   * 开始绘制
   * @param {Boolean} isMeasure -是否测量
   * @param {function} callback -回调函数
   */
  draw(isMeasure, callback) {
    this._isMeasure = isMeasure;

    if (!this._hasLayer()) {
      this._loadLayer();
    }

    const that = this;
    const map = this._map;
    // 改变光标
    map.getCanvas().style.cursor = 'crosshair';

    this._isDrawing = true;
    let clickNum = 0;

    /**
     * 添加点
     * @param {Array} lonLat -经纬度
     * @param {Number} startEnd -起止(-1-中间 0-起始 1-终止)
     */
    function addPoint(lonLat, startEnd = -1) {
      /* 点图层 */
      if (startEnd !== 1) {
        // 获取点要素
        let pointFea = GeoJsonProvider.getPointFeature(lonLat);
        that._pointFC.features.push(pointFea);
      }
      // 更新数据源
      LayerProvider.updateSource(map, that._pointSourceId, that._pointFC);

      /* 线图层 */
      // 获取折线坐标集合
      let lineCoords = that._pointFC.features.map(fea => {
        return fea.geometry.coordinates;
      });
      lineCoords.push(lonLat);
      // 闭合
      if (lineCoords.length > 2) {
        lineCoords.push(lineCoords[0]);
      }

      let lastLineFea;
      if (that._lineFC.features.length > 0) {
        lastLineFea = that._lineFC.features[that._lineFC.features.length - 1];
      } else {
        lastLineFea = GeoJsonProvider.getEmptyFeature('LineString');
        that._lineFC.features = [lastLineFea];
      }
      lastLineFea.geometry.coordinates = lineCoords;
      if (lineCoords.length > 1) {
        // 更新数据源
        LayerProvider.updateSource(map, that._lineSourceId, that._lineFC);
      }

      /* 面图层 */
      let lastFea;
      if (that._fc.features.length > 0) {
        lastFea = that._fc.features[that._fc.features.length - 1];
      } else {
        lastFea = GeoJsonProvider.getEmptyFeature('Polygon');
        that._fc.features = [lastFea];
      }
      lastFea.geometry.coordinates = [ lineCoords ];

      if (lineCoords.length > 3) {
        // 更新数据源
        LayerProvider.updateSource(map, that._sourceId, that._fc);
      }

      /* 标注图层 */
      if (isMeasure && startEnd === 1 && lineCoords.length > 3) {
        // 计算面积(平方千米)
        let area = (turf.area(lastFea) / (1000 * 1000)).toFixed(4);
        // 计算周长(千米)
        let len = turf.length(lastFea, {
          units: 'kilometers'
        }).toFixed(4);

        let centerFea = turf.centerOfMass(lastFea);
        centerFea.properties.area = '面积: ' + area + ' km²';
        centerFea.properties.len = '周长: ' + len + ' km';

        // 添加到标注数据源
        that._labelFC.features.push(centerFea);
        // 更新数据源
        LayerProvider.updateSource(map, that._labelSourceId, that._labelFC);
      }

      // 回调
      if (startEnd === 1 && callback) {
        callback(lastFea);
      }
    }

    /**
     * 单击事件
     */
    this._clickEvent = function onClickEvent(e) {
      if (!that._isDrawing) return;

      let lonLat = [e.lngLat.lng, e.lngLat.lat];
      if (clickNum < 1) {
        if (!isMeasure) {
          // 非测量 清空要素集
          that._labelFC.features = [];
          that._pointFC.features = [];
          that._lineFC.features = [];
          that._fc.features = [];
        }

        // 定义折线要素
        let lineFea = GeoJsonProvider.getEmptyFeature('LineString');
        that._lineFC.features.push(lineFea);

        // 定义多边形要素
        let feature = GeoJsonProvider.getEmptyFeature('Polygon');
        that._fc.features.push(feature);

        // 添加起始点
        addPoint(lonLat, 0);
      } else {
        // 添加顶点
        addPoint(lonLat);
      }

      clickNum += 1;
    }

    /**
     * 鼠标移动事件
     */
    this._mouseMoveEvent = function onMoveEvent(e) {
      if (!that._isDrawing || clickNum === 0) return;

      let lonLat = [e.lngLat.lng, e.lngLat.lat];
      // 获取折线坐标集合
      let lineCoords = that._pointFC.features.map(fea => {
        return fea.geometry.coordinates;
      });
      lineCoords.push(lonLat);
      // 闭合
      if (lineCoords.length > 2) {
        lineCoords.push(lineCoords[0]);
      }

      // 更新折线数据
      let lastLineFea;
      if (that._lineFC.features.length > 0) {
        lastLineFea = that._lineFC.features[that._lineFC.features.length - 1];

        if (lineCoords.length > 1) {
          lastLineFea.geometry.coordinates = lineCoords;
          LayerProvider.updateSource(map, that._lineSourceId, that._lineFC);
        }
      }

      // 更新多边形数据
      let lastFea;
      if (that._fc.features.length > 0) {
        lastFea = that._fc.features[that._fc.features.length - 1];

        if (lineCoords.length > 3) {
          lastFea.geometry.coordinates = [lineCoords];
          LayerProvider.updateSource(map, that._sourceId, that._fc);
        }
      }
    }

    /**
     * 双击事件
     */
    this._dbclickEvent = function onDbclickEvent(e) {
      if (!that._isDrawing || clickNum === 0) return;

      let lonLat = [e.lngLat.lng, e.lngLat.lat];
      // 添加终止点
      addPoint(lonLat, 1);
      clickNum = 0;

      // 清理临时数据
      that._pointFC.features = [];
      that._lineFC.features = [];

      LayerProvider.updateSource(map, that._pointSourceId, that._pointFC);
      LayerProvider.updateSource(map, that._lineSourceId, that._lineFC);
    }

    // 绑定事件
    map.on('click', this._clickEvent);
    map.on('dblclick', this._dbclickEvent);
    map.on('mousemove', this._mouseMoveEvent);
  }

  /**
   * 绘制结束
   */
  finish() {
    const map = this._map;

    // 改变光标
    map.getCanvas().style.cursor = '';

    // 解除事件绑定
    map.off('click', this._clickEvent);
    map.off('dblclick', this._dbclickEvent);
    map.off('mousemove', this._mouseMoveEvent);

    this._isDrawing = false;
  }

  /**
   * 获取GeoJSON要素集
   *
   * @returns {Object} GeoJSON对象
   */
  getData() {
    return this._fc;
  }

  /**
   * 清空数据
   */
  clearData() {
    this._fc.features = [];
    // 更新数据源
    LayerProvider.updateSource(this._map, this._sourceId, this._fc);
  }

  /**
   * 移除图层
   */
  removeLayer() {
    // 移除标注图层
    this._removeLayer(this._labelLengthLayerId, null);
    this._removeLayer(this._labelAreaLayerId, this._labelSourceId);
    // 移除点图层
    this._removeLayer(this._pointLayerId, this._pointSourceId);
    // 移除线图层
    this._removeLayer(this._lineLayerId, this._lineSourceId);
    // 移除填充图层
    this._removeLayer(this._layerId, this._sourceId);

    // 数据设置为空
    this._labelFC.features = [];
    this._pointFC.features = [];
    this._lineFC.features = [];
    this._fc.features = [];
  }

  /** 私有方法 **/
  /**
   * 加载图层
   */
  _loadLayer() {
    // 加载填充图层
    this._loadFillLayer();
    // 加载边界图层
    this._loadLineLayer();

    // 加载顶点图层
    this._loadPointLayer();
    if (this._isMeasure) {
      // 加载标注图层
      this._loadLabelLayer();
    }
  }

  /**
   * 判断图层是否已加载
   *
   * @returns {Boolean}
   */
  _hasLayer() {
    return (
      LayerProvider.hasLayer(this._map, this._layerId) &&
      LayerProvider.hasLayer(this._map, this._lineLayerId) &&
      LayerProvider.hasLayer(this._map, this._pointLayerId)
    );
  }

  /**
   * 添加点图层
   */
  _loadPointLayer() {
    if (LayerProvider.hasLayer(this._map, this._pointLayerId)) {
      return;
    }

    // 添加数据源
    this._map.addSource(this._pointSourceId, {
      type: "geojson",
      data: this._pointFC
    });

    // 添加图层
    this._map.addLayer({
      type: "circle",
      id: this._pointLayerId,
      source: this._pointSourceId,
      minzoom: this._minzoom,
      layout: {
        visibility: "visible",
      },
      paint: {
        "circle-radius": 3.5,
        "circle-color": "#176BC9",
        "circle-opacity": 0.8,
        "circle-stroke-width": 2,
        "circle-stroke-color": "#78B7FF"
      }
    });
  }

  /**
   * 添加标注图层
   */
  _loadLabelLayer() {
    if (LayerProvider.hasLayer(this._map, this._labelAreaLayerId)) {
      return;
    }

    const map = this._map;
    // 自定义字体颜色
    let textColor = map._customStyle ? map._customStyle.textColor : '#F8F8FF';

    // 添加数据源
    map.addSource(this._labelSourceId, {
      type: "geojson",
      data: this._labelFC
    });

    // 添加面积标注图层
    map.addLayer({
      type: "symbol",
      id: this._labelAreaLayerId,
      source: this._labelSourceId,
      minzoom: this._minzoom,
      layout: {
        "symbol-placement": "point",
        "text-ignore-placement": true,
        "text-field": "{area}",
        "text-offset": [0, 0],
        "text-font": ["Microsoft YaHei"],
        "text-max-width": 32,
        "text-max-angle": 30,
        "text-size": {
          base: 1,
          stops: [
            [16, 14],
            [18, 20],
          ],
        },
        visibility: "visible",
      },
      paint: {
        "text-color": textColor,
        "text-halo-color": "#8B8B8B",
        "text-halo-width": 0.1,
        "text-halo-blur": 0.5,
      }
    });

    // 添加长度标注图层
    map.addLayer({
      type: "symbol",
      id: this._labelLengthLayerId,
      source: this._labelSourceId,
      minzoom: this._minzoom,
      layout: {
        "symbol-placement": "point",
        "text-ignore-placement": true,
        "text-field": "{len}",
        "text-offset": [0, 1.5],
        "text-font": ["Microsoft YaHei"],
        "text-max-width": 32,
        "text-max-angle": 30,
        "text-size": {
          base: 1,
          stops: [
            [16, 14],
            [18, 20],
          ],
        },
        visibility: "visible",
      },
      paint: {
        "text-color": textColor,
        "text-halo-color": "#8B8B8B",
        "text-halo-width": 0.1,
        "text-halo-blur": 0.5,
      }
    });
  }

  /**
   * 添加移动线图层
   */
  _loadLineLayer() {
    if (LayerProvider.hasLayer(this._map, this._lineLayerId)) {
      return;
    }

    // 添加数据源
    this._map.addSource(this._lineSourceId, {
      type: "geojson",
      data: this._lineFC
    });

    // 添加图层
    this._map.addLayer({
      type: "line",
      id: this._lineLayerId,
      source: this._lineSourceId,
      minzoom: this._minzoom,
      layout: {
        visibility: "visible",
      },
      paint: {
        "line-color": "#176BC9",
        "line-width": 2,
        "line-opacity": 0.75,
        "line-dasharray": [2.5, 1.5],
      }
    });
  }

  /**
   * 添加填充图层
   */
  _loadFillLayer() {
    if (LayerProvider.hasLayer(this._map, this._layerId)) {
      return;
    }

    // 添加数据源
    this._map.addSource(this._sourceId, {
      type: "geojson",
      data: this._fc
    });

    // 添加图层
    this._map.addLayer({
      type: "fill",
      id: this._layerId,
      source: this._sourceId,
      minzoom: this._minzoom,
      layout: {
        visibility: "visible",
      },
      paint: {
        "fill-color": "#00FFF0",
        "fill-opacity": 0.25,
      }
    });
  }

  /**
   * 移除图层
   * @param {String} layerId -图层ID
   * @param {String} sourceId -数据源ID
   */
  _removeLayer(layerId, sourceId) {
    // 移除图层
    LayerProvider.removeLayer(this._map, layerId);

    if (sourceId) {
      // 移除数据源
      LayerProvider.removeSource(this._map, sourceId);
    }
  }
}
