
const MAP_FIELD_DRAW_LAYER_NAME = '_Draw_layer';
const MAP_FIELD_INIT_LAYER_NAME = '_Init_layer';

export const olMap = function(selector) {
  this.selector = selector;
  this.mapObject = null;
  this.minMapZoom = 16;
  this.maxMapZoom = 18;
  this.allowDraw = false;
  this.allowSelect = false;
  this.allowMark = false;
  this.coord = [115.452752, 31.789033];
  this.isInited = false;
  this.error = null;
  this.userLayers = [];
  this.callback = {
    'draw::start': [],
    'draw::end': [],
    'draw::remove': [],
    'draw::reset': [],
    'interactive::select': [],
    'mark::start': [],
    'mark::end': [],
    'mark::reset': [],
  };
  // 当前支持的配置和默认值, 支持.拼接的字符串作为键(只能一级)
  const SUPPORT_CONFIG = {
    style: {
      fill: {
        color: 'rgba(204, 255, 204, 0.3)', // 多边形填充颜色
        'color::SELECTED': "rgb(204, 255, 255, 0.5)", // 选择模式下选中时的多边形填充颜色
      },
      stroke: {
        color: '#47c68f', // 线颜色
        width: 3, // 线宽
        'color::SELECTED': '#00FFFF', // 选择模式下选中时的线颜色
        'width::SELECTED': 8, // 选择模式下选中时的线宽
      },
      text: {
        font: 'normal 12px 微软雅黑', // 字体
        fill: {
          color: '#FF0000', // 文字颜色
        }
      }
    }
  };
  this.drawResult = null;
  this.draw = null;
  this.currentSelect = null;
  this.markResult = null;

  this.__ASSERT = function(cond, msg) {
    if(!cond)
      this.__SetError(msg || '错误');
    return !!cond;
  };
  // 设置回调
  this.SetCallback = function(type, func) {
    if(!func || !this.callback.hasOwnProperty(type))
      return false;
    let arr = this.callback[type];
    if(arr.includes(func))
      return false;
    arr.push(func);
    return func;
  };
  // 移除回调, 函数为空则移除该回调类型下的所有函数
  this.RemoveCallback = function(type, func = null) {
    if(!this.callback.hasOwnProperty(type))
      return false;
    if(func)
    {
      let arr = this.callback[type];
      let index = arr.indexOf(func);
      if(index >= 0)
      {
        arr.splice(index, 1);
        return true;
      }
      return false;
    }
    else
    {
      this.callback[type] = [];
      return true;
    }
  };
  // 清空全部回调
  this.ClearCallback = function() {
    for(let k in this.callback)
      this.callback[k] = [];
  };
  // private 调用回调
  this.__Callback = function(type/*, 其他参数 */) {
    let args = [];
    for(let i = 1; i < arguments.length; i++)
      args.push(arguments[i]);
    this.callback[type].forEach((x) => x(...args));
  };
  // private 请求必须初始化, 未初始化则错误
  this.__RequireInited = function() {
    return this.__ASSERT(this.IsInited(), '地图未初始化');
  };
  // private 设置当前错误, 并抛出错误
  this.__SetError = function(err) {
    this.error = err;
    if(this.error)
    {
      console.error(err);
      throw this.error;
    }
  };
  // 是否完成初始化
  this.IsInited = function() {
    return this.mapObject !== null;
  };
  // 初始化, 如果已初始化则错误
  this.Init = function() {
    this.__ASSERT(!this.IsInited(), '已经初始化');
    this.__CreateMap(this.selector);
    this.isInited = true;
  };
  // 销毁地图对象, 如果未初始化则返回
  this.DestroyMap = function() {
    if(!this.IsInited())
      return;
    this.ClearCallback();
    this.userLayers = [];
    //delete this.mapObject;
    document.querySelector('#' + this.selector).innerHTML = '';
    this.mapObject = null;
  };
  // private 创建地图元素, 已初始化则错误, 元素ID未指定则错误
  this.__CreateMap = function() {
    this.__ASSERT(!this.IsInited(), '已经初始化');
    this.__ASSERT(this.selector, '元素ID缺失');

    let projection = new ol.proj.Projection({
      //地图投影类型
      code: "EPSG:3857",
      units: "degrees",
      //extent:extent
    });
    let aerial = new ol.layer.Tile({
      source: new ol.source.XYZ({
        url: "http://t0.tianditu.gov.cn/img_w/wmts?" +
          "SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles" +
          "&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=cc4aba6e967096098249efa069733067",
      }),
      isGroup: true,
      name: "卫星影像图",
    });

    let yingxzi = new ol.layer.Tile({
      source: new ol.source.XYZ({
        url: "https://t0.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=cc4aba6e967096098249efa069733067",
      }),
      isGroup: true,
      name: "天地图文字标注--卫星影像图",
    });

    //加载地图
    let map = new ol.Map({
      // interactions: ol.interaction.DragPan({
      //   onFocusOnly: false
      // }),
      controls: ol.control.defaults({attribution: false, zoom: false, rotate: false}).extend([]), //隐藏放大缩小按钮
      layers: [aerial, yingxzi],
      projection: projection,
      target: this.selector,
      logo: 'false',
      view: new ol.View({
        center: ol.proj.fromLonLat(this.coord),
        //zoom: false,
        zoom: 17.9,
        minZoom: this.minMapZoom, //地图缩小限制
        maxZoom: this.maxMapZoom, //地图放大限制
      }),
    });

    this.mapObject = map;
    //console.log(map, map.getProperties());
  };
  // 获取配置值
  this.__GetConfig = function(config, name, defVal) {
    if(!config || typeof(config) !== 'object')
      return defVal;
    if(config.hasOwnProperty(name))
      return config[name];
    let arr = name.split('.');
    let ptr = config;
    for(let i in arr)
    {
      ptr = ptr[arr[i]];
      //console.log(arr[i], JSON.stringify(ptr));
      if(!ptr)
        break;
    }
    //console.log(`__GetConfig(${name}) -> ${ptr}`);
    if(ptr === undefined || ptr === null || ptr === '')
      return defVal;
    return ptr;
  };
  // 获取默认配置值
  this.GetDefaultConfig = function(name) {
    let arr = name.split('.');
    let ptr = SUPPORT_CONFIG;
    for(let i in arr)
    {
      ptr = ptr[arr[i]];
      if(!ptr)
        break;
    }
    return ptr;
  };
  // 设置当前坐标点
  this.SetCoord = function(lng, lat) {
    this.__RequireInited();
    this.mapObject.getView().setCenter(ol.proj.fromLonLat([lng, lat]));
    //console.log('SetCoord', lng, lat,this.mapObject);
    return true;
  };
  // 获取地图投影对象
  this.GetProjection = function() {
    this.__RequireInited();
    return this.mapObject.get('view').getProjection();
  };
  // 检查名称是否存在
  this.ExistsName = function(name) {
    if(this.userLayers.length === 0)
      return false;
    let res = this.userLayers.findIndex((x) => {
      return x.name === name;
    });
    return res !== -1;
  };
  // private 检查名称是否存在, 存在则错误
  this.__CheckNameExists = function(name) {
    return this.__ASSERT(!this.ExistsName(name), `标识重复: ${name}`);
  };
  // private 放入图层, 名称存在则错误
  this.__PushLayer = function(name, layer, title = null, data = null, config = null) {
    this.__RequireInited();
    this.__CheckNameExists(name);
    this.mapObject.addLayer(layer);
    //console.log(name, layer);
    this.userLayers.push({
      name: name,
      layer: layer,
      visible: true,
      title: title,
      data: data,
      config: config,
    });
    return true;
  };
  // 根据索引获取图层, 索引可为负数, 索引无效则错误
  this.GetLayer = function(index) {
    if(this.userLayers.length === 0)
      return null;
    let i = index < 0 ? this.userLayers.length + index : index;
    this.__ASSERT(i >= 0 && i < this.userLayers.length, `无效索引: ${index}`);
    return this.userLayers[i];
  };
  // 根据名称查找图层, 名称不存在返回null
  this.FindLayer = function(name) {
    if(this.userLayers.length === 0)
      return null;
    return this.userLayers.find((x) => x.name === name);
  };
  // private 搜索图层, 索引/名称, orFail为真, 则未找到时错误
  this.__SearchLayer = function(name, orFail = false) {
    let layer = null;
    let index = -1;
    if(typeof(name) === 'number')
    {
      let i = name < 0 ? this.userLayers.length + name : name;
      if(i >= 0 && i < this.userLayers.length)
      {
        layer = this.userLayers[i];
        index = i;
      }
    }
    else
    {
      let i = this.userLayers.findIndex((x) => x.name === name);
      if(i >= 0)
      {
        layer = this.userLayers[i];
        index = i;
      }
    }
    if(orFail && index === -1)
      this.__SetError(`未找到图层: ${name}(${typeof(name)})`);
    return [layer, index];
  };
  // 带出图层数据 索引/名称, 未找到则返回null, 找到会移除图层数据, 但不会从地图移除图层
  this.__TakeLayer = function(name) {
    if(this.userLayers.length === 0)
      return null;
    let [layer, index] = this.__SearchLayer(name);
    if(index !== -1)
      this.userLayers.splice(index, 1);
    return layer;
  };
  // 移除图层, 如果不指定name, 则移除最后一个
  this.PopLayer = function(name = null) {
    this.__RequireInited();
    //console.log("pop", name, typeof(name));
    if(name === null)
      name = -1;
    let layer = this.__TakeLayer(name);
    if(layer)
      this.mapObject.removeLayer(layer.layer);
    return layer !== null;
  };
  // 设置图层显隐
  this.SetLayerVisible = function(name, on) {
    this.__RequireInited();
    let [layer, ] = this.__SearchLayer(name, true);
    layer.visible = true;
    layer.layer.setVisible(on);
    return true;
  };
  // 隐藏图层
  this.HideLayer = function(name) {
    return this.SetLayerVisible(name, false);
  };
  // 显示图层
  this.ShowLayer = function(name) {
    return this.SetLayerVisible(name, true);
  };
  // 开始绘制
  this.StartDraw = function() {
    this.__RequireInited();
    this.__ASSERT(this.allowDraw, '未开启绘制');
    this.drawResult = null;
    this.__Callback('draw::start');

    this.PopLayer(MAP_FIELD_DRAW_LAYER_NAME);
    this.mapObject.removeInteraction(this.draw);
    let drawLayer = new ol.layer.Vector({
      source: new ol.source.Vector(),
    });

    this.__PushLayer(MAP_FIELD_DRAW_LAYER_NAME, drawLayer);
    this.draw = new ol.interaction.Draw({
      source: drawLayer.getSource(),
      type: "Polygon"
    });
    let self = this;
    this.draw.on('drawend', function (evt) {
      self.mapObject.removeInteraction(self.draw);
      let feature = evt.feature;
      let geometry = feature.getGeometry();
      let coordinate = geometry.getCoordinates();
      self.drawResult = coordinate;
      self.__Callback('draw::end', self.drawResult);
    });
    this.mapObject.addInteraction(this.draw);
    //console.log("drawPolygon");
  };
  // 移除绘制
  this.ClearDraw = function() {
    this.__RequireInited();
    this.__ASSERT(this.draw, '没有开始绘制');
    this.mapObject.removeInteraction(this.draw);
    this.PopLayer(MAP_FIELD_DRAW_LAYER_NAME);
    this.draw = null;
    this.drawResult = null;
    this.__Callback('draw::remove');
    this.__Callback('draw::end', '');
  };
  // 重置绘制
  this.ResetDraw = function() {
    this.ClearDraw();
    this.StartDraw();
    this.__Callback('draw::reset');
  };
  // 设置是否启用绘制
  this.SetAllowDraw = function(on) {
    this.__RequireInited();
    if(this.allowDraw == on)
      return;
    this.allowDraw = on;
    this.RemoveDrawLayer();
  };
  this.RemoveDrawLayer = function() {
    this.__RequireInited();
    if(this.draw)
    {
      this.ClearDraw();
      this.draw = null;
    }
    this.PopLayer(MAP_FIELD_DRAW_LAYER_NAME);
    this.drawResult = null;
    this.markResult = null;
  };
  // 更新视图
  this.Update = function() {
    this.__RequireInited();
    this.mapObject.updateSize();
  };
  // 新增/替换图层
  this.SetLayer = function(name, theGeom, title = null, data = null, config = null) {
    this.__RequireInited();
    this.PopLayer(name);
    this.AddLayer(name, theGeom, title, data, config);
  };
  // 新增图层(name不可存在)
  this.AddLayer = function(name, theGeom, title = null, data = null, config = null) {
    //console.log("pop", name, theGeom);
    if(!theGeom)
      return false;
    this.__RequireInited();
    this.__CheckNameExists(name);
    if(typeof(theGeom) !== 'object')
      theGeom = JSON.parse(theGeom);
    let features = {
      type: 'Feature',
      geometry: theGeom,
      properties: {
        name: name,
        title: title,
        data: data,
      },
    };
    //地图只加载一次
    //加载地图编辑
    //图层查询定位开始 ---------start
    let hc_land = new ol.layer.Vector({
      title: name,
      source: new ol.source.Vector({
        projection: this.GetProjection(),
        features: new ol.format.GeoJSON().readFeatures(JSON.stringify(features)),
      }),
      style: new ol.style.Style({
        fill: new ol.style.Fill({
          //矢量图层填充颜色，以及透明度
          color: this.__GetConfig(config, 'style.fill.color', "rgba(204, 255, 204, 0.3)"),
        }),
        stroke: new ol.style.Stroke({
          //边界样式
          color: this.__GetConfig(config, 'style.stroke.color', "#47c68f"),
          width: this.__GetConfig(config, 'style.stroke.width', 3),
        }),
        //矢量标注样式设置函数，设置image为图标ol.style.Icon
        text: new ol.style.Text({
          textAlign: "center", //位置
          textBaseline: "middle", //基准线
          font: this.__GetConfig(config, 'style.text.font', "normal 12px 微软雅黑"), //文字样式
          text: title, //文本内容
          fill: new ol.style.Fill({
            //文本填充样式（即文字颜色)
            color: this.__GetConfig(config, 'style.text.fill.color', "#FF0000"),
          }),
        }),
      }),
    });
    if(!this.__PushLayer(name, hc_land, title, data, config))
      return false;
    let maxXMap = hc_land.values_.source.featuresRtree_.rbush_.data.maxX;
    let maxYMap = hc_land.values_.source.featuresRtree_.rbush_.data.maxY;
    let minXMap = hc_land.values_.source.featuresRtree_.rbush_.data.minX;
    let minYMap = hc_land.values_.source.featuresRtree_.rbush_.data.minY;
    //定位查询位置
    let center = ol.extent.getCenter([maxXMap, maxYMap, minXMap, minYMap]); //获取边界区域的中心位置
    this.mapObject.getView().animate({
      // 只设置需要的属性即可
      center: center, // 中心点
      zoom: 17.9, // 缩放级别
      rotation: undefined, // 缩放完成view视图旋转弧度
      duration: 1000, // 缩放持续时间，默认不需要设置
    });
    return true;
  };
  // 新增图层列表
  this.AddLayers = function(list /* name, theGeom, title = null, data = null, config = null */) {
    if(!list.length)
      return;
    this.__RequireInited();
    for(let i in list)
    {
      let {name, theGeom, title, data, config} = list[i];
      this.AddLayer(name, theGeom, title, data, config);
    }
  };
  // 清除所有用户图层
  this.Clear = function() {
    this.__RequireInited();
    for(let i in this.userLayers)
      this.PopLayer(name);
    this.userLayers = [];
  };
  // 设置图层列表
  this.SetLayers = function(list /* name, theGeom, title = null, data = null, config = null */) {
    this.Clear();
    this.AddLayers(list);
  };
  this.SetAllowSelect = function(on) {
    this.__RequireInited();
    if(this.allowSelect == on)
      return;
    this.allowSelect = on;
    this.ClearSelection();
    if(this.allowSelect)
    {
      let self = this;
      this.mapObject.on("singleclick", function (evt) {
        let feature = self.mapObject.forEachFeatureAtPixel(
          evt.pixel,
          (feature) => feature,
        );
        self.SetSelection(feature ? feature.values_.name : null, true);
      });
    }
    else
    {
      this.mapObject.on("singleclick", null);
    }
  };
  this.ClearSelection = function() {
    this.__RequireInited();
    if(this.currentSelect)
    {
      this.__ResetStyle(this.currentSelect);
      this.currentSelect = null;
    }
  };
  this.SetSelection = function(name, callback = false) {
    this.__RequireInited();
    if(name)
    {
      if(name !== this.currentSelect)
      {
        this.ClearSelection();
        this.__HighlightStyle(name);
      }
      else if(this.currentSelect)
      {
        name = null;
        this.ClearSelection();
      }
    }
    else
    {
      if(this.currentSelect)
      {
        name = null;
        this.ClearSelection();
      }
    }
    this.currentSelect = name;
    if(callback)
      this.__Callback('interactive::select', this.currentSelect);
  };
  this.__ResetStyle = function(name) {
    let ulayer = this.FindLayer(name);
    let style = ulayer.layer.getStyle();
    style.setFill(new ol.style.Fill({
      //矢量图层填充颜色，以及透明度
      color: this.__GetConfig(ulayer.config, 'style.fill.color', "rgba(204, 255, 204, 0.3)"),
    }));
    style.setStroke(new ol.style.Stroke({
      //边界样式
      color: this.__GetConfig(ulayer.config, 'style.stroke.color', "#47c68f"),
      width: this.__GetConfig(ulayer.config, 'style.stroke.width', 3),
    }));
    ulayer.layer.setStyle(style);
  };
  this.__HighlightStyle = function(name) {
    let ulayer = this.FindLayer(name);
    //console.log(this.mapObject, ulayer, ulayer.layer.getStyle());
    let style = ulayer.layer.getStyle();
    style.setFill(new ol.style.Fill({
      //矢量图层填充颜色，以及透明度
      color: this.__GetConfig(ulayer.config, 'style.fill.color::SELECTED', "rgb(204, 255, 255, 0.5)"),
    }));
    style.setStroke(new ol.style.Stroke({
      //边界样式
      color: this.__GetConfig(ulayer.config, 'style.stroke.color::SELECTED', "#00FFFF"),
      width: this.__GetConfig(ulayer.config, 'style.stroke.width::SELECTED', 8),
    }));
    ulayer.layer.setStyle(style);
  };
  // 设置是否启用标记
  this.SetAllowMark = function(on) {
    this.__RequireInited();
    if(this.allowMark == on)
      return;
    this.allowMark = on;
    this.RemoveDrawLayer();
  };
  // 开始标记
  this.StartMark = function() {
    this.__RequireInited();
    this.__ASSERT(this.allowMark, '未开启标记');
    this.markResult = null;
    this.__Callback('mark::start');

    this.PopLayer(MAP_FIELD_DRAW_LAYER_NAME);
    let sourceMap = new ol.source.Vector({wrapX: false});
    let drawLayer = new ol.layer.Vector({
      source: sourceMap,
    });

    this.__PushLayer(MAP_FIELD_DRAW_LAYER_NAME, drawLayer);
    this.draw = new ol.interaction.Draw({
      source: drawLayer.getSource(),
      type: "Point"
    });
    //this.draw.__sourceMap = sourceMap;
    let self = this;
    this.draw.on('drawend', function (evt) {
      if(self.draw)
      {
        self.__ClearMark();
        self.StartMark();
      }
      let feature = evt.feature;
      let geometry = feature.getGeometry();
      let coordinate = geometry.getCoordinates();
      self.markResult = coordinate;
      let newFeature = new ol.Feature({
        geometry: new ol.geom.Point(coordinate), //几何信息
        //name: "标注点",
      });
      newFeature.setStyle(new ol.style.Style({
        image: new ol.style.Icon({
          //设置图标偏移
          anchor: [0.5, 1],
          //标注样式的起点位置
          anchorOrigin: "top-right",
          //X方向单位：分数
          anchorXUnits: "fraction",
          //Y方向单位：像素
          anchorYUnits: "fraction",
          //偏移起点位置的方向
          offsetOrigin: "top-right",
          //透明度
          opacity: 0.9,
          //图片路径
          src: require("@/assets/images/point/p6.png"),
        }),
/*        text: new ol.style.Text({
          textAlign: "center", //位置
          textBaseline: "middle", //基准线
          font: "normal 12px 微软雅黑", //文字样式
          //text: "标注点", //文本内容
          fill: new ol.style.Fill({
            //文本填充样式（即文字颜色)
            color: "#ff0000",
          }),
        }),*/
        zIndex: 9999,
      }));
      self.draw.source_.addFeature(newFeature);
      self.__Callback('mark::end', self.markResult);
    });
    this.mapObject.addInteraction(this.draw);
    //console.log("drawPolygon");
  };
  // 移除标记
  this.__ClearMark = function() {
    this.__RequireInited();
    this.__ASSERT(this.draw, '没有开始标记');
    this.mapObject.removeInteraction(this.draw);
    this.draw = null;
  };
  // 重置标记
  this.ResetMark = function() {
    this.__RequireInited();
    this.__ASSERT(this.draw, '没有标记');
    this.mapObject.removeInteraction(this.draw);
    this.PopLayer(MAP_FIELD_DRAW_LAYER_NAME);
    this.markResult = null;
    this.__Callback('mark::reset');
  };

  // 构造初始化
  if(this.selector)
    this.Init();
  return this;
};

