<template>
  <div id="scene">
    <div id="cvs" ref="content"></div>
    <!-- 这里的预览模式需要做成不能修改的模式 -->
    <edit-dialog
        ref="edit"
        @addEquipment="handleAddEqu"
        @removeEquipment="handleRemoveEqu"
        @cancel="handleCancel"
        :hasDev="hasDevice"
        :pointNum="pointNum"
        :tunnelId="params.tunnelId"
        :position="targetP?.name"
        :hasEquipment="hasDevice"
        :pointGap="pointGap"
        :form="params.form"
    />
    <pre-dialog
        ref="info"
        :pointNum="50"
        :pointGap="50"
        :equipmentType="equipmentType"
        :equipmentName="equipmentName"
        :equipmentValue="50"
        :devRealtimeDetail="devRealtimeDetail"
    />
  </div>
</template>

<script setup>
import * as three from "three";
import ThreeDScene from "./sceneClass/demo.js";
import DevInfo from "./displayInfoComp/DevInfo.vue";
import EditDialog from "./editEquComp/editDialog.vue";
import PreDialog from "./preEquComp/preDialog.vue";
// 导入模模型加载器
import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader";
// import { DRACOLoader } from "three/examples/jsm/loaders/dracoloader";
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import * as TWEEN from "three/examples/jsm/libs/tween.module";
import {
  CSS2DRenderer,
  CSS2DObject,
} from "three/addons/renderers/CSS2DRenderer.js";
// 引入CSS2渲染器CSS2DRenderer和CSS2模型对象CSS2DObject

import {OBJLoader} from "three/examples/jsm/loaders/OBJLoader";
import {RGBELoader} from "three/examples/jsm/loaders/RGBELoader";
import {
  onMounted,
  reactive,
  ref,
  watch,
  defineProps,
  defineExpose,
} from "vue";
import {ElMessage} from "element-plus";
import {getScreenSimulateTunnel} from "@/api/tunnelManage";
// 获取html标签跟随组件dom
const content = ref(null);
const info = ref(null);
const edit = ref(null);
const realtimeData = ref([]);
let modelList = ref(null);
let demo; //定义demo对象
const loader = new OBJLoader();
let hdrLoader = new RGBELoader();
let backColorSet = three.sRGBEncoding;
const params = defineProps([
  "isedit",
  "tunnelId",
  "tunnelLength",
  "form",
  "deviceData",
  "devRealtimeData",
  "simulateData",
  "tunnelAlias",
]);

// hover到设备回显参数
let equipmentName = ref("");
let equipmentType = ref("");
let pointValue = ref({});
let devRealtimeDetail = ref({
  equipmentType: "",
  value: "",
  unit: "",
});

watch(
    () => params.tunnelLength,
    (now) => {
      params.tunnelLength = now;
      pointGap = now / 20;
    },
    {deep: true}
);

let pointGap = reactive(params.tunnelLength);
onMounted(handleMounted);

// 挂载后回调
async function handleMounted() {
  const doms = [info.value.$el, info.value.$el];
  demo = new ThreeDScene(three, content.value);
  //看是不是预览模式，然后继续相关的操作(会在demo中的初始化中进行)
  // "../../../../public/tunnelModel/chanel-have-wall-now-use.gltf"
  demo.isedit = false;
  const loaded = await demo.loadModel(
      GLTFLoader,
      "/tunnelModel/chanel-have-wall-now-use.gltf"
  );
  demo.addOrbitControls(OrbitControls, true);
  demo.addTween(TWEEN);
  demo.addCSS3Renderer(CSS2DRenderer, CSS2DObject, doms);
  demo.setDistance(10);
  lClickCallback(demo); //绑定左键回调
  rClickCallback(demo); //绑定右键回调

  //加载HDR背景图片
  demo.loadBackground(hdrLoader, backColorSet);

  // 初始化设备模型
  try {
    const map = new Map();
    map.set("equ_fan", await loadModel("/devicesModel/model2.obj"));
    map.set("equ_sensors", await loadModel("/devicesModel/sensors.obj"));
    demo.initDevicesModel(map);

    modelList.value = [
      {
        typeKey: "frequency",
        position: "point_001_tr",
        threshold: "",
        equipmentId: 3048,
        equipmentName: "温度传感器",
        equipmentType: "sensor",
        data: 100, //实时数据
        unit: "℃", //单位
      },
      {
        typeKey: "temperature",
        position: "point_020_tr",
        threshold: "",
        equipmentId: 10,
        equipmentName: "1度传感器",
        equipmentType: "sensor",
        data: 10, //实时参数
        unit: "%", //单位
      },
    ];

    // 初始化仿真參數
    const {data} = await getScreenSimulateTunnel(params.tunnelId);
    demo.enableLeftBtn = false; //关闭左键
    demo.pointsVisible(false); //隐藏附着点
    TunnelSceneSimulateRender(data);
    // 初始化标牌信息
    renderRoadPlane({nickname: params.tunnelAlias, length: params.tunnelLength});
  } catch (err) {
  }
}

