<template>
  <div
    id="tracking-map"
    ref="mapContainer"
    style="width: 100%; height: 448px"
    class="tracking-map"
    :class="{ 'dark-mode': themeStore.theme === 'dark' }"
    v-vloading="loading"
  ></div>
</template>
<script setup lang="ts">
import L from 'leaflet'
import DestinationIcon from '../images/destinationIcon.png'
import OriginIcon from '../images/originIcon.png'
import TransferIcon from '../images/transferIcon.png'
import * as turf from '@turf/turf'
import { useThemeStore } from '@/stores/modules/theme'
import { transportationMode } from '@/components/transportationMode'

const themeStore = useThemeStore()

const props = defineProps<{
  serial_no?: string
  uncode?: string
  _schemas?: string
  mode?: string
}>()

const markerPositions = ref([])
// 创建自定义图标
const originIcon = L.icon({
  iconUrl: OriginIcon,
  iconSize: [20, 20],
  iconAnchor: [10, 20],
  popupAnchor: [0, -8]
})

const destinationIcon = L.icon({
  iconUrl: DestinationIcon,
  iconSize: [20, 20],
  iconAnchor: [10, 20],
  popupAnchor: [0, -8]
})

const transferIcon = L.icon({
  iconUrl: TransferIcon,
  iconSize: [20, 20],
  iconAnchor: [10, 20],
  popupAnchor: [0, -8]
})

let map: L.Map | null = null

// 初始化地图（不添加标记）
const initMap = () => {
  if (map) {
    return
  }

  map = L.map('tracking-map', { scrollWheelZoom: false }).setView([51.505, -0.09], 3)

  // 添加 TileLayer
  L.tileLayer('https://map.kerryapex.com/osm_tiles/{z}/{x}/{y}.png', {
    attribution:
      '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
  }).addTo(map)
}

// 修正经度
const fixLng = (lng: number) => {
  while (lng > 180) lng = lng - 360
  while (lng <= -180) lng = lng + 360
  return lng
}
const getBBox = () => {
  let bnd = map.getBounds()
  let ww = bnd.getEast() - bnd.getWest()
  let cent_pt = map.getCenter()
  let bbox = [
    fixLng(cent_pt.lng - ww / 2),
    bnd.getSouth(),
    fixLng(cent_pt.lng + ww / 2),
    bnd.getNorth()
  ]

  let bbox2 = []
  let bbox3 = []
  if (ww > 360) {
    bbox[0] = -180
    bbox[2] = 0

    bbox2[0] = 0
    bbox2[1] = bbox[1]
    bbox2[2] = 180
    bbox2[3] = bbox[3]
  }
  if (bbox[2] * bbox[0] < 0 || bbox[2] < bbox[0]) {
    if (bbox[0] >= 0) {
      bbox2[0] = -180
      bbox2[2] = bbox[2]
      bbox2[1] = bbox[1]
      bbox2[3] = bbox[3]
      bbox[2] = 180
    } else {
      bbox2[0] = 0
      bbox2[2] = bbox[2]
      bbox2[1] = bbox[1]
      bbox2[3] = bbox[3]
      bbox[2] = 0
    }
  }
  if (bbox2[2] * bbox2[0] < 0 || bbox2[2] < bbox2[0]) {
    if (bbox2[0] >= 0) {
      bbox3[0] = -180
      bbox3[2] = bbox2[2]
      bbox3[1] = bbox2[1]
      bbox3[3] = bbox2[3]
      bbox2[2] = 180
    } else {
      bbox3[0] = 0
      bbox3[2] = bbox2[2]
      bbox3[1] = bbox2[1]
      bbox3[3] = bbox2[3]
      bbox2[2] = 0
    }
  }
  let ll = []
  ll.push(bbox)

  if (bbox2.length > 0) ll.push(bbox2)
  if (bbox3.length > 0) ll.push(bbox3)
  return ll
}
let track_added_marker = []

