/*
 * @Author: your name
 * @Date: 2022-01-11 13:45:12
 * @LastEditTime: 2022-02-26 13:59:03
 * @LastEditors: Please set LastEditors
 * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 * @FilePath: /test/hello-world/src/utils/mapClass.js
 */
import { pipeColor, svgUrl } from "@/utils/mapClass/config.js";

// 编辑类
// 在地图上新增的设备可以直接编辑,
// 已经保存完成的设备需要点编辑才可以编辑
export class EditorMap {
  // 地图的对象实例
  map = null;
  // 父vue的实例
  vue = null;
  // 操作 新建,编辑,删除,编辑跟删除只对已经在图上的设备有效 默认值:0, 新建:1,编辑:2, 删除: 3。
  // 新建的时候会把未保存的线条清空
  control = 0;
  // 鼠标事件对象,用来将点跟线上图
  mousetool = null;
  // 当前正在手动绘制的对象
  nowMouseTarget = null;
  // 当线mousetool线被按下的时候的flag 当线被按下的时候为true,就不询问是否删除了
  mosueToolPolineDownFlag = false;
  // 绘制marer的时候的配置,在绘制完挂载事件的时候需要使用
  mouseToolMarkerOptions = null;
  // 绘制poline的时候的配置,在绘制完挂载事件的时候需要使用
  mouseToolPolineOptions = null;

  // 存放所有的设备的数组集合,这是一个对象,对象里面是各种设备的数组
  allDevice = {};
  // 存放所有的管道
  pipeArr = {};
  // 当前的infowindow的组件
  infowindowComponent = null;
  // infowindow本身
  infowindow = null;

  constructor(contaienr, config = {}, vue) {
    this.map = new AMap.Map(contaienr, {
      viewMode: "3D",
      center: [114.196007, 38.260288],
      layers: [AMap.createDefaultLayer()], // layers 字段为空或者不赋值将会自动创建默认底图。
      zoom: 14,
      ...config,
    });
    this.vue = vue;
    this.init();
  }

  init() {
    // 地图事件
    this.mapEvent();
    // 手动点线上图准备,编辑模式
    this.mouseAddDevice();
    // this.mouseAddMarker();
    // this.mouseAddPipeline();
  }
  // map的事件监听
  mapEvent() {
    this.map.on("click", () => {
      // mousetool对象画出的对象的操作
      // 如果有手动绘制对象,要手动清楚一下,因为画线的时候不好清除旧线,这其实是用来清楚旧线的
      // 当画出来的线被mousedown,不删除,但是mouseToolPipeLineFlag要归位,在移出线的时候统一归位
      // 如果对象是marker,直接删除
      if (this.nowMouseTarget?.type == "AMap.Marker") {
        this.mouseToolDrawClear();
      } else {
        // 当地图上已经画了线,并且没有点在线上,询问是否删除
        if (this.nowMouseTarget && !this.mosueToolPolineDownFlag) {
          this.confirm("是否重新绘制管道", { type: "warning" })
            .then(() => {
              // 删除原来的线
              this.mouseToolDrawClear();
              // 鼠标事件开启,并且赋值原来的属性,this.mouseToolMarkerOptions是开启绘制的时候记录的
              this.mousetool.polyline(this.mouseToolPolineOptions);
            })
            .catch(() => {});
        }
      }
    });
    this.map.on("moveend", () => {
      console.log("地图停止移动");
      if (this.flag) {
        console.log("弹框");
        this.flag = false;
      }
    });
    window.panTo = () => {
      this.flag = true;
      this.map.panTo([116.428285, 39.886129]);
    };
  }
  // 弹框工具
  confirm(message, obj) {
    return this.vue.$confirm(message, obj);
  }

  // 改变操作状态
  changeControl(num) {
    this.control = num;
  }

  // 点线编辑上图准备
  mouseAddDevice() {
    this.map.plugin(["AMap.MouseTool"], () => {
      this.mousetool = new AMap.MouseTool(this.map);
    });
    // 挂载绘制结束的事件
    this.mouseDrawEvent();
  }

  // 点或者线上图结束后触发的事件
  mouseDrawEvent() {
    this.mousetool.on("draw", (e) => {
      const target = e.obj;
      // console.log([target._position.lng, target._position.lat]);
      const { type: targetType } = target;
      // 当这个点是marker的时候
      if (targetType == "AMap.Marker") {
        this.mouseToolMarkerEvent(target);
      } else {
        // 如果是线,挂上编辑
        this.lineEditor(target);
        this.mousetool.close();
        this.mouseToolPolineEvent(target);
        console.log(targetType, "当前对象是管道");
      }
      this.nowMouseTarget = target;
    });
  }

