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

/**
 * 定位(+聚焦)图层
 * @alias LocationLayer
 *
 * @summary <用于定位/聚焦点位加载>
 */
export default class LocationLayer {
  /**
   * 构造函数
   * @param {Object} options -图层选项
   */
  constructor(options) {
    /**
     * 地图对象
     * @type {Object}
     */
    this._map = options.map;

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

    // 图标
    this._icon = 'busi_location';
    this._iconFocus = 'busi_focus';

    /**
     * 图标偏移
     * @type {Array}
     */
    this._offset = DataUtils.default(options.offset, [0, 0]);

    /**
     * 不透明度
     * @type {Number} (0-1)
     */
    this._opacity = DataUtils.default(options.opacity, 1.0);

    /**
     * 线颜色
     */
    this._lineColor = DataUtils.default(options.lineColor, '#87CEFA');

    /**
     * 线宽
     */
    this._lineWidth = DataUtils.default(options.lineWidth, 7.5);

    /**
     * 字段名
     * @type {String}
     */
    this._fieldName = DataUtils.default(options.field, undefined);

    /**
     * 文本大小
     * @type {Number}
     */
    this._textSize = DataUtils.default(options.textSize, 13);

    /**
     * 文本偏移
     * @type {Array}
     */
    this._textOffset = DataUtils.default(options.textOffset, [0, -1.75]);

    /**
     * 点数据源ID
     * @type {String}
     */
    this._sourceId = DataUtils.default(options.sourceId, 'locate_focus');
    // 线数据源ID
    this._lineSourceId = this._sourceId + '_line';
    // 面数据源ID
    this._fillSourceId = this._sourceId + '_fill';

    /**
     * 点图层ID
     * @type {String}
     */
    this._layerId = DataUtils.default(options.layerId, this._sourceId);
    // 线图层ID
    this._lineLayerId = this._layerId + '_line';
    // 面图层ID
    this._fillLayerId = this._layerId + '_fill';

    // 数据源
    this._fc = GeoJsonProvider.getEmptyFC();
    this._lineFC = GeoJsonProvider.getEmptyFC();
    this._polygonFC = GeoJsonProvider.getEmptyFC();

    this._isDragging = false; // 是否正在拖拽
    this._draggingFeature = null; // 正在拖拽的要素

    // 事件
    this._mouseDownEvent = null; // 编辑时实现
    this._enterEvent = null;
    this._leaveEvent = null;
    this._clickEvent = null; // 外部实现单击事件
    // 初始化鼠标事件
    this._initEvent();
  }

  /** 公有方法 **/
  /**
   * 定位
   * @param {Array} center -经纬度
   * @param {Number} zoom -缩放层级
   * @param {Boolean} jump -是否直接定位
   */
  locate(center, zoom = null, jump = false) {
    if (jump) {
      // 调至定位点
      MapProvider.jumpTo(this._map, center, zoom)
    } else {
      // 飞行至定位点
      MapProvider.flyTo(this._map, center, zoom);
    }
  }

  /**
   * 添加定位
   * @param {Array|Object} data -数据
   * @param {Number} zoom -缩放层级
   * @param {Boolean} jump -是否直接定位
   */
  addData(data, zoom = null, jump = false) {
    // 加载图层
    if (!this.hasLayer()) {
      this.loadLayer();
    }
    // 隐藏图层
    this.showLayer(false);

    // 设置定位图标
    this._map.setLayoutProperty(this._layerId, 'icon-image', this._icon);
    this._map.setLayoutProperty(this._layerId, 'icon-offset', this._offset);

    // 更新数据源
    this._updateDataSource(data, zoom, true, jump);
    // 显示图层
    this.showLayer(true);
  }

  /**
   * 清除数据
   */
  clearData() {
    this._fc.features = [];
    this._lineFC.features = [];
    this._polygonFC.features = [];

    const map = this._map;
    // 更新数据源
    LayerProvider.updateSource(map, this._sourceId, this._fc);
    LayerProvider.updateSource(map, this._lineSourceId, this._lineFC);
    LayerProvider.updateSource(map, this._fillSourceId, this._polygonFC);
  }