const clear_marker = () => {
  track_added_marker.forEach((v) => {
    map!.removeLayer(v)
  })

  track_added_marker = []
}
const draw_marker = (dottedLine = [], solidLine = []) => {
  clear_marker()
  dottedLine.forEach((l) => {
    addMapLine(l, true, { color: '#ff7500', weight: 2 })
  })
  solidLine.forEach((l) => {
    addMapLine(l, false, { color: '#ff7500', weight: 2 })
  })
}
const addMapLine = (l, IsDash, opts) => {
  let mpts = l.pts
  if (mpts == null || mpts.length == 0) return

  let bnd = map.getBounds()
  let ww = bnd.getEast() - bnd.getWest()
  let cc = Math.ceil(ww / 360)

  let boxlist = getBBox()

  let ll = []

  for (let ii = 0; ii < mpts.length; ii++) {
    ll[ii] = [mpts[ii][1], mpts[ii][0]]
  }
  let pline = turf.lineString(ll, { name: '' })
  let level = map.getZoom()
  let options = {
    tolerance:
      Math.round(
        ((level < 8 ? 0.0005 : level < 12 ? 0.00005 : 0) / (level == 0 ? 1 : level)) * 1000000
      ) / 1000000,
    highQuality: false
  }
  let simplified = turf.simplify(pline, options)

  let lines_list = []
  let clipped: any = simplified
  boxlist.forEach((bbox) => {
    let bb = bbox
    clipped = turf.bboxClip(simplified, bb)
    if (clipped != null) {
      if (clipped.geometry.type === 'LineString') {
        let line_pts = clipped.geometry.coordinates
        let pta = []
        let jj = 0
        for (let ii = 0; ii < line_pts.length; ii++) {
          pta[jj++] = [line_pts[ii][1], line_pts[ii][0]]
        }
        lines_list.push(pta)
      } else if (clipped.geometry.type === 'MultiLineString') {
        clipped.geometry.coordinates.forEach((_pts) => {
          let line_pts = _pts
          let pta = []
          let jj = 0
          for (let ii = 0; ii < line_pts.length; ii++) {
            pta[jj++] = [line_pts[ii][1], line_pts[ii][0]]
          }
          lines_list.push(pta)
        })
      }
    }
  })

  let cc1 = Math.floor(bnd.getWest() / 360)
  let cc2 = Math.ceil(bnd.getEast() / 360)
  cc = cc2 - cc1 + 1

  let kk = cc1
  // let lines_list = [mpts]
  while (kk >= cc1 && kk <= cc2 && cc > 0) {
    lines_list.forEach((a) => {
      let ii = 0
      let jj = 0
      let pts = []
      for (ii = 0; ii < a.length; ii++) pts[jj++] = [a[ii][0], a[ii][1] + kk * 360]

      if (jj > 0) {
        if (IsDash) {
          showTrackLine(
            pts,
            jj,
            Object.assign({}, { color: '#ff7500', dashArray: '10', weight: 2 }, opts)
          )
        } else {
          showTrackLine(pts, jj, Object.assign({}, { color: '#ff7500', weight: 1 }, opts))
        }
      }
    })

    kk++
    cc--
  }
}

const showTrackLine = (pts, jj, opts) => {
  let arrow = L.polyline(pts, Object.assign({}, { color: '#ff7500', weight: 2 }, opts)).addTo(map)
  track_added_marker.push(arrow)
}

const addResetZoomButton = (center: L.LatLng, zoom: number) => {
  const ResetZoomControl = L.Control.extend({
    options: {
      position: 'topleft'
    },
    onAdd: function () {
      const container = L.DomUtil.create('div', 'reset-zoom-control leaflet-bar')

      // 创建重置缩放按钮
      const resetZoomButton = L.DomUtil.create('a', 'reset-zoom-button leaflet-bar-part', container)
      resetZoomButton.href = '#'
      resetZoomButton.title = 'Reset Zoom'
      resetZoomButton.innerHTML = `<div class="outer-ring" style="height: 100%; padding: 4px;border: 1px solid #2b2f36;border-radius: 50%;">
        <div class="inner-ring" style=" height: 100%; width: 100%; background-color: #2b2f36; border-radius: 50%;"></div>
        </div>`
      resetZoomButton.setAttribute('role', 'button')

      // 点击按钮时重置地图到首次标记后的缩放和中心
      resetZoomButton.addEventListener('click', () => {
        map!.setView(center, zoom)
      })

      return container
    }
  })

  // 添加重置按钮到地图
  new ResetZoomControl().addTo(map!)
}