  // 绘制marker结束后,在marker身上添加的事件
  mouseToolMarkerEvent(target) {
    // 由于画出来的marker点击会换位置,所以当移入的时候删除绘制事件,移出去在增加绘制事件
    target.on("mouseover", (e) => {
      // 鼠标事件关闭
      this.mousetool.close(false);
    });
    target.on("mouseout", (e) => {
      // 这里不方便获取原来的属性,因为position不好解决,还是设置一个值吧
      // 鼠标事件开启,并且赋值原来的属性,this.mouseToolMarkerOptions是开启绘制的时候记录的
      this.mousetool.marker(this.mouseToolMarkerOptions);
    });
    // 点
    target.on("click", (e) => {
      // 弹框
    });
  }
  // 挂上线以及线的事件
  lineEditor(line) {
    // line.editor && line.editor.close();
    // 当前点击次数,1次为编辑,2次为弹框
    line.editorNum = 0;
    line.editor = new AMap.PolyEditor(this.map, line);
  }
  // 绘制管道的时候,挂载的事件
  mouseToolPolineEvent(target) {
    // 线按下的时候会变成编辑,mousetool事件会清空 移出线的时候 在把polyline事件加上
    target.on("mouseover", (e) => {
      // 鼠标事件关闭
      // this.mousetool.close(false);
    });
    target.on("mouseout", (e) => {
      // 有时候按在线上移动地图,map点击事件中mosueToolPolineDownFlag无法归位,在这里归位
      this.mosueToolPolineDownFlag = false;
      // 鼠标事件开启,并且赋值原来的属性,this.mouseToolMarkerOptions是开启绘制的时候记录的
      // this.mousetool.polyline(this.mouseToolPolineOptions);
    });
    // 线
    target.on("mousedown", (e) => {
      const line = e.target;
      // mosueTool按下的flag,按在线上,不询问是否删除
      this.mosueToolPolineDownFlag = true;
      // 按下的时候要关闭事件
      this.mousetool.close(false);
      // 如果当前状态不是编辑,则进入编辑状态
      if (line.editorNum < 1) {
        // 打开并且++
        line.editor.open();
        line.editorNum++;
      } else {
        // 这里就要弹框了
        console.log(line.getPath());
      }
    });
  }
  // 设备点击上图开启
  mouseAddMarker(markerObj = {}) {
    // 清空已经绘制完的对象
    this.mousetoolClose(true);
    // 记录一下配置项,在挂载点击的时候,需要使用
    this.mouseToolMarkerOptions = {
      draggable: true,
      ...markerObj,
    };
    this.mousetool.marker(this.mouseToolMarkerOptions);
  }
  // 管线点击上图开启
  mouseAddPipeline(pipeObj = {}) {
    this.mousetoolClose(true);
    // 开始画线
    this.mosuetoolPolineFlag = true;
    this.mouseToolPolineOptions = {
      strokeWeight: 5,
      ...pipeObj,
    };
    this.mousetool.polyline(this.mouseToolPolineOptions);
  }

  // 手动清除map上绘制的对象
  mouseToolDrawClear() {
    if (this.nowMouseTarget) {
      this.map.remove(this.nowMouseTarget);
      // 如果有editor,则关闭
      this.nowMouseTarget.editor && this.nowMouseTarget.editor.close();
      this.nowMouseTarget = null;
    }
  }
  // 关闭点击上图事件 true清除之前绘制的图像,false 仅关闭上图事件
  mousetoolClose(boolean) {
    //  清空地图上的绘制对象的同时,也要清楚这个nowMouseTarget控制对象
    if (this.nowMouseTarget) {
      // 如果有editor,则关闭
      this.nowMouseTarget.editor && this.nowMouseTarget.editor.close();
      this.nowMouseTarget = null;
    }
    this.mousetool.close(boolean);
  }

