<script setup lang='ts'>
import {
  Viewer, ScreenSpaceEventType, Cartesian3,
  Math as CMath, Color, EventHelper, UrlTemplateImageryProvider,
  DistanceDisplayCondition, ScreenSpaceEventHandler, WebMercatorTilingScheme,
  VerticalOrigin, HeightReference, Cartesian2, Cartographic, GeoJsonDataSource, SceneMode, WebMapTileServiceImageryProvider, GeographicTilingScheme
} from 'cesium'
import {
  computed, onMounted, onUnmounted, ref, watch
} from 'vue'
// import useStore from '@/pages/store/index'
import lnglatIcon from '@/assets/icons/lnglat.svg'
import camera from '@/assets/icons/camera.svg'
import broadcast from '@/assets/icons/broadcast.svg'
import preBranke from '@/assets/icons/preBranke.svg'
import carBranke from '@/assets/icons/carBranke.svg'
import { NInput, useNotification, useDialog } from 'naive-ui'
import EquipmentService, { TreeList, Device } from '@/services/equipmentTree.service'

const notification = useNotification()
const dialog = useDialog()
const equipmentService = new EquipmentService()
// const store = useStore()
const props = defineProps({
  type: { default: true, type: Boolean },
  checkList: { default: [], type: Array<TreeList> },
  treeList: { default: [], type: Array<TreeList> }
})
const isAdmin = computed(() => props.type)
let viewer = null as Any
let Helper = new EventHelper() as Any
let handler = null as Any
const currenIcon = ref({ icon: '', x: 0, y: 0 })
const icontypes = [
  {
    name: '摄像机', type: 'camera', icon: camera, deviceType: 0
  },
  {
    name: '广播', type: 'broadcast', icon: broadcast, deviceType: 1
  },
  {
    name: '人行闸机', type: 'preBranke', icon: preBranke, deviceType: 2
  },
  {
    name: '车行闸机', type: 'carBranke', icon: carBranke, deviceType: 3
  }
]
const modelShow = ref(false)
const model = ref({
  deviceId: '',
  itemId: -1,
  longitude: '',
  latitude: '',
  describeData: '',
  selfId: '',
  deviceType: 0
})
const rules = {
  deviceId: [ { required: true, message: '请输入设备ID' } ],
  itemId: [ { required: true, message: '请选择项目' } ],
  longitude: [ { required: true, message: '请输入经度' } ],
  latitude: [ { required: true, message: '请输入维度' } ],
  describeData: [ { required: true, message: '请输入安装位置描述' } ]
}
const options = ref([] as Array<TreeList>)

const formRef = ref()
const isAdd = ref(false)

/**
 * 添加路径
 * @param lngLat 经纬度数组
 * @param displayMin 最小显示范围
 * @param displayMax 最大显示范围
 */
function addLine(lngLat: Array<number>, displayMin?: number, displayMax?: number) {
  if (!viewer) return
  viewer.entities.add({
    position: Cartesian3.fromDegrees(lngLat[0], lngLat[1]),
    polyline: {
      positions: Cartesian3.fromDegreesArray(lngLat),
      width: 5,
      material: Color.fromCssColorString('#55F8F8'),
      clampToGround: true,
      distanceDisplayCondition: new DistanceDisplayCondition(displayMin, displayMax)
    }
  })
}

/**
 * 添加label
 * @param lng 经度
 * @param lat 纬度
 * @param icon 图标
 * @param text 文本内容
 * @param displayMin 最小显示范围
 * @param displayMax  最大显示范围
 */
function addLable(lng: number, lat: number, icon: string, text: string, displayMin?: number, displayMax?: number, oldData?: Device) {
  if (!viewer) return
  viewer.entities.add({
    position: Cartesian3.fromDegrees(lng, lat),
    billboard: {
      image: icon || lnglatIcon,
      width: 25,
      height: 25,
      verticalOrigin: VerticalOrigin.BOTTOM,
      disableDepthTestDistance: 21618529,
      heightReference: HeightReference.CLAMP_TO_GROUND,
      distanceDisplayCondition: new DistanceDisplayCondition(displayMin, displayMax)
    },
    label: {
      text,
      font: '12px MicrosoftYaHei',
      backgroundColor: Color.fromCssColorString('#0F2830'),
      showBackground: true,
      fillColor: Color.WHITE,
      pixelOffset: new Cartesian2(0, -40),
      disableDepthTestDistance: 21618529,
      heightReference: HeightReference.CLAMP_TO_GROUND,
      distanceDisplayCondition: new DistanceDisplayCondition(displayMin, displayMax)
    },
    oldData
  })
}