// 定义首次渲染地图的中心和缩放级别
let initialCenter: L.LatLng | null = null
let initialZoomLevel: number | null = null
let isFirstRender = true // 标记是否为首次渲染

let mirrorMarkers = ref({})

// 设置中心点之后的东北角和西南角的经纬度对象
let latLngBoundsObj = ref({ northEast: { lat: 0, lng: 0 }, southWest: { lat: 0, lng: 0 } })
// 设置中心和缩放级别
const setCenterAndZoom = () => {
  if (viewData.value.length > 0) {
    // 根据标记的位置设置中心点以及缩放级别
    const bounds = L.latLngBounds(viewData.value)
    map!.fitBounds(bounds, { paddingTopLeft: [20, 90], paddingBottomRight: [0, 30] })

    setTimeout(() => {
      if (isFirstRender) {
        initialCenter = map!.getCenter()
        initialZoomLevel = map!.getZoom()
        isFirstRender = false
        // 定义最小缩放等级  缩放等级数值越小，地图显示的区域越大
        const minZoom = 5 // 示例：最小缩放等级为 5

        // 如果当前缩放等级大于最小缩放等级，则调整缩放等级
        if (initialZoomLevel > minZoom) {
          map!.setZoom(minZoom)
          initialZoomLevel = minZoom
        }
      }
      // 获取东北角和西南角的经纬度
      const curLatLon = map.getBounds()
      latLngBoundsObj.value = {
        northEast: { lat: curLatLon.getNorth(), lng: curLatLon.getEast() },
        southWest: { lat: curLatLon.getSouth(), lng: curLatLon.getWest() }
      }
      addResetZoomButton(initialCenter!, initialZoomLevel!)
    }, 500)
  }
}

// 用来记录初始化时默认打开的弹出窗口，以便在地图移动时判断是否需要重新打开弹出窗口
const popupObj = ref({})
// 将经纬度格式化为四位小数，防止精度问题
const formatLocationNum = (num: string | number) => {
  return Number(num).toFixed(4)
}
// 判断是否需要打开弹出窗口
const shouldDisplayPopup = (isInit?: boolean) => {
  const allMarkers = {
    ...initMarksObj.value,
    ...mirrorMarkers.value
  }
  Object.values(allMarkers).forEach((marker: any) => {
    // 创建 LatLngBounds 对象
    const bounds = L.latLngBounds(latLngBoundsObj.value.southWest, latLngBoundsObj.value.northEast)
    const marksLatLng = marker.getLatLng()
    // 检查标记是否在边界内
    const isInsideBounds = bounds.contains(marksLatLng)
    const locationKey = `${formatLocationNum(marksLatLng.lat)},${formatLocationNum(marksLatLng.lng)}`
    if (!isInsideBounds) return
    if (isInit) {
      if (!popupObj.value[locationKey]) {
        popupObj.value[locationKey] = true
        marker.openPopup()
      }
    } else {
      const popupItem = popupObj.value[locationKey]
      // 如果用户没有关闭弹出窗口，则重新打开弹出窗口
      if (popupItem) {
        marker.openPopup()
      }
    }
  })
}

// 初始标记的位置
const initMarksObj = ref({})
// 添加标记
const addMarkersToMap = () => {
  if (!map) return // 确保地图已经初始化

  markerPositions.value.forEach((position) => {
    const marker = L.marker([position.lat, position.lng], { icon: position.icon }).addTo(map)

    const customPopupContent = `
      <div class="popup-content">
        <p class="label">${position.label}</p>
        <p>
          <span class="font_family icon-icon_location_fill_b" style="color: ${position.iconColor}"></span>
          <span>${position.city}</span>
        </p>
      </div>
    `
    marker.bindPopup(customPopupContent, {
      closeButton: false,
      autoClose: false,
      autoPan: false, // 防止弹出框打开时移动地图
      closeOnClick: false
    })
    marker.on('click', () => {
      const latLng = marker.getLatLng() // 获取标记的经纬度
      const key = `${latLng.lat},${latLng.lng}` // 使用经纬度组合成一个唯一键
      if (key in popupObj.value) {
        popupObj.value[key] = marker.isPopupOpen() // 直接切换 isPopup 状态
      }
    })
    initMarksObj.value[`${position.lat},${position.lng}`] = marker
  })
  setTimeout(() => {
    updateVisibleMarkers(true)
  }, 1000)
}