  /**
   *
   *
   *
   *
   *
   *
   * 地图上add设备
   *
   *
   * @description:
   * @param {*} deviceData marker的数据
   * @param {*} compontent marker点击弹出的infowindow的组件
   * @return {*}
   */
  addDevice(deviceData, compontent) {
    const { longitude: lng, latitude: lat, iconType } = deviceData;
    const icon = svgUrl[iconType];
    let device = this.createMarker({
      map: this.map,
      anchor: "bottom-center",
      icon,
      position: [lng, lat],
      extData: deviceData,
    });
    // device.hide();
    // 如果没有有这个公司的数组,就创建,有就直接push
    if (!Array.isArray(this.allDevice[iconType])) {
      this.allDevice[iconType] = [];
    }
    this.allDevice[iconType].push(device);

    // 设备的事件函数
    this.deviceEvent(device, compontent);
  }
  deviceEvent(device, compontent) {
    device.on("click", (e) => {
      const target = e.target;
      // 如果control==0就是默认值,没有使用123功能,就显示infowindow
      if (this.control == 0) {
        this.markerClick(target, compontent);
      } else if (this.control == 2) {
        // 2是已经上图的设备拥有的编辑功能
      } else if (this.control == 3) {
        // 3是删除操作
      }
    });
  }
  /** 点击marker出现infowindow
   * @description:
   * @param {*} target 点击的对象
   * @param {*} compontent marker点击弹出的infowindow的组件
   * @return {*}
   */
  markerClick(target, compontent) {
    const deviceExtData = target.getExtData();

    const { longitude: lng, latitude: lat } = deviceExtData;

    // 创建一个可以控制的组件,将其dom插入infowindow
    this.infowindowComponent = this.createInfowindowDom(
      this.vue,
      this,
      deviceExtData,
      compontent
    );

    // 没恩么用,控制台测试的时候用着玩的
    window.func = () => {
      const { longitude: lng, latitude: lat } = target.getExtData();
      const cd = {
        id: 1,
        name: "9",
        lng,
        lat,
      };
      this.infowindowComponentChange(cd);
      target.setExtData(cd);
    };
    this.infowindow = new AMap.InfoWindow({
      isCustom: true,
      content: this.infowindowComponent.$el,
      position: [lng, lat],
      // anchor: "top-left",
      // offset: [20, -45],
      anchor: "middle-left",
      offset: [20, -10],
    });

    this.infowindow.open(this.map);
  }
  // 创建要加入到infowindow里的
  createInfowindowDom(vueRoot, mapClass, deviceData, compontent) {
    const Component = this.vue.$Vue.extend(compontent, {});
    return new Component({
      data() {
        return {
          // 当前vue实例
          vueRoot,
          // 自己写的map类
          mapClass,
          // 数据
          deviceData,
        };
      },
    }).$mount();
  }
  // 当前显示的infowindow内部的数据发生变化,一般在socket传回数据的时候使用
  infowindowComponentChange(data) {
    this.infowindowComponent.deviceData = data;
  }
  // 创建marker
  createMarker(MarkerOptions) {
    return new AMap.Marker(MarkerOptions);
  }
  // 地图上add管道
  addPipeLine(objData, component) {
    const { path, pipePressure, iconType } = objData;
    console.log(path);
    // 根据压力获取颜色
    const color = pipeColor[pipePressure + 1];
    const pipe = this.createPipeLine({
      path: eval(path),
      strokeWeight: 4,
      strokeColor: color,
      extData: objData,
      cursor: "pointer",
    });

    this.map.add(pipe);
    console.log(pipe.getExtData());
    // pipe.hide();
    if (!Array.isArray(this.pipeArr[iconType])) {
      this.pipeArr[iconType] = [];
    }
    this.pipeArr[iconType].push(pipe);
    // console.log(this.pipeArr);
    // this.map.panTo([path[0][0], path[0][1]]);
    this.pipeEvent(pipe, component);
  }
  createPipeLine(pipeLineOptions) {
    return new AMap.Polyline(pipeLineOptions);
  }
  pipeEvent(pipe, compontent) {
    pipe.on("mouseover", (e) => {
      const target = e.target;
      // 获取当前颜色
      const options = target.getOptions();
      options.strokeColor = "blue";
      target.setOptions(options);
    });

    pipe.on("mouseout", (e) => {
      const target = e.target;
      // 根据管道压力获取颜色
      const { pipePressure } = target.getExtData();
      const color = pipeColor[pipePressure + 1];
      // 获取当前颜色
      const options = target.getOptions();
      options.strokeColor = color;
      target.setOptions(options);
    });
    pipe.on("click", (e) => {
      const target = e.target;
      target.lnglat = e.lnglat;
      // 如果control==0就是默认值,没有使用123功能,就显示infowindow
      if (this.control == 0) {
        this.pipeClick(target, compontent);
      } else if (this.control == 2) {
        // 2是已经上图的设备拥有的编辑功能
      } else if (this.control == 3) {
        // 3是删除操作
      }
    });
  }