/**
 * 加载geojson
 * @param path 文件路径
 * @param callback
 * @param displayMin
 * @param displayMax
 */
function loadGeoJson(path:string, displayMin?: number, displayMax?: number) {
  GeoJsonDataSource.load(path, {
    stroke: Color.fromCssColorString('#55F8F8'),
    strokeWidth: 3,
    clampToGround: false,
    fill: Color.fromCssColorString('rgba(0,0,0,0)')
  }).then((res) => {
    const entities = res.entities.values
    // console.log(55555, entities)
    // for (let k = 0; k < entities.length; k++) {
    //   const el = entities[k]
    //   const polygon = el.polygon as Any
    //   const polyline = el.polyline as Any
    //   const billboard = el.billboard as Any
    //   if (polygon || polyline || el.billboard) {
    //     if (polygon) polygon.distanceDisplayCondition = new DistanceDisplayCondition(displayMin, displayMax)
    //     if (polyline) polyline.distanceDisplayCondition = new DistanceDisplayCondition(displayMin, displayMax)
    //     if (billboard) billboard.distanceDisplayCondition = new DistanceDisplayCondition(displayMin, displayMax)
    //   }
    // }
    if (viewer) {
      viewer.dataSources.add(res)
      viewer.scene.requestRender()
    }
  })
}

/**
 * 移动
 * @param lng
 * @param lat
 * @param profundity 高度
 */
function flyTo(lng: number, lat: number, profundity = 19633) {
  if (!viewer) return
  viewer.camera.flyTo({
    destination: Cartesian3.fromDegrees(lng, lat * 0.997, profundity),
    orientation: {
      heading: CMath.toRadians(0.0),
      pitch: CMath.toRadians(-60.0),
      roll: CMath.toRadians(0.0)
    }
  })
}

/**
 * 屏幕坐标转经纬度
 * @param position xy
 */
function XYToLngLat(position: { x: number, y: number }) {
  // 二维屏幕坐标转为三维笛卡尔空间直角坐标(世界坐标)
  const cartesian3 = viewer.scene.globe.pick(viewer.camera.getPickRay(position), viewer.scene)
  // 第一步:笛卡尔空间直角坐标系转为地理坐标(弧度制)
  const cartographic = Cartographic.fromCartesian(cartesian3)
  // 第二步: 地理坐标(弧度制) 转为经纬度坐标
  const lat = CMath.toDegrees(cartographic.latitude)
  const lng = CMath.toDegrees(cartographic.longitude)
  return { lng, lat }
}

// 初始化移动添加标点
function initMove() {
  // 添加标点
  const dom = document.querySelector('.maps') as HTMLElement
  dom.onmousedown = (e: Any) => {
    const item = e.target.parentElement.dataset.type
    if (item) {
      const isNoPro = props.checkList.find((el) => el.lastData === 0 || !el.lastData)
      if (isNoPro || !props.checkList.length || modelShow.value || props.checkList.length > 1) {
        let txt = ''
        if (isNoPro) txt = '请选择项目'
        if (!props.checkList.length) txt = '请先选择项目'
        if (modelShow.value) txt = '请完成你的操作再来！'
        if (props.checkList.length > 1) txt = '请不要勾选多个项目'
        notification.warning({
          content: txt,
          duration: 3000
        })
        return
      }
      let ismove = false
      modelShow.value = false
      dom.onmousemove = (em) => {
        // 移动样式
        if (!ismove) ismove = true
        if (currenIcon.value.icon !== item) currenIcon.value.icon = item
        currenIcon.value.x = em.x - 15 + window.scrollX
        currenIcon.value.y = em.y - 100 + window.scrollY
      }
      dom.onmouseup = (eu) => {
        currenIcon.value = { icon: '', x: 0, y: 0 }
        const { lng, lat } = XYToLngLat({ x: eu.x + window.scrollX, y: eu.y - 74 + window.scrollY })
        const items = icontypes.find((el) => el.type === item)
        if (ismove) {
          addLable(lng, lat, items?.icon!, items?.name!, 1, 16219740)
          model.value = {
            deviceId: '',
            itemId: +props.checkList[0].id,
            longitude: lng.toString(),
            latitude: lat.toString(),
            describeData: '',
            selfId: '',
            deviceType: icontypes.filter((el) => el.type === item)[0].deviceType
          }
          modelShow.value = true
          isAdd.value = true
        }
        // flyTo(lng, lat)
        viewer.scene.requestRender()
        console.log('经纬度', lng, lat, props.checkList, model.value)

        dom.onmousemove = null
        dom.onmouseup = null
        ismove = false
      }
    }
  }
}