  /**
   * 设置定位
   * @param {Array|Object} data -数据
   * @param {Number} zoom -缩放层级
   * @param {Boolean} jump -是否直接定位
   */
  setData(data, zoom = null, jump = false) {
    // 加载图层
    if (!this.hasLayer()) {
      this.loadLayer();
    }
    // 隐藏图层
    this.showLayer(false);

    // 设置定位图标
    this._map.setLayoutProperty(this._layerId, 'icon-image', this._icon);
    this._map.setLayoutProperty(this._layerId, 'icon-offset', this._offset);

    // 更新数据源
    this._updateDataSource(data, zoom, false, jump);
    // 显示图层
    this.showLayer(true);
  }

  /**
   * 设置聚焦
   * @param {Array|Object} data -数据
   * @param {Number} zoom -缩放层级
   * @param {Boolean} jump -是否直接定位
   */
  setFocus(data, zoom = null, jump = false) {
    // 加载图层
    if (!this.hasLayer()) {
      this.loadLayer();
    }
    // 隐藏图层
    this.showLayer(false);

    // 设置聚焦图标
    this._map.setLayoutProperty(this._layerId, 'icon-image', this._iconFocus);
    this._map.setLayoutProperty(this._layerId, 'icon-offset', [0, 0]);

    // 更新数据源
    this._updateDataSource(data, zoom, false, jump);
    // 显示图层
    this.showLayer(true);
  }

  /**
   * 设置选中
   * @param {Object} data -GeoJSON要素/要素集
   */
  setSelection(data) {
    // 加载图层
    if (!this.hasLayer()) {
      this.loadLayer();
    }
    // 隐藏图层
    this.showLayer(false);

    // 设置定位图标
    this._map.setLayoutProperty(this._layerId, 'icon-image', this._icon);
    this._map.setLayoutProperty(this._layerId, 'icon-offset', this._offset);

    // 更新数据源
    this._updateDataSource(data, null, false, false);
    // 显示图层
    this.showLayer(true);
  }

  /**
   * 开始拖拽
   */
  startDragging() {
    this._isDragging = true;

    const map = this._map;
    const layerId = this._layerId;

    let targetFeature = null;
    let that = this;

    /**
     * 鼠标按下事件
     */
    this._mouseDownEvent = function(e) {
      if (e.features.length === 0 ||
          e.features[0].geometry.type !== 'Point') return;

      targetFeature = e.features[0];
      // Prevent the default map drag behavior.
      e.preventDefault();

      MapProvider.setCursor(map, 'move');

      // 绑定地图事件
      MapProvider.onEvent(map, 'mousemove', mouseMoveEvent);
      MapProvider.onceEvent(map, 'mouseup', mouseUpEvent); // 只响应一次
    };

    /**
     * 鼠标移动事件
     */
    let mouseMoveEvent = function(e) {
      if (!targetFeature || !targetFeature.properties.uid) return;

      MapProvider.setCursor(map, 'move');

      let coords = e.lngLat;
      const features = that._fc.features.filter(feature => feature.properties.uid === targetFeature.properties.uid);
      if (features.length > 0) {
        that._draggingFeature = features[0];
        that._draggingFeature.geometry.coordinates = [coords.lng, coords.lat];

        // 更新数据源
        LayerProvider.updateSource(map, that._sourceId, that._fc);
      }
    }

    /**
     * 鼠标抬起事件
     */
    let mouseUpEvent = function(e) {
      MapProvider.setCursor(map);
      // Unbind mouse events
      MapProvider.offEvent(map, 'mousemove', mouseMoveEvent)
    }

    // 绑定图层事件
    LayerProvider.onEvent(map, layerId, 'mousedown', this._mouseDownEvent);
  }

  /**
   * 停止拖拽
   */
  stopDragging() {
    this._isDragging = false;

    const map = this._map;
    const layerId = this._layerId;
    // 解除图层事件绑定
    LayerProvider.offEvent(map, layerId, 'mousedown', this._mouseDownEvent);
  }

  /**
   * 加载图层
   */
  loadLayer() {
    if (this.hasLayer()) return;

    // 加载面图层(边框)
    this._loadFillLayer();
    // 加载线图层
    this._loadLineLayer();
    // 加载点图层
    this._loadPointLayer();

    // 绑定事件
    this._onEvent();
  }