  pipeClick(target, compontent) {
    const deviceExtData = target.getExtData();
    const { lng, lat } = target.lnglat;
    // 创建一个可以控制的组件,将其dom插入infowindow
    this.infowindowComponent = this.createInfowindowDom(
      this.vue,
      this,
      deviceExtData,
      compontent
    );
    // 没恩么用,控制台测试的时候用着玩的
    // window.func = () => {
    //   const { lng, lat } = target.getExtData();
    //   const cd = {
    //     id: 1,
    //     name: "9",
    //     lng,
    //     lat,
    //   };
    //   this.infowindowComponentChange(cd);
    //   target.setExtData(cd);
    // };
    this.infowindow = new AMap.InfoWindow({
      isCustom: true,
      content: this.infowindowComponent.$el,
      position: [lng, lat],
      // anchor: "top-left",
      // offset: [20, -15],
      anchor: "middle-left",
      offset: [20, 0],
    });
    this.infowindow.open(this.map);
  }
  infowindowClose() {
    if (!this.infowindow) return;
    this.infowindow.close();
  }

  allilter(companyArr, typeArr) {
    for (let pipeItem in this.pipeArr) {
      this.pipeArr[pipeItem].forEach((pipe) => {
        const data = pipe.getExtData();
        if (companyArr.indexOf(data.companyType + "") >= 0) {
          pipe.show();
        } else {
          pipe.hide();
        }
      });
    }
    for (let deviceItem in this.allDevice) {
      this.allDevice[deviceItem].forEach((device) => {
        const data = device.getExtData();
        // 设备过滤受到bigwindow页面的的两种制约,companyArr, typeArr 两个数组制约显示隐藏
        // 燃气没有公司,所以没有device.companyType不收到公司的控制
        const companyHas = companyArr.indexOf(data.companyType + "") >= 0;
        // 设备存在
        const deviceHas = typeArr.indexOf(data.iconType + "") >= 0;
        // 必须设备存在数组里,才会显示设备 !data.companyType代表用户不受公司制约
        if (deviceHas && (companyHas || !data.companyType)) {
          device.show();
        } else {
          device.hide();
        }
      });
    }
  }
  // 根据公司过滤是否显示 公司由一个数组传过来
  companyFilter(companyArr) {
    for (let pipeItem in this.pipeArr) {
      this.pipeArr[pipeItem].forEach((pipe) => {
        const data = pipe.getExtData();
        if (companyArr.indexOf(data.companyType + "") >= 0) {
          pipe.show();
        } else {
          pipe.hide();
        }
      });
    }
    for (let deviceItem in this.allDevice) {
      this.allDevice[deviceItem].forEach((device) => {
        const data = device.getExtData();

        // 燃气没有公司,所以没有device.companyType不收到公司的控制
        const companyHas = companyArr.indexOf(data.companyType + "") >= 0;

        // 必须设备存在数组里,才会显示设备 !data.companyType代表用户不受公司制约
        if (companyHas || !data.companyType) {
          device.show();
        } else {
          device.hide();
        }
      });
    }
  }

  // 根据设备类型隐藏展示
  iconTypeFilter(typeArr) {
    for (let deviceItem in this.allDevice) {
      this.allDevice[deviceItem].forEach((device) => {
        if (typeArr.indexOf(deviceItem + "") >= 0) {
          device.show();
        } else {
          device.hide();
        }
      });
    }
  }
  // 公司改变,先调用设备方法,在调用公司方法
  companyChange(companyArr, typeArr) {
    this.iconTypeFilter(typeArr);
    this.companyFilter(companyArr);
  }
  // 设备选择,先调用公司方法,在调用设备方法覆盖
  deviceChange(companyArr, typeArr) {
    this.companyFilter(companyArr);
    this.iconTypeFilter(typeArr);
  }
  // 卫星图切换
  satellite = null;
  changeMap(bool) {
    // 卫星,
    if (!bool) {
      if (this.satellite) return;
      this.satellite = new AMap.TileLayer.Satellite();
      this.map.addLayer(this.satellite);
    } else {
      if (this.satellite) {
        this.map.removeLayer(this.satellite);
        this.satellite = null;
      }
    }
  }
}