// 新增轮船当前位置标记
const addShipMarker = (x: number) => {
  const solidLine = allMapData.value.solidLine
  // 如果轮船还未出发，则显示起点轮船标记
  if (solidLine.length === 0) {
    // 创建轮船图标
    const arrowIcon = L.divIcon({
      html: `
        <div class="container">
          <div class="circle"></div>
          <span style="padding: 0; color:white; border:1px solid white" class="font_family icon-${transportationMode?.[props?.mode]}" ></span>
        </div>
        `,
      className: 'arrow-icon',
      iconSize: [50, 50],
      iconAnchor: [25, 25], // 箭头的中心点
      popupAnchor: [0, -25] // 弹出框的锚点
    })

    let curMarkerLocation = markerPositions.value.find((item) => item.label === 'Origin')
    const arrowMarker = L.marker([curMarkerLocation.lat, curMarkerLocation.lng + x * 360], {
      icon: arrowIcon
    }).addTo(map)
    track_added_marker.push(arrowMarker)
  } else if (solidLine.length > 0) {
    // 如果轮船已经出发，则显示轮船当前位置标记
    // 如果线段至少有两个点，才添加箭头
    // 获取线段的最后一个点和倒数第二个点
    const lastPoint = solidLine[solidLine.length - 1]
    const secondLastPoint = solidLine[solidLine.length - 2]
    // 计算线段末端的角度（以弧度为单位）
    const angle =
      (Math.atan2(
        Number(lastPoint.lon) - Number(secondLastPoint.lon), // Δlon (x)
        Number(lastPoint.lat) - Number(secondLastPoint.lat) // Δlat (y)
      ) *
        (180 / Math.PI) +
        360) %
      360
    // 创建自定义箭头图标
    const arrowIcon = L.divIcon({
      html: `
      <div style="transform: rotate(${angle}deg);" class="container">
        <div class="circle"></div>
        <span style="color:white;border:1px solid white" class="font_family icon-icon_arrow_b"></span>
      </div>
      `,
      className: 'arrow-icon',
      iconSize: [50, 50],
      iconAnchor: [25, 25], // 箭头的中心点
      popupAnchor: [0, -25] // 弹出框的锚点
    })
    // 创建箭头标记，并根据计算出的角度旋转箭头
    const arrowMarker = L.marker([Number(lastPoint.lat), Number(lastPoint.lon) + x * 360], {
      icon: arrowIcon
    }).addTo(map)
    // 将箭头标记也存储在 track_added_marker 数组中，以便后续管理
    track_added_marker.push(arrowMarker)
  }
}

// 更新可见标记
const updateVisibleMarkers = (isInit?: boolean) => {
  const newVisibleMarkers = new Set()

  let bnd = map.getBounds()
  let ww = bnd.getEast() - bnd.getWest()

  let cc = Math.ceil(ww / 360)

  let cc1 = Math.floor(bnd.getWest() / 360)
  let cc2 = Math.ceil(bnd.getEast() / 360)
  cc = cc2 - cc1 + 1

  let x = cc1

  // 移除所有标记
  Object.values(mirrorMarkers.value).forEach((marker: any) => {
    map.removeLayer(marker)
    delete mirrorMarkers.value[`${marker.getLatLng().lat},${marker.getLatLng().lng}`]
  })

  // 计算当前视图中的标记，包括多地球的情况
  while (x >= cc1 && x <= cc2 && cc > 0) {
    if (x === 0) {
      // 初始化时不添加当前轮船位置标记
      if (!isInit) addShipMarker(x)
      x++
      cc--
      continue
    }

    Object.values(initMarksObj.value).forEach((marker: any) => {
      const latLng = marker.getLatLng()
      const key = `${latLng.lat},${latLng.lng + x * 360}`
      if (!mirrorMarkers.value[key]) {
        const newMarker: any = L.marker([latLng.lat, latLng.lng + x * 360], {
          icon: marker.options.icon
        }).bindPopup(marker.getPopup().getContent(), marker.getPopup().options)

        newMarker.on('click', () => {
          const latLng = newMarker.getLatLng() // 获取标记的经纬度
          const key = `${latLng.lat},${latLng.lng}` // 使用经纬度组合成一个唯一键

          if (key in popupObj.value) {
            popupObj.value[key] = newMarker.isPopupOpen() // 直接切换 isPopup 状态
          }
        })
        mirrorMarkers.value[key] = newMarker
        map.addLayer(newMarker)
      }
      newVisibleMarkers.add(mirrorMarkers.value[key])
    })

    addShipMarker(x)

    x++
    cc--
  }
  shouldDisplayPopup(isInit)
}