  /**
   * 显示/隐藏图层
   * @param {Boolean} show -是否显示
   */
  showLayer(show) {
    LayerProvider.showLayer(this._map, this._layerId, show);
    LayerProvider.showLayer(this._map, this._lineLayerId, show);
    LayerProvider.showLayer(this._map, this._fillLayerId, show);
  }

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

  /**
   * 移除图层(and数据源)
   */
  removeLayer() {
    const map = this._map;

    // 解除事件绑定
    this.offEvent();

    // 移除图层
    LayerProvider.removeLayer(map, this._layerId);
    LayerProvider.removeLayer(map, this._lineLayerId);
    LayerProvider.removeLayer(map, this._fillLayerId);

    // 移除数据源
    LayerProvider.removeSource(map, this._sourceId);
    LayerProvider.removeSource(map, this._lineSourceId);
    LayerProvider.removeSource(map, this._fillSourceId);
  }


  /**
   * 绑定单击事件
   * @param {Function} event -事件函数
   */
  onClick(event) {
    const map = this._map;

    this._clickEvent = event;
    if (this.hasLayer()) {
      LayerProvider.onEvent(map, this._layerId, 'click', event);
      LayerProvider.onEvent(map, this._lineLayerId, 'click', event);
      LayerProvider.onEvent(map, this._fillLayerId, 'click', event);
    }
  }

  /**
   * 绑定事件
   * @param {String} name -事件名称
   * @param {Function} event -事件函数
   *
   * @summary <name参数取值：mouseenter-鼠标移入; mouseleave-鼠标移除; click-鼠标单击>
   */
  onEvent(name, event) {
    const map = this._map;

    switch (name) {
      case 'mouseenter': {
        this._enterEvent = event;
        break;
      }
      case 'mouseleave': {
        this._leaveEvent = event;
        break;
      }
      case 'click': {
        this._clickEvent = event;
        break;
      }
      default:
        return;
    }

    if (this.hasLayer()) {
      LayerProvider.onEvent(map, this._layerId, name, event);
      LayerProvider.onEvent(map, this._lineLayerId, name, event);
      LayerProvider.onEvent(map, this._fillLayerId, name, event);
    }
  }

  /**
   * 解除事件绑定
   */
  offEvent() {
    if (!this.hasLayer()) return;

    const map = this._map;

    const layerId = this._layerId;
    const lineLayerId = this._lineLayerId;
    const fillLayerId = this._fillLayerId;

    // 点图层
    LayerProvider.offEvent(map, layerId, 'mouseenter', this._enterEvent);
    LayerProvider.offEvent(map, layerId, 'mouseleave', this._leaveEvent);
    if (this._clickEvent) {
      LayerProvider.offEvent(map, layerId, 'click', this._clickEvent);
    }

    // 线图层
    LayerProvider.offEvent(map, lineLayerId, 'mouseenter', this._enterEvent);
    LayerProvider.offEvent(map, lineLayerId, 'mouseleave', this._leaveEvent);
    if (this._clickEvent) {
      LayerProvider.offEvent(map, lineLayerId, 'click', this._clickEvent);
    }

    // 面图层
    LayerProvider.offEvent(map, fillLayerId, 'mouseenter', this._enterEvent);
    LayerProvider.offEvent(map, fillLayerId, 'mouseleave', this._leaveEvent);
    if (this._clickEvent) {
      LayerProvider.offEvent(map, fillLayerId, 'click', this._clickEvent);
    }
  }