// 初始化
async function initMap(callback:()=>void) {
  // store.setLoading(true)
  // init
  viewer = new Viewer('cesiumContainer', {
    baseLayerPicker: false,
    geocoder: false,
    infoBox: false,
    homeButton: false,
    sceneModePicker: false,
    navigationHelpButton: false,
    animation: false,
    creditContainer: 'cesiumContainer',
    timeline: false,
    fullscreenButton: false,
    vrButton: false,
    requestRenderMode: true,
    scene3DOnly: true,
    selectionIndicator: false,
    navigationInstructionsInitiallyVisible: false
    // sceneMode: SceneMode.COLUMBUS_VIEW
  })
  // 增加地形属性
  viewer.scene.globe.depthTestAgainstTerrain = true
  // 缩放范围
  viewer.scene.screenSpaceCameraController.minimumZoomDistance = 100
  viewer.scene.screenSpaceCameraController.maximumZoomDistance = 7546388
  // viewer.imageryLayers.removeAll(true)
  // viewer.imageryLayers.get(0).show = false// 不显示底图
  // viewer.scene.globe.baseColor = Color.fromCssColorString('#07101a') // 设置地球颜色07101a
  viewer.imageryLayers.addImageryProvider(new UrlTemplateImageryProvider({
    url: 'https://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8'
  }))
  // loadGeoJson('are.json', 1, 7546388)
  // loadGeoJson('road.json', 1, 7546388)
  // 加载图层
  // viewer.imageryLayers.addImageryProvider(new UrlTemplateImageryProvider({
  //   url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
  //   subdomains: [ '0', '1', '2', '3' ],
  //   tilingScheme: new WebMercatorTilingScheme(),
  //   maximumLevel: 14
  // }))

  // 加载地形
  // viewer.terrainProvider = await (CesiumTerrainProvider as any).fromUrl('https://www.supermapol.com/realspace/services/3D-stk_terrain/rest/realspace/datas/info/data/path', {
  //   requestMetadata: true,
  //   requestVertexNormals: true,
  //   requestWaterMask: true
  // })

  viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.LEFT_DOUBLE_CLICK) // 取消原双击事件
  viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(ScreenSpaceEventType.LEFT_CLICK) // 取消原单击事件

  // viewer.camera.changed.addEventListener(() => {
  //   // 打印中心点坐标、高度
  //   const { height } = viewer.scene.globe.ellipsoid.cartesianToCartographic(viewer.camera.position)
  //   console.log(999999, height)
  // })

  // 重新注册点击事件
  handler = new ScreenSpaceEventHandler(viewer.scene.canvas)

  // 右键还原
  handler.setInputAction(() => {
    viewer.camera.flyTo({
      destination: Cartesian3.fromDegrees(101.84599, 30.04260, 7546388),
      orientation: {
        heading: CMath.toRadians(0.0),
        pitch: CMath.toRadians(-85.0),
        roll: CMath.toRadians(0.0)
      }
    })
  }, ScreenSpaceEventType.RIGHT_CLICK)

  // 点击左键
  handler.setInputAction((movement: Any) => {
    const pick = viewer.scene.pick(movement.position)
    if (!isAdmin.value || !pick) return
    if (isAdd.value) {
      notification.warning({
        content: '请完成你的操作再来！',
        duration: 3000
      })
      return
    }
    console.log('点击详情', pick.id)
    isAdd.value = false
    const { oldData } = pick.id
    model.value = { ...oldData, selfId: pick.id.id }
    modelShow.value = true
  }, ScreenSpaceEventType.LEFT_CLICK)

  // 监听加载结束
  Helper.add(viewer.scene.globe.tileLoadProgressEvent, (e: any) => {
    if (e === 0) {
      viewer.camera.flyTo({
        destination: Cartesian3.fromDegrees(101.84599, 30.04260, 7546388),
        orientation: {
          heading: CMath.toRadians(0.0),
          pitch: CMath.toRadians(-85.0),
          roll: CMath.toRadians(0.0)
        }
      })
      // store.setLoading(false)
      Helper.removeAll()
      // 初始化添加标点
      callback()
    }
  })
}