// 处理得到的数据
const handleData = (data) => {
  let key = 0
  let curLine = []
  let resultLine = []
  data.forEach((item, index) => {
    if (item.sn === '1' && key === 0) {
      key++
      curLine.push([Number(item.lat), Number(item.lon)])
    } else if (item.sn === '1' && key !== 0) {
      resultLine.push({
        name: key,
        pts: curLine
      })
      curLine = [[Number(item.lat), Number(item.lon)]]
      key++
    }
    if (item.sn !== '1') {
      curLine.push([Number(item.lat), Number(item.lon)])
    }
    if (index === data.length - 1 && item.sn !== '1') {
      resultLine.push({
        name: key,
        pts: curLine
      })
    }
  })
  return resultLine
}

const allMapData = ref()
const viewData = ref([])
const loading = ref(false)
// 请求接口并处理标记
const getMarker = () => {
  loading.value = true
  $api
    .getTrackingDetailMapData({
      serial_no: props.serial_no,
      uncode: props.uncode,
      _schemas: props._schemas
    })
    .then((res) => {
      if (res.code === 200) {
        allMapData.value = res.data
        const { data } = res
        data?.point &&
          data?.point.forEach((item) => {
            const iconColorList = {
              Destination: { color: '#24ca5a', icon: destinationIcon },
              Origin: { color: '#ED6D00', icon: originIcon },
              Transfer: { color: '#ed0000', icon: transferIcon }
            }
            markerPositions.value.push({
              lat: Number(item.lat),
              lng: Number(item.lng),
              city: item.infor,
              label: item.label,
              icon: iconColorList[item.label].icon,
              iconColor: iconColorList[item.label].color
            })
          })
        if (data?.rangePoint.length > 0) {
          if (Number(data.rangePoint[2].lon) < Number(data.rangePoint[1].lon)) {
            data.rangePoint[2].lon = Number(data.rangePoint[2].lon) + 360
          }
          data.rangePoint.splice(0, 1)
        }
        viewData.value = (data?.rangePoint.length > 0 ? data?.rangePoint : data?.point)?.map(
          (item) => {
            return { lat: Number(item.lat), lng: Number(item.lon || item.lng) }
          }
        )
        // 请求成功后添加标记
        addMarkersToMap()
        // 设置中心和缩放级别
        setCenterAndZoom()
        if (data?.dottedLine) {
          draw_marker(handleData(data.dottedLine), handleData(data.solidLine))
          map.on('moveend', function () {
            draw_marker(handleData(data.dottedLine), handleData(data.solidLine))
            updateVisibleMarkers()
          })
          map.on('zoomend', function () {
            draw_marker(handleData(data.dottedLine), handleData(data.solidLine))
            updateVisibleMarkers()
          })
        }
      }
    })
    .finally(() => {
      loading.value = false
    })
}

// 监听 `serial_no` 变化
watch(
  () => props.serial_no,
  (val) => {
    if (val) {
      getMarker()
    }
  },
  { immediate: true }
)

// 当组件挂载时初始化地图
onMounted(() => {
  initMap() // 初始化地图，不加标记
})

onUnmounted(() => {
  // 清除地图实例，防止内存泄漏
  if (map) {
    map.off() // 移除所有事件监听器
    map.remove() // 移除地图
  }
})
</script>