  /** 私有方法 **/
  /**
   * 加载点图层
   */
  _loadPointLayer() {
    const map = this._map;
    const minZoom = this._minzoom;

    // 添加数据源
    map.addSource(this._sourceId, {
      type: "geojson",
      data: this._fc
    });
    // 添加图层
    if (this._fieldName) {
      // 自定义字体颜色
      let textColor = map._customStyle ? map._customStyle.textColor : '#F8F8FF';

      map.addLayer({
        type: "symbol",
        id: this._layerId,
        source: this._sourceId,
        minzoom: minZoom,
        layout: {
          "symbol-placement": "point",
          "symbol-avoid-edges": true,
          "icon-image": this._icon,
          "icon-offset": this._offset,
          "icon-allow-overlap": true,
          "text-optional": true,
          "text-allow-overlap": true,
          "text-line-height": 1.2,
          "text-max-angle": 38,
          "text-offset": this._textOffset,
          "text-field": "{" + this._fieldName + "}",
          "text-size": this._textSize,
          "text-font": ["Microsoft YaHei"],
          "text-letter-spacing": 0.02,
          "text-max-width": 16,
          visibility: "none"
        },
        paint: {
          "icon-opacity": this._opacity,
          "text-color": textColor,
          "text-halo-color": "#8B8B8B",
          "text-halo-width": 0.1,
        }
      }, map._build3dLayerId);
    } else {
      map.addLayer({
        type: "symbol",
        id: this._layerId,
        source: this._sourceId,
        minzoom: minZoom,
        layout: {
          "symbol-placement": "point",
          "symbol-avoid-edges": true,
          "icon-image": this._icon,
          "icon-offset": this._offset,
          "icon-allow-overlap": true,
          //"icon-ignore-placement": true,
          visibility: "none"
        },
        paint: {
          "icon-opacity": this._opacity
        }
      }, map._build3dLayerId);
    }
  }

  /**
   * 加载线图层
   */
  _loadLineLayer() {
    const map = this._map;
    const minZoom = this._minzoom;
    const maxZoom = map.getMaxZoom();

    // 添加数据源
    map.addSource(this._lineSourceId, {
      type: "geojson",
      data: this._lineFC
    });
    // 添加图层
    map.addLayer({
      type: "line",
      id: this._lineLayerId,
      source: this._lineSourceId,
      minzoom: minZoom,
      layout: {
        "line-join": "round",
        "line-cap": "round",
        visibility: "none"
      },
      paint: {
        "line-color": this._lineColor,
        "line-width": {
          base: 1.5,
          stops: [
            [minZoom, 0.75],
            [maxZoom, this._lineWidth],
          ]
        },
        "line-opacity": this._opacity
      }
    }, map._build3dLayerId);
  }

  /**
   * 加载面图层
   */
  _loadFillLayer() {
    const map = this._map;
    const minZoom = this._minzoom;

    // 添加数据源
    map.addSource(this._fillSourceId, {
      type: "geojson",
      data: this._polygonFC
    });
    // 添加图层
    map.addLayer({
      type: "line",
      id: this._fillLayerId,
      source: this._fillSourceId,
      minzoom: minZoom,
      layout: {
        "line-join": "round",
        "line-cap": "butt",
        visibility: "none"
      },
      paint: {
        "line-color": this._lineColor,
        "line-width": this._lineWidth,
        "line-opacity": this._opacity
      }
    }, map._build3dLayerId);
  }

  /**
   * 初始化事件
   */
  _initEvent() {
    const map = this._map;

    // 移入事件
    this._enterEvent = (e) => {
      MapProvider.setCursor(map, 'pointer');
    }

    // 移出事件
    this._leaveEvent = (e) => {
      MapProvider.setCursor(map);
    }
  }

  /**
   * 绑定事件
   */
  _onEvent() {
    const map = this._map;

    const layerId = this._layerId;
    const lineLayerId = this._lineLayerId;

    LayerProvider.onEvent(map, layerId, 'mouseenter', this._enterEvent);
    LayerProvider.onEvent(map, layerId, 'mouseleave', this._leaveEvent);

    LayerProvider.onEvent(map, lineLayerId, 'mouseenter', this._enterEvent);
    LayerProvider.onEvent(map, lineLayerId, 'mouseleave', this._leaveEvent);
  }