function close() {
  if (isAdd.value) {
    const { values } = viewer.entities
    if (values.length) viewer.entities.removeById(values[values.length - 1].id)
    viewer.scene.requestRender()
    isAdd.value = false
  }
  modelShow.value = false
}

async function submit() {
  await formRef.value.validate()
  // 提交数据
  const msg = isAdd.value ? await equipmentService.addDevice({ ...model.value }) : await equipmentService.upDevice({ ...model.value })
  notification.success({
    content: msg,
    duration: 3000
  })
  // 属性赋值
  const { values } = viewer.entities
  values[values.length - 1].oldData = { ...model.value, selfId: values[values.length - 1].id }
  modelShow.value = false
  isAdd.value = false
}

async function del() {
  dialog.info({
    title: '你确定要删除当前设备？',
    positiveText: '确定',
    onPositiveClick: async () => {
      const msg = await equipmentService.delDevice({ ...model.value })
      notification.success({
        content: msg,
        duration: 3000
      })
      viewer.entities.removeById(model.value.selfId)
      viewer.scene.requestRender()
      modelShow.value = false
    }
  })
}

/** 递归 */
function recursion(arr: Array<TreeList>, newData: Array<TreeList>) {
  for (let k = 0; k < arr.length; k++) {
    const el = arr[k]
    if (el.lastData === 1 || el.lastData === 0) {
      newData.push({
        name: el.name,
        id: el.id,
        lastData: el.lastData,
        itemLongitude: el.itemLongitude,
        itemLatitude: el.itemLatitude,
        deviceDataList: el.deviceDataList
      })
    }
    if (el.children && el.children.length) recursion(el.children, newData)
  }
}
/** input 正则 */
function onlyAllowNumber(value:string, type:boolean) {
  if (type) {
    return +(value) >= 0 && +(value) <= 180
  }
  return +(value) >= 0 && +(value) <= 90
}
// 选中时
watch(() => props.checkList, (v) => {
  if (!isAdmin.value) return
  if (!v.length) {
    viewer.entities.removeAll()
  } else {
    equipmentService.getDevice(v[0]?.id as number).then((res) => {
      viewer.entities.removeAll()
      for (let k = 0; k < res.length; k++) {
        const el = res[k]
        if (el.del === 1) {
          const items = icontypes.find((es) => es.deviceType === el.deviceType)
          addLable(+(el.longitude!), +(el.latitude!), items!.icon, items!.name, 1, 16219740, el)
        }
      }
      viewer.scene.requestRender()
    })
  }
  viewer.scene.requestRender()
  close()
})

// 树列表
watch(() => props.treeList, (v) => {
  options.value = v
  viewer.entities.removeAll()
  if (isAdmin.value || !v.length) return
  const newData = [] as TreeList[]
  recursion(v, newData)
  for (let k = 0; k < newData.length; k++) {
    const el = newData[k]
    if (el.lastData === 1) {
      // 项目
      if (el.itemLongitude) {
        const lngLat = el.itemLongitude?.split(',')
        addLable(+(lngLat![0]), +(lngLat![1]), lnglatIcon, el.nameReferred || el.name, 1038633, 4938633)
      }
      if (el.deviceDataList && el.deviceDataList.length) {
        // 设备
        for (let j = 0; j < el.deviceDataList.length; j++) {
          const es = el.deviceDataList[j]
          if (es.del === 1) {
            const items = icontypes.find((ed) => ed.deviceType === es.deviceType)
            addLable(+(es.longitude!), +(es.latitude!), items!.icon, items!.name, 1, 938633)
          }
        }
      }
    } else {
      // 公司
    }
  }
  viewer.scene.requestRender()
})

// 修改经纬度=>同步图标
watch(() => [ model.value.longitude, model.value.latitude ], (v) => {
  if (isAdd.value) return
  const ens = viewer.entities.getById(model.value.selfId)
  ens.position = Cartesian3.fromDegrees(+v[0], +v[1])
  setTimeout(() => {
    viewer.scene.requestRender()
  }, 1500)
})