// 从新渲染数据
async function rerender() {
  try {
    // 初始化仿真參數
    const {data} = await getScreenSimulateTunnel(params.tunnelId);
    console.log(data);
    const modeData = randomPosition([
      ...data.frequencyChangerList,
      ...data.windPressureSensorList,
      ...data.sensorList,
    ]).map((item) => ({
      ...item,
      equipmentType: item.equipmentType.startsWith("frequency")
          ? "frequency"
          : "sensor",
    }));
    demo.editTunnelInit(modeData);
    demo.SignsInf(params.form.tunnelAlias, String(params.tunnelLength));
  } catch (error) {
    ElMessage({
      message: "获取仿真数据异常！",
      type: "warning",
    });
  }
}

// 随机生成1-20的随机数
function randomNum(max = 20) {
  const nums = new Set();
  while (nums.size < max) {
    nums.add(Math.floor(Math.random() * max + 1).toString());
  }
  return [...nums];
}

//随机定位
function randomPosition(simulateData) {
  const mid = Math.floor(simulateData.length / 2);
  const lrIndex = Math.floor(Math.random() * 2);
  const lr_position = ["tl", "tr"];
  const leftPosition = randomNum();
  const rightPosition = randomNum();
  const leftData = simulateData.slice(0, mid).map((item, index) => ({
    ...item,
    position: `point_0${leftPosition[index].padStart(2, 0)}_${
        lr_position[lrIndex]
    }`,
  }));
  const rightData = simulateData
      .slice(mid, simulateData.length)
      .map((item, index) => ({
        ...item,
        position: `point_0${rightPosition[index].padStart(2, 0)}_${
            lr_position[1 - lrIndex]
        }`,
      }));
  return [...leftData, ...rightData];
}

// 每个模型加载回调
function loadModel(path) {
  return new Promise((resolve, reject) => {
    loader.load(
        path,
        (obj) => {
          resolve(obj);
        },
        (xhr) => {
        },
        (err) => {
          reject(err);
        }
    );
  });
}

let hasDevice = ref(true);
let devInfo = reactive({
  meshId: null,
  name: "无",
  state: "无",
  position: "无",
});

// 对象建构赋值功能
function editDevInfo(
    value = {
      meshId: null,
      name: "无",
      state: "无",
      position: "无",
    }
) {
  devInfo.meshId = value.meshId;
  devInfo.name = value.name;
  devInfo.state = value.state;
  devInfo.position = value.position;
}

//左键/双击左键回调函数
function lClickCallback(demo) {
  // demo动态添加函数，为操作组件内部
  function displayDevInfo(targetPoint = null) {
    hasDevice.value = targetPoint.hasDevice;
    if (!targetPoint.info) {
      editDevInfo();
      return;
    }
    editDevInfo(targetPoint.info);
  }

  // 传给内部使用
  demo.addFunction("displayDevInfo", displayDevInfo);
}

let pointNum = ref(0);
let targetP = ref({});

// 右键点击附着点后调函数
function rClickCallback(demo) {
  function editDev(targetPoint = null) {
    hasDevice.value = targetPoint.hasDevice;
    targetP.value = targetPoint;
    pointNum.value = Number(
        targetPoint.name.substring(
            targetPoint.name.indexOf("_") + 1,
            targetPoint.name.lastIndexOf("_")
        ) - 1
    );
    if (!targetPoint.info) return;
    pointValue.value = targetPoint.info
    hoverDevEquipmentCallback(targetPoint.info);
    editDevInfo(targetPoint.info);
  }

  demo.addFunction("editDev", editDev);
}

// 场景添加设备
function handleAddEqu(formInfo) {
  demo.addEquipment(targetP.value, formInfo);
  // //将墙壁设置为可以穿透点击
  for (let line = 1; line <= 20; line++) {
    if (line < 10) {
      let wall = "wall_" + "0" + line;
      this.scene.getObjectByName(wall).layers.set(0);
    } else if (line >= 10) {
      let wall = "wall_" + line;
      this.scene.getObjectByName(wall).layers.set(0);
    }
  }
}