  /**
   * 更新数据源
   * @param {Array|Object} data -数据
   * @param {Number} zoom -缩放层级
   * @param {Boolean} isAdd - 是否添加
   * @param {Boolean} isJump -是否直接定位
   */
  _updateDataSource(data, zoom, isAdd = false, isJump = false) {
    const map = this._map;

    this._fc.features = [];
    this._lineFC.features = [];

    let center = null;
    let bbox = null;
    const features = isAdd ? this._fc.features : [];
    const lineFeatures = isAdd ? this._lineFC.features : [];
    const polygonFeatures = isAdd ? this._polygonFC.features : [];

    if (VerifyUtils.isArray(data)) {
      // 数组
      if (VerifyUtils.isObject(data[0])) {
        for (let i = 0; i < data.length; i++) {
          // 对象
          if (data[i].center === true) {
            let lon = data[i].gisLng || data[i].lng || data[i].longitude || data[i].lon;
            let lat = data[i].gisLat || data[i].lat || data[i].latitude;
            center = [lon, lat];
          }

          // 获取定位要素
          let feature = this._getLocatedFeature(data[i]);
          if (feature) {
            features.push(feature);
          }
        }

        if (!center) {
          const tmpFC = GeoJsonProvider.getEmptyFC();
          tmpFC.features = features;

          if (features.length > 1) {
            // 计算边界框
            bbox = GeogUtils.calcBBox(tmpFC);
          } else {
            // 计算中点
            const fea = turf.center(tmpFC);
            center = fea.geometry.coordinates;
          }
        }
      } else {
        // 值
        center = [data[0], data[1]];
        let feature = GeoJsonProvider.getPointFeature(center, {});

        features.push(feature);
      }
    } else if (VerifyUtils.isObject(data)) {
      // JSON对象/GeoJSON要素、要素集
      // 获取定位要素
      let feature = this._getLocatedFeature(data);
      if (feature) {
        // 计算中点/中心点
        center = GeogUtils.calcCenter(feature);

        switch (feature.geometry.type.toLowerCase()) {
          case 'point': {
            features.push(feature);
            break;
          }
          case 'linestring': {
            // 计算边界框
            bbox = GeogUtils.calcBBox(feature);

            lineFeatures.push(feature);
            // 构造中心点要素
            const centerFea = GeoJsonProvider.getPointFeature(center, feature.properties);
            features.push(centerFea);
          }
          case 'polygon': {
            // 计算边界框
            bbox = GeogUtils.calcBBox(feature);

            polygonFeatures.push(feature)
            // 构造中心点要素
            const centerFea = GeoJsonProvider.getPointFeature(center, feature.properties);
            features.push(centerFea);
            break;
          }
          default:
            break;
        }
      }
    } // end of if ()

    this._fc.features = features;
    this._lineFC.features = lineFeatures;
    this._polygonFC.features = polygonFeatures;
    // 添加uid属性
    GeoJsonProvider.geneUUID(this._fc);
    GeoJsonProvider.geneUUID(this._lineFC);
    GeoJsonProvider.geneUUID(this._polygonFC);

    // 更新数据源
    LayerProvider.updateSource(map, this._sourceId, this._fc);
    LayerProvider.updateSource(map, this._lineSourceId, this._lineFC);
    LayerProvider.updateSource(map, this._fillSourceId, this._polygonFC);

    if (zoom) {
      if (bbox) {
        // 缩放到边界
        MapProvider.fitBounds(map, bbox, zoom, isJump);
      } else {
        if (isJump) {
          // 跳至定位点
          MapProvider.jumpTo(map, center, zoom);
        } else {
          // 飞行至定位点
          MapProvider.flyTo(map, center, zoom);
        }
      }
    }
  }

  /**
   * 获取定位要素
   * @param {Object} data -数据
   * @param {Number} zoom -缩放层级
   */
  _getLocatedFeature(data) {
    let feature = null;
    if (VerifyUtils.isGeoJsonFC(data)) {
      // GeoJSON要素集合
      feature = data.features[0];
    } else if (VerifyUtils.isGeoJsonFeature(data)) {
      // GeoJSON要素
      feature = data;
    } else if (VerifyUtils.isObject(data)) {
      // JSON对象
      let lon = data.gisLng || data.lng || data.longitude || data.lon;
      let lat = data.gisLat || data.lat || data.latitude;
      if (lon && lat) {
        // JSON对象转GeoJSON要素
        feature = GeoJsonProvider.getPointFeature([parseFloat(lon), parseFloat(lat)], data);
      }
    }

    if (!feature) {
      return null;
    }

    if (feature.geometry.type.toLowerCase() === 'multilinestring') {
      feature.geometry.type = 'LineString';
      feature.geometry.coordinates = feature.geometry.coordinates[0];
    } else if (feature.geometry.type.toLowerCase() === 'multipolygon') {
      feature.geometry.type = 'Polygon';
      feature.geometry.coordinates = feature.geometry.coordinates[0];
    }

    return feature;
  }
}
