import DataUtils from '../utils/DataUtils';
import VerifyUtils from '../utils/VerifyUtils';
import MapProvider from '../geog/MapProvider';
import LayerProvider from '../geog/LayerProvider';
import GeoJsonProvider from '../geog/GeoJsonProvider';

/**
 * 定义点聚合图层
 * @alias ClusterLayer
 *
 * @summary <用于点聚合加载>
 */
export default class ClusterLayer {
  /**
   * 构造函数
   * @param {Object} options -图层选项
   */
  constructor(options) {
    // 默认数据源ID
    this._defaultSourceId = 'point_cluster';
    // 数据源是否存在
    this._hasSource = true;

    /**
     * 地图对象
     * @type {Object}
     */
    this._map = options.map;

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

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

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

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

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

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

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

    // 数据源要素集
    this._fc = GeoJsonProvider.getEmptyFC();

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

    // 聚合图层
    this._enterEvent = null;
    this._leaveEvent = null;
    this._clickEvent = null;
    // 初始化鼠标事件
    this._initEvent();
  }

  /** 公有方法 **/
  /**
   * 设置图标
   * @param {Object} icon -图标/表达式
   *
   * @summary <若icon为表达式，其参数：
   * {
   *   property: fieldName,
   *   type: "categorical",
   *   stops: [[value, imageName],[value2, imageName2],...],
   *   default: imageName
   * }>
   */
  setIcon(icon) {
    this._icon = icon;

    // 设置Layout样式
    if (this.hasLayer()) {
      this._map.setLayoutProperty(this._unclusterLayerId, 'icon-image', this._icon);
    }
  }

  /**
   * 设置数据
   * @param {Object|String|Array} data -GeoJSON要素集/查询语句/点位数组
   *
   * @summary <data参数：
   *   Object - GeoJson要素/要素集
   *   String - cql_filter查询语句
   *   Array - 点位数组(包含longitude, latitude字段)
   * >
   */
  setData(data) {
    // 加载图层
    if (!this.hasLayer()) {
      this.loadLayer();
    }
    // 隐藏图层
    this.showLayer(false);

    const map = this._map;
    let fc = null;
    if (VerifyUtils.isGeoJsonFC(data)) {
      // GeoJSON要素集
      fc = data;
    } else if (VerifyUtils.isGeoJsonFeature(data)) {
      // GeoJSON要素
      fc = GeoJsonProvider.getEmptyFC();
      fc.features = [data];
    } else if (VerifyUtils.isArray(data)) {
      // 数组转GeoJSON对象
      fc = GeoJsonProvider.data2PointFC(data);
    } else if (VerifyUtils.isString(data)) {
      // 查询语句
      if (this._hasSource) {
        const source = map.getSource(this._sourceId);
        fc = source._data + (data ? "&cql_filter=" + data : '');
      }
    }

    this._fc.features = [];
    this._fc = fc || GeoJsonProvider.getEmptyFC();

    // 更新数据源
    map.getSource(this._sourceId).setData(this._fc);
    // 显示图层
    this.showLayer(true);
  }

  /**
   * 添加数据(只针对接口)
   * @param {Array|Object} data -点数组/GeoJSON要素集
   */
  appendData(data) {
    if (!data) return;

    // 加载图层
    if (!this.hasLayer()) {
      this.loadLayer();
      // 显示图层
      this.showLayer(true);
    }

    if (!this._fc || VerifyUtils.isString(this._fc)) {
      this._fc = GeoJsonProvider.getEmptyFC();
    }

    // 要素集必须存在
    if (!VerifyUtils.isGeoJsonFC(this._fc)) return;

    // 要添加的数据必须是GeoJSON要素集、点数组
    if (!(VerifyUtils.isGeoJsonFC(data) || VerifyUtils.isArray(data))) return;

    let fc = null;
    if (VerifyUtils.isGeoJsonFC(data)) {
      fc = data;
    } else if (VerifyUtils.isArray(data)) {
      // 数组转GeoJSON对象
      fc = GeoJsonProvider.data2PointFC(data);
    }
    if (!fc) return;

    // 合并要素列表
    this._fc.features = this._fc.features.concat(fc.features);

    // 更新数据源
    this._map.getSource(this._sourceId).setData(this._fc);
  }

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

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

    const sourceId = this._sourceId;
    // 判断数据源是否存在
    this._hasSource = LayerProvider.hasSource(map, sourceId);
    if (!this._hasSource) {
      // 添加数据源
      map.addSource(sourceId, {
        type: "geojson",
        data: this._dataUrl || GeoJsonProvider.getEmptyFC(),
        cluster: true,
        clusterMaxZoom: this._clusterMaxZoom,
        clusterRadius: this._clusterRadius,
      });
    }

    // 添加点聚合图层
    map.addLayer({
      type: "circle",
      id: this._layerId,
      source: sourceId,
      minzoom: minZoom,
      filter: ["has", "point_count"],
      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
      },
      layout: {
        visibility: "none"
      }
    });
    // 添加标注图层
    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-placement": "point",
        "symbol-avoid-edges": true,
        "icon-image": this._icon,
        "icon-offset": [0, 0],
        "icon-allow-overlap": true,
        "icon-ignore-placement": true,
        visibility: "none"
      }
    }, map._build3dLayerId);

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

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

    // 点聚合图层
    LayerProvider.showLayer(map, this._layerId, show);
    // 点聚合数字图层
    LayerProvider.showLayer(map, this._labelLayerId, show);
    // 非聚合图层
    LayerProvider.showLayer(map, this._unclusterLayerId, show);
  }

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

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

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

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

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

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

  /**
   * 判断图层是否可见
   *
   * @returns {Boolean}
   */
  isVisible() {
    const map = this._map;

    return (
      LayerProvider.isLayerVisible(map, this._layerId) ||
      LayerProvider.isLayerVisible(map, this._labelLayerId) ||
      LayerProvider.isLayerVisible(map, this._unclusterLayerId)
    );
  }

  /**
   * 绑定非聚合图层单击事件
   * @param {Function} event -事件函数
   */
  onClick(event) {
    this._unclusterClickEvent = event;
    if (this.hasLayer()) {
      LayerProvider.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()) {
      LayerProvider.onEvent(this._map, this._unclusterLayerId, name, event);
    }
  }

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

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

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

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

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

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

    // 聚合点移出事件
    this._leaveEvent = (e) => {
      MapProvider.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;

    // 绑定聚合图层事件
    LayerProvider.onEvent(map, layerId, 'mouseenter', this._enterEvent);
    LayerProvider.onEvent(map, layerId, 'mouseleave', this._leaveEvent);
    LayerProvider.onEvent(map, layerId, 'click', this._clickEvent);

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