// olMap类静态成员
olMap.MAP_FIELD_DRAW_LAYER_NAME = MAP_FIELD_DRAW_LAYER_NAME;
olMap.MAP_FIELD_INIT_LAYER_NAME = MAP_FIELD_INIT_LAYER_NAME;
olMap.gen_multipoint = function() {
  let json = {
    type: 'MultiPoint',
    coordinates: [],
  };
  for(let i in arguments)
  {
    json.coordinates.push(arguments[i]);
  }
  return json;
};
olMap.gen_multipoint_json = function() {
  return JSON.stringify(this.gen_multipoint(...arguments));
};
olMap.gen_point = function(x, y, z) {
  let json = {
    type: 'Point',
    coordinates: Array.isArray(x) ? x : (z !== undefined ? [x, y, z] : [x, y]),
  };
  return json;
};
olMap.gen_point_json = function(x, y, z) {
  return JSON.stringify(this.gen_point(x, y, z));
};
olMap.gen_polygon = function() {
  let json = {
    type: 'Polygon',
    coordinates: [],
  };
  for(let i in arguments)
  {
    json.coordinates.push(arguments[i]);
  }
  return json;
};
olMap.gen_polygon_json = function(x, y, z) {
  return JSON.stringify(this.gen_polygon(x, y, z));
};
