import DataUtils from "../utils/DataUtils";
import VerifyUtils from "../utils/VerifyUtils";
import GeoJsonUtils from "../utils/GeoJsonUtils";
import LayerUtils from "../utils/LayerUtils";
import MapUtils from "../utils/MapUtils";

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

    /**
     * 最小缩放层级
     * @type {Number}
     */
    this._minZoom = DataUtils.default(options.minZoom, 9);

    /**
     * 聚合最大缩放层级
     * @type {Number}
     */
    this._clusterMaxZoom = DataUtils.default(options.clusterMaxZoom, 14);

    /**
     * 聚合半径
     * @type {Number}
     */
    this._clusterRadius = DataUtils.default(options.clusterRadius, 120);

    /**
     * 图标(表达式)
     * @type {String}
     */
    this._icon = DataUtils.default(options.icon, 'marker');

    /**
     * 图标大小(表达式)
     * @type {Number} 
     */
    this._size = DataUtils.default(options.size, 1.0);

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

    /**
     * 标注文字大小(表达式)
     * @type {String} 
     */
    this._textSize = DataUtils.default(options.textSize, 14);

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

    /**
     * 是否在3D建筑物图层下面
     * @type {Boolean}
     */
    this._underBuilding = DataUtils.default(options.underBuilding, false);
 
    /**
     * 筛选器
     * @type {Array} 
     */
    this._filter = DataUtils.default(options.filter, undefined);

    /**
     * 数据服务地址
     * @type {String} 
     */
    this._dataUrl = DataUtils.default(options.dataUrl, undefined);

    /**
     * 数据源ID
     * @type {String}
     */
    this._sourceId = DataUtils.default(options.sourceId, 'point_cluster');

    /**
     * 聚合图层ID
     * @type {String}
     */
    this._layerId = DataUtils.default(options.layerId, this._sourceId);
    // 标注图层
    this._labelLayerId = this._layerId + '-label';
    // 非聚合图层Id
    this._unclusterLayerId = this._layerId + '-uncluster';
    
    // 要素集
    this._fc = GeoJsonUtils.getEmptyFC();

    // 聚合图层事件
    this._enterEvent = null;
    this._leaveEvent = null;
    this._clickEvent = null;

    // 非聚合图层事件(外部实现)
    this._unclusterEnterEvent = null;
    this._unclusterLeaveEvent = null;
    this._unclusterClickEvent = null;

    // 初始化事件
    this._initEvent();
  }

  /** 公有方法 **/
  /**
   * 设置数据
   * @param {Object|Array} data -GeoJson要素集/要素列表/点位数组
   * 
   * @summary <data参数：
   *   Object - GeoJson要素集
   *   Array - GeoJson要素数组/点位数组(包含longitude/lng, latitude/lat字段)
   * >
   */
  setData(data) {
    // 加载图层
    if (!this.hasLayer()) {
      this.loadLayer();
    }

    let fc = GeoJsonUtils.getEmptyFC();

    if (VerifyUtils.isFeatureCollection(data)) {
      // 要素集
      if (data.features && data.features.length > 0) {
        fc.features = data.features;
      }
    } else if (VerifyUtils.isArray(data) && data.length > 0) {
      if (VerifyUtils.isFeature(data[0])) {
        // 要素集合
        fc.features = data;
      } else { 
        // 点位数组
        fc = GeoJsonUtils.array2PointFC(data);
      }
    }

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

    // 更新数据源
    LayerUtils.updateSource(this._map, this._sourceId, fc);
    // 显示图层
    this.showLayer(true);
  }

  /**
   * 设置筛选器
   * @param {Array} filter -筛选器
   */
  setFilter(filter) {
    if (!this.hasLayer()) return;
    
    const map = this._map;

    // 空数组
    if (VerifyUtils.isArray(filter) && filter.length === 0) {
      filter = null;
    }

    let newFilter = null;
    if (this._filter) {
      newFilter = this._filter.length === 1
                  ? ['all', this._filter]
                  : [].concat(this._filter);
    }

    if (filter) {
      if (!newFilter) {
        newFilter = filter;
      } else {
        newFilter.push(filter);
      }
    }

    // 设置筛选器
    map.setFilter(this._layerId, newFilter);
    map.setFilter(this._unclusterLayerId, newFilter);
  }

  /**
   * 设置绘制属性
   * @param {String} name -属性名称
   * @param {String|Object} value -属性值 
   */
  setPaintProperty(name, value) {
    if (!this.hasLayer()) return;
      
    this._map.setPaintProperty(this._layerId, name, value);
    this._map.setPaintProperty(this._unclusterLayerId, name, value);
  }

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

    const map = this._map;
    const minZoom = this._minZoom;

    const sourceId = this._sourceId;
    if (!LayerUtils.hasSource(map, sourceId)) {
      // 添加数据源
      map&&map.addSource(sourceId, {
        type: "geojson",
        data: this._dataUrl || GeoJsonUtils.getEmptyFC(),
        cluster: true,
        clusterMaxZoom: this._clusterMaxZoom,
        clusterRadius: this._clusterRadius,
      });
    }

    // 自定义样式
    const customStyle = map&&map._params.customStyle;
    // 自定义字体颜色
    const textColor = customStyle ? customStyle.textColor : '#F8F8FF';
    const haloColor = customStyle ? customStyle.haloColor : '#8B8B8B';
    const haloWidth = customStyle ? customStyle.haloWidth : 0.1;
    const haloBlur = customStyle ? customStyle.haloBlur : 0;

    // 3D建筑物图层ID
    const buildingLayerId = this._underBuilding 
                            ? map._params.building3DLayerId 
                            : undefined;
    // 聚合点图层
    map.addLayer({
      type: "circle",
      id: this._layerId,
      source: sourceId,
      minzoom: minZoom,
      filter: ["has", "point_count"],
      layout: {
        visibility: "none"
      },
      paint: {
        "circle-color": [
          "step",
          ["get", "point_count"], "#40E0D0",
          200, "#F1F037",
          800, "#FD8D3C"
        ],
        "circle-radius": [
          "step",
          ["get", "point_count"], 20,
          200, 30,
          800, 40,
        ],
        "circle-opacity": 0.85,
        "circle-stroke-width": 6,
        "circle-stroke-color": [
          "step",
          ["get", "point_count"], "#40E0D0",
          200, "#F1F037",
          800, "#FD8D3C"
        ],
        "circle-stroke-opacity": 0.45
      }
    });
    // 标注图层
    map.addLayer({
      type: "symbol",
      id: this._labelLayerId,
      source: sourceId,
      minzoom: minZoom,
      filter: ["has", "point_count"],
      layout: {
        "text-field": "{point_count_abbreviated}",
        "text-font": ["Microsoft YaHei"],
        "text-size": [
          "step",
          ["get", "point_count"], 14,
          200, 18,
          800, 24,
        ],
        visibility: "none"
      }
    });
    // 非聚合图层
    map.addLayer({
      type: "symbol",
      id: this._unclusterLayerId,
      source: sourceId,
      minzoom: minZoom,
      filter: ["!has", "point_count"],
      layout: {
        "symbol-avoid-edges": true,
        "symbol-placement": 'point',
        "icon-allow-overlap": true,
        "icon-ignore-placement": true,
        "icon-image": this._icon,
        "icon-size": this._size,
        "icon-offset": [0, 10],
        "text-ignore-placement": true,
        "text-optional": true,
        "text-field": "{" + this._fieldName + "}",
        "text-font": ["Microsoft YaHei"],
        "text-size": this._textSize,
        "text-offset": this._textOffset,
        visibility: "none"
      },
      paint: {
        "text-color": textColor,
        "text-halo-color": haloColor,
        "text-halo-width": haloWidth,
        "text-halo-blur": haloBlur,
      }
    }, buildingLayerId);

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

  /**
   * 显示/隐藏图层
   * @param {Boolean} show -是否显示
   */
  showLayer(show = true) {
    const map = this._map;

    // 聚合图层
    LayerUtils.showLayer(map, this._layerId, show);
    // 标注图层
    LayerUtils.showLayer(map, this._labelLayerId, show);
    // 非聚合图层
    LayerUtils.showLayer(map, this._unclusterLayerId, show);
  }

  /**
   * 判断图层是否已加载
   * 
   * @returns {Boolean}
   */
  hasLayer() {
    const map = this._map;

    return (
      LayerUtils.hasLayer(map, this._layerId) &&
      LayerUtils.hasLayer(map, this._labelLayerId) &&
      LayerUtils.hasLayer(map, this._unclusterLayerId)
    );
  }

  /**
   * 移除图层 
   */
  removeLayer() {
    const map = this._map;
    // 解除事件绑定
    this.offEvent();

    // 聚合图层
    LayerUtils.removeLayer(map, this._layerId);
    // 标注图层
    LayerUtils.removeLayer(map, this._labelLayerId);
    // 非聚合图层
    LayerUtils.removeLayer(map, this._unclusterLayerId);

    this._fc.features = [];
  }

  /**
   * 移除数据源
   */
  removeSource() {
    LayerUtils.removeSource(this._map, this._sourceId);
  }

  /**
   * 绑定非聚合图层单击事件
   * @param {Function} event -事件函数 
   */
  onClick(event) {
    this._unclusterClickEvent = event;

    if (this.hasLayer()) {
      LayerUtils.onEvent(this._map, this._unclusterLayerId, 'click', event);
    }
  }

  /**
   * 绑定非聚合图层事件
   * @param {String} name -事件名称
   * @param {Function} event -事件函数
   * 
   * @summary <name参数取值：
   *  mouseenter-鼠标移入; 
   *  mouseleave-鼠标移出; 
   *  click-鼠标单击
   * > 
   */
  onEvent(name, event) {
    switch (name) {
      case 'mouseenter': {
        this._unclusterEnterEvent = event;
        break;
      }
      case 'mouseleave': {
        this._unclusterLeaveEvent = event;

        break;
      }
      case 'click': {
        this._unclusterClickEvent = event;
        break;
      }
      default:
        return;
    }

    if (this.hasLayer()) {
      LayerUtils.onEvent(this._map, this._unclusterLayerId, name, event);
    }
  }

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

    const map = this._map;

    const layerId = this._layerId;
    const unclusterLayerId = this._unclusterLayerId;

    // 非聚合图层
    if (this._unclusterEnterEvent) {
      LayerUtils.offEvent(map, unclusterLayerId, 'mouseenter', this._unclusterEnterEvent);
    }
    if (this._unclusterLeaveEvent) {
      LayerUtils.offEvent(map, unclusterLayerId, 'mouseleave', this._unclusterLeaveEvent);
    }
    if (this._unclusterClickEvent) {
      LayerUtils.offEvent(map, unclusterLayerId, 'click', this._unclusterClickEvent);
    }

    // 聚合图层
    LayerUtils.offEvent(map, layerId, 'mouseenter', this._enterEvent);
    LayerUtils.offEvent(map, layerId, 'mouseleave', this._leaveEvent);
    LayerUtils.offEvent(map, layerId, 'click', this._clickEvent);
  }

  /** 私有方法 **/
  /**
   * 初始化事件 
   */
  _initEvent() {
    const map = this._map;
    const that = this;

    /**
     * 聚合点移入事件 
     */
    this._enterEvent = (e) => {
      MapUtils.setCursor(map, 'pointer');
    }
    /**
     * 聚合点移出事件 
     */
    this._leaveEvent = (e) => {
      MapUtils.setCursor(map);
    }
    /**
     * 聚合点单击事件
     */
    this._clickEvent = (e) => {
      const features = map.queryRenderedFeatures(e.point, {
        layers: [that._layerId],
      });

      const clusterId = features[0].properties.cluster_id;
      map.getSource(that._sourceId)
         .getClusterExpansionZoom(clusterId, (err, zoom) => {
          if (err) return;

          map.easeTo({
            center: features[0].geometry.coordinates,
            zoom: zoom + 1,
          });
        });
    }
  }

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

    const layerId = this._layerId;
    const unclusterLayerId = this._unclusterLayerId;

    // 聚合图层
    LayerUtils.onEvent(map, layerId, 'mouseenter', this._enterEvent);
    LayerUtils.onEvent(map, layerId, 'mouseleave', this._leaveEvent);
    LayerUtils.onEvent(map, layerId, 'click', this._clickEvent);

    // 非聚合图层
    if (this._unclusterEnterEvent) {
      LayerUtils.onEvent(map, unclusterLayerId, 'mouseenter', this._unclusterEnterEvent);
    }
    if (this._unclusterLeaveEvent) {
      LayerUtils.onEvent(map, unclusterLayerId, 'mouseleave', this._unclusterLeaveEvent);
    }
    if (this._unclusterClickEvent) {
      LayerUtils.onEvent(map, unclusterLayerId, 'click', this._unclusterClickEvent);
    }
  }  
}