<style lang="scss">
@import 'leaflet/dist/leaflet.css';
.tracking-map {
  border-radius: 12px;
  .leaflet-popup-content-wrapper {
    border-radius: 6px;
  }
  .leaflet-popup-content {
    margin: 0;
    padding: 4px;
    .popup-content {
      p {
        height: 15px;
        margin: 0;
        white-space: nowrap;
        span {
          margin-left: -2px;
          font-size: 12px;
          font-weight: 700;
          color: #2b2f36;
        }
      }
      .label {
        margin-bottom: 4px;
        font-size: 12px;
        color: #646a73;
      }
    }
  }

  /* 隐藏默认的弹出窗口箭头 */
  .leaflet-popup-tip {
    display: none;
  }

  .leaflet-control-zoom {
    span {
      color: #2b2f36;
    }
  }
}
.leaflet-control {
  a.leaflet-control-zoom-in,
  a.leaflet-control-zoom-out,
  a.leaflet-bar-part {
    width: 26px;
    height: 26px;
    font-size: 18px;
  }
  a.leaflet-control-zoom-in,
  a.leaflet-control-zoom-out {
    display: flex;
    justify-content: center;
    align-items: center;
  }
}
.dark-mode {
  div.leaflet-control-zoom.leaflet-bar.leaflet-control {
    border: 0;
    border-radius: 4px;
    box-shadow: none;
    a {
      background-color: #30353c;
      border-bottom: none;
      span {
        color: var(--color-neutral-1);
      }
      &:first-child {
        span {
          display: inline-block;
          width: 24px;
          height: 28px;
          border-bottom: 1px solid #3f434a;
        }
      }
    }
  }
  .reset-zoom-control {
    border: none;
    background-color: #30353c;
  }
  a.leaflet-bar-part {
    background-color: #30353c;
    border-radius: 4px;
    box-shadow: none;
    overflow: hidden;
    div {
      border-color: var(--color-neutral-1) !important;
      div {
        background-color: var(--color-neutral-1) !important;
      }
    }
  }
}

/* 示例：将所有地图图片的颜色反转 */
.dark-mode img:not(.leaflet-marker-icon) {
  filter: invert(1) hue-rotate(230deg) saturate(60%) brightness(60%) opacity(80%);
}
// 防止暗黑模式下地图超出容器
.tracking-map {
  overflow: hidden;
}
// 修改暗黑模式下的背景色
.leaflet-container.dark-mode,
.leaflet-map-pane.dark-mode,
.leaflet-tile-container.dark-mode {
  background-color: #2b2f36;
}
// 处理版权信息在切换模式后样式错误bug
.leaflet-right .leaflet-control-attribution {
  background: rgba(255, 255, 255, 0.8);
  margin: 0 4px 4px 0;
  color: #2b2f36;
  span {
    font-size: 12px;
    color: #2b2f36;
  }
  a {
    color: #0078a8;
    font-size: 12px;
  }
}
/* 自定义重置缩放按钮控件样式 */
.reset-zoom-control {
  margin-top: 10px;
  border-radius: 4px;
  background-color: white;
  border: 1px solid #ccc;
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.3);
}

.reset-zoom-button {
  background-color: white;
  border: none;
  cursor: pointer;
  padding: 6px;
}
</style>

<style lang="scss">
.tracking-map {
  .container {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 50px;
    width: 50px;
    /* background-color: #d9dddf; */
    .font_family {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-12px, -12px);
      display: flex;
      justify-content: center;
      align-items: center;
      width: 24px;
      height: 24px;
      padding-bottom: 2px;
      padding-left: 1px;
      color: white;
      background-color: rgba(255, 117, 0, 1);
      border-radius: 50%;
      font-size: 14px;
      border: 1px solid white;
    }
  }
  .circle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    background-color: rgba(255, 117, 0, 1);
    border-radius: 50%;
    position: relative;
    animation: expandAndFade 1.3s linear infinite;
    overflow: hidden; /* 确保子元素不会溢出 */

    opacity: 0.9;
  }

  @keyframes expandAndFade {
    0% {
      transform: scale(1);
      opacity: 0.9;
    }

    50% {
      transform: scale(1.5);
      opacity: 0.7;
    }

    100% {
      transform: scale(2);
      opacity: 0.2;
    }
  }
}
</style>
