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 FillLayer
 * 
 * @summary <用于多边形数据加载>
 */
export default class FillLayer {
  /**
   * 构造函数
   * @param {Object} options -图层选项
   */
  constructor(options) {
    /**
     * 地图对象
     * @type {Object}
     */
    this._map = options.map;

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

    /**
     * 填充颜色
     * @type {String}
     */
    this._fillColor = DataUtils.default(options.color, 'green');

    /**
     * 填充不透明度
     * @type {Number}
     */
    this._fillOpacity = DataUtils.default(options.opacity, 0.95);
    
    /**
     * 是否有边框线
     * @type {Boolean} 
     */
    this._outline = DataUtils.default(options.outline, false);

    /**
     * 线颜色
     * @type {String}
     */
    this._lineColor = DataUtils.default(options.lineColor, 'green');
 
    /**
     * 线宽
     * @type {Number}
     */
    this._lineWidth = DataUtils.default(options.lineWidth, 1.5);
     
    /**
     * 线不透明度
     * @type {Number}
     */
    this._lineOpacity = DataUtils.default(options.lineOpacity, 1.0);
 
    /**
     * 短线数组
     * @type {Array} 
     */
    this._dasharray = DataUtils.default(options.dasharray, undefined);
    
    /**
     * 是否在3D建筑物图层下面
     * @type {Boolean}
     */
    this._underBuilding = DataUtils.default(options.underBuilding, true);

    /**
     * 过滤条件数组
     * @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, 'polygon_simple');

    /**
     * 图层ID
     * @type {String}
     */
    this._layerId = DataUtils.default(options.layerId, this._sourceId);
    // 边框线图层ID
    this._outlineLayerId = this._layerId + '-outline';

    // 要素集
    this._fc = GeoJsonUtils.getEmptyFC();
 
    // 事件
    this._enterEvent = null;
    this._leaveEvent = null;
    this._clickEvent = null; // 外部实现单击事件
    // 初始化事件
    this._initEvent();
  }

  /** 公有方法 **/
  /**
   * 设置数据
   * @param {Object|Array} data -GeoJson要素集/要素/要素列表
   * 
   * @summary <data参数：
   *   Object - GeoJson要素集/要素
   *   Array - GeoJson要素集合
   * >
   */
  setData(data) {
    // 加载图层
    if (!this.hasLayer()) {
      this.loadLayer();
    }

    let fc = GeoJsonUtils.getEmptyFC();

    if (VerifyUtils.isFeatureCollection(data)) { 
      // GeoJSON要素集
      if (data.features && data.features.length > 0) {
        fc.features = data.features;
      }
    } else if (VerifyUtils.isFeature(data)) { 
      // GeoJSON要素
      fc.features = [data];
    } else if (VerifyUtils.isArray(data) && data.length > 0) {
      if (VerifyUtils.isFeature(data[0])) { 
        // GeoJSON要素集合
        fc.features = 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;

    // 空数组
    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);
      }
    }

    // 设置筛选器
    this._map.setFilter(this._layerId, newFilter);
    if (this._outline) {
      this._map.setFilter(this._outlineLayerId, newFilter);
    }
  }

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

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

    const sourceId = this._sourceId;
    const layerId = this._layerId;
    const lineLayerId = this._outlineLayerId;
    // 判断数据源是否存在
    if (!LayerUtils.hasSource(map, sourceId)) {
      // 添加数据源
      map&&map.addSource(sourceId, {
        type: "geojson",
        data: this._dataUrl || GeoJsonUtils.getEmptyFC(),
      });
    }

    // 3D建筑物图层ID
    const buildingLayerId = this._underBuilding 
                            ? map._params.building3DLayerId 
                            : undefined;
    // 添加图层
    map.addLayer({
      type: "fill",
      id: layerId,
      source: sourceId,
      minzoom: minZoom,
      layout: {
        visibility: "none"
      },
      paint: {
        "fill-color": this._fillColor,
        "fill-opacity": this._fillOpacity
      }      
    }, buildingLayerId);
    
    if (this._outline) {
      // 添加边框线图层
      map.addLayer({
        type: "line",
        id: lineLayerId,
        source: sourceId,
        minzoom: minZoom,
        layout: {
          visibility: "none"
        },
        paint: {
          "line-color": this._lineColor,
          "line-width": this._lineWidth,
          "line-opacity": this._lineOpacity
        }        
      }, buildingLayerId);

      // 更新Paint属性
      if (this._dasharray) {
        map.setPaintProperty(lineLayerId, 'line-dasharray', this._dasharray);
      }
    }

    // 设置筛选器
    if (this._filter) {
      map.setFilter(layerId, this._filter);

      if (this._outline) {
        map.setFilter(lineLayerId, this._filter);
      }
    }

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

  /**
   * 显示/隐藏图层
   * @param {Boolean} show -是否显示
   */
  showLayer(show = true) {
    LayerUtils.showLayer(this._map, this._layerId, show);
    if (this._outline) {
      LayerUtils.showLayer(this._map, this._outlineLayerId, show);
    }
  }

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

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

    LayerUtils.removeLayer(this._map, this._layerId);
    if (this._outline) {
      LayerUtils.removeLayer(this._map, this._outlineLayerId);
    }

    this._fc.features = [];
  }

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

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

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

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

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

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

    LayerUtils.offEvent(map, layerId, 'mouseenter', this._enterEvent);
    LayerUtils.offEvent(map, layerId, 'mouseleave', this._leaveEvent);
    if (this._clickEvent) {
      LayerUtils.offEvent(map, layerId, 'click', this._clickEvent);
    }
  }

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

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

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

    LayerUtils.onEvent(map, layerId, 'mouseenter', this._enterEvent);
    LayerUtils.onEvent(map, layerId, 'mouseleave', this._leaveEvent);
    if (this._clickEvent) {
      LayerUtils.onEvent(map, layerId, 'click', this._clickEvent);
    }
  }
}