onMounted(() => {
  initMap(() => {
    if (isAdmin.value) initMove()
    // loadGeoJson('geo.json')
    // loadGeoJson('1.geojson')
    // loadGeoJson('2.geojson')
    // loadGeoJson('3.geojson')
    // loadGeoJson('4.geojson')
    // loadGeoJson('5.geojson')
    // loadGeoJson('6.geojson')

    // loadGeoJson('gm.geojson')
    // loadGeoJson('tmy.geojson')
    // loadGeoJson('dd.geojson')
  })
})

onUnmounted(() => {
  // store.setLoading(false)
  if (viewer) {
    if (viewer.entities) viewer.entities.removeAll()
    viewer.destroy()
  }
  viewer = null
  Helper = null
  handler = null
  const dom = (document.querySelector('.maps') as HTMLElement)
  if (dom && dom.onmousedown) dom.onmousedown = null
})
</script>
<template>
  <div class="maps">
    <div id="cesiumContainer" />
    <template v-if="isAdmin">
      <div class="topbar">
        <template
          v-for="(item, index) in icontypes"
          :key="index"
        >
          <div>
            <Icon
              :name="item.type"
              :size="50"
              :data-type="item.type"
            />
            <p>{{ item.name }}</p>
          </div>
        </template>
      </div>
      <Icon
        :name="currenIcon.icon"
        :size="25"
        class="moveIcon"
        :style="`left:${currenIcon.x};top:${currenIcon.y}`"
      />
      <div
        v-if="modelShow"
        class="details"
      >
        <n-card
          style="width: 400px"
          :bordered="false"
          size="huge"
          role="dialog"
          aria-modal="true"
          class="n-modal"
        >
          <n-form
            ref="formRef"
            :model="model"
            :rules="rules"
          >
            <n-form-item
              path="deviceId"
              label="设备ID"
            >
              <n-input
                v-model:value="model.deviceId"
                :readonly="!isAdd"
              />
            </n-form-item>
            <n-form-item
              path="itemId"
              label="所属项目"
            >
              <n-tree-select
                v-model:value="model.itemId"
                :options="options"
                :render-label="(info:any)=>info.option.nameReferred || info.option.name"
                label-field="name"
                key-field="id"
                check-strategy="child"
                clearable
                filterable
              />
            </n-form-item>
            <n-form-item
              path="longitude"
              label="经度"
            >
              <n-input
                v-model:value="model.longitude"
                :allow-input="(e: string) => onlyAllowNumber(e, true)"
              />
            </n-form-item>
            <n-form-item
              path="latitude"
              label="纬度"
            >
              <n-input
                v-model:value="model.latitude"
                :allow-input="(e: string) => onlyAllowNumber(e, false)"
              />
            </n-form-item>
            <n-form-item
              path="describeData"
              label="安装位置描述"
            >
              <n-input
                v-model:value="model.describeData"
                type="textarea"
                size="small"
                :autosize="{
                  minRows: 3,
                  maxRows: 5
                }"
                :maxlength="150"
              />
            </n-form-item>
          </n-form>
          <template #footer>
            <n-button @click="close">
              取消
            </n-button>
            <n-button
              v-if="!isAdd"
              type="error"
              @click="del"
            >
              删除
            </n-button>
            <n-button
              type="primary"
              @click="submit"
            >
              确定
            </n-button>
          </template>
        </n-card>
      </div>
    </template>
  </div>
</template>
<style scoped lang="scss">
.maps {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;

  #cesiumContainer {
    width: 100%;
    height: 100%;
  }

  .topbar {
    position: absolute;
    top: 10px;
    left: 50%;
    width: 1080px;
    height: 104px;
    transform: translate(-50%, 0);
    background: linear-gradient(229deg, rgba(26, 65, 78, 0.85) 0%, rgba(3, 20, 26, 0.95) 100%);
    border: 1px solid rgba(105, 206, 206, 0.8);
    display: flex;
    align-items: center;

    div {
      display: flex;
      flex-flow: column;
      align-items: center;
      width: 65px;
      user-select: none;
      cursor: pointer;
      font-size: 14px;

      &:first-child {
        margin-left: 10px;
      }

      p {
        margin-top: 5px;
        text-align: center;
      }
    }

  }

  .moveIcon {
    position: absolute;
    top: 0;
    left: 0;
  }

  .details {
    position: absolute;
    right: 10px;
    top: 10px;
    min-height: 400px;
  }
}
</style>