const centerDialogVisible = ref(false);

// 删除设备
function handleRemoveEqu() {
  if (!targetP.value.hasDevice) {
    ElMessage({
      message: "该点位不存在设备！",
      type: "warning",
    });
    return;
  }
  centerDialogVisible.value = true;
}

// 处理取消关闭编辑框事件
function handleCancel() {
  if (!demo) return;
  // 关闭标签
  demo.isControlOrbit(true);
  demo._resetState();
  demo.clearTagsObj();
}

//现在首先有二种方案，是写在TunnelScene.vue中还是demo.js中呢？
//我认为可能看数据在哪获取？到时候看在哪里导入，首先我们放在TunnelScene里面吧
//因为没获取到接口，我们先写死！！！
//需要参考接口的数据结构！！！，主要是传入函数的参数应该是什么结构？？？
const ThreeConfig = {
  code: 0,
  data: {
    tunnelThreeConfig: [
      {
        equipmentId: "fan_01", //传感器id
        equipmentName: "01", //设备名称
        pointName: "point_005_tl", //附着点名称(定位)
        equipmentType: "fan", //设备类型(类型可根据后端
        equipmentValue: 23, //设备存的值
      },
      {
        equipmentId: "sensors_01", //传感器id
        equipmentName: "01", //设备名称
        pointName: "point_009_bl", //附着点名称(定位)
        equipmentType: "sensors", //设备类型(类型可根据后端
        equipmentValue: 67, //设备存的值
      },
    ],
  },
  msg: "dda",
};

//=============隧模型道数据仿真====================
/**
 * @description: 隧道中的设备重新模拟渲染
 * @param {Object} data 渲染数据格式 [{equipmentId,equipmentName,pointName,equipmentType,equipmentValue}]
 */
function TunnelSceneSimulateRender(data) {
  if (!demo) return;
  const modeData = randomPosition([
    ...data.frequencyChangerList,
    ...data.windPressureSensorList,
    ...data.sensorList,
  ]).map((item) => ({
    ...item,
    equipmentType: item.equipmentType.startsWith("frequency")
        ? "frequency"
        : "sensor",
  }));
  demo.editTunnelInit(modeData); //渲染设备数据
}

/**
 * @description: 鼠标悬浮在设备上时回调函数
 */
function hoverDevEquipmentCallback(targetPointInfo) {
  const {
    equipmentId,
    equipmentName: eName,
    equipmentType: eType,
    typeKey,
    unit: u,
    value
  } = targetPointInfo;
  //请求到的实时数据进行渲染
  //业务代码....获取设备实时数据
  equipmentType.value = eType;
  equipmentName.value = eName;
  devRealtimeDetail.value.equipmentType = typeKey;
  devRealtimeDetail.value.unit = u;
  devRealtimeDetail.value.value = value; //1000替换为value
}

/**
 * @description: 渲染隧道入口提示板信息
 * @param {Object} data 隧道信息 {nickname,length}
 */
function renderRoadPlane(data) {
  if (!demo) return;
  const {nickname, length} = data; //隧道名称，长度
  demo.SignsInf(nickname, length + "");
}

const changePointValue = (item) => {
  if (item.equipmentId == pointValue.value.equipmentId) {
    pointValue.value.value = item.value
  }
  hoverDevEquipmentCallback(pointValue.value)
}
defineExpose({
  rerender, TunnelSceneSimulateRender, changePointValue
});
</script>

<style lang="scss" scoped>
#scene {
  position: relative;
  height: 100%;
  width: 100%;

  #cvs {
    height: 100%;
  }
}

#remove-title {
  height: 42px;
  font-size: 32px;
  font-family: MicrosoftYaHei, MicrosoftYaHei;
  font-weight: bold;
  color: #08b7b8;
  line-height: 42px;
  letter-spacing: 3px;
  text-align: center;
  margin: 65px 0px 70px 0px;
}

.btn {
  display: flex;
  justify-content: center;
  gap: 80px;

  :nth-child(1) {
    width: 190px;
    height: 60px;
    border-radius: 11px;
    border: 2px solid #0f82af;
    font-size: 28px;
    font-family: MicrosoftYaHei;
    color: #08b7b8;
    line-height: 37px;
    background: transparent;
  }

  :nth-child(2) {
    width: 190px;
    height: 60px;
    background: #08b7b8;
    border-radius: 11px;
    font-size: 28px;
    font-family: MicrosoftYaHei, MicrosoftYaHei;
    font-weight: bold;
    color: #ffffff;
    line-height: 37px;
  }
}
</style>
