<template>
    <div class="map-contain">
        <canvas id="map" ref="root"></canvas>
        <img src="/image/compass.png" class="compass" ref="compass"
            :style="`transform: rotateZ(${compassRotationAngle}rad)`" alt="" @click="changeCompass">
        <div class="navigate-preview">
            <img src="/image/navigate.png" class="navigate" @click="changeNavigate" alt="" />
        </div>
    </div>
</template>

<script setup>
import { markRaw, onMounted, watch, ref, computed, onBeforeUnmount, nextTick } from 'vue'
import { useMapStore } from '@/store/MapStore.js'
import { useVehicleStore } from '@/store/VehicleStore'
import { storeToRefs } from 'pinia'
import { vehicleScale } from '../js/tools'
import DeckNew from '@/tools/Deck';
import * as turf from '@turf/turf'

const safe_color = '#FFA500'
const BASE_RAD = 1.5707963267948966
const MAP_ZOOM = 17
const LAYER_PROPOITY = {
    runablearea: 1,
    staticobjs: 2,
    diggingarea: 3,
    diggingworkarea: 3,
    dumparea: 4,
    barricade: 5,
    electronicFence: 6,
    wetArea: 7,
    lane: 8,
    obstacles: 9,
    stationarea: 10,
    parkspot: 11,
    lanenode: 12,
    currPathinfo: 13,
    nextPathinfo: 14,
    avoidancePath: 15,
    traj: 16,
    obstacle: 17
}

const vehicles = {}
let ismousedown = false
let ismousemove = false
let resetTime = null
let obstaclePolygons = []
let dynamicPaths = []

const reNavigateTime = 10 * 1000

const props = defineProps({
    isShowBasic: Boolean,
    clearSinglePointEnable: Boolean,
    reloadData: Boolean
})

let map = markRaw({})
let currentPosition = markRaw([])
const root = ref(null)
const mapLayers = markRaw({})
const safeObjects = markRaw({})
const safeTrajs = markRaw({})
const singlePaths = markRaw([])
const navigate = ref(true)
const currentRotation = ref(0)
const markerLayerGroupLists = markRaw({})
const isSinglePreview = ref(false)
const compassRotationAngle = ref(0)
const compass = ref(null)
const mapStore = useMapStore()
const mapStoreToRefs = storeToRefs(mapStore)
const {
    lanenode,
    lane,
    runablearea,
    centerline,
    obstacles,
    diggingworkarea,
    dumparea,
    barricade,
    stationarea,
    parkspot,
    staticobjs,
    electronicFence,
    morepoint, // 单机循迹单路径
    morepath, // 单机循迹多路径
    nextPathinfo,
    currPathinfo,
    nextPathAvailable,
    avoidancePath, // 绕障路径
    obstacleData,
    dynamicPathinfo, // 车辆动态路权信息
} = mapStoreToRefs
const vehicleStore = useVehicleStore()
const vehicleStoreToRefs = storeToRefs(vehicleStore)
const { vehiclesPositioninfo, safeObj, trajObj, paraminfo, deleteVehicles } = vehicleStoreToRefs

const deviceId = computed(() => {
    return paraminfo.value.deviceId
})

const emits = defineEmits(['handleSend'])

onMounted(() => {
    //阻止鼠标右键
    document.oncontextmenu = f => {
        event.returnValue = false
    }
    let currentCoordinate = []
    let bool = false
    map = new DeckNew({
        initialViewState: {
            latitude: document.location.mapCenter[0],
            longitude: document.location.mapCenter[1],
            zoom: MAP_ZOOM,
            // pickingRadius:5,
            controller: true,
        },
        controller: true,
        // parent: document.getElementById('mapMain'),
        canvas: 'map',
        //如果deck滚动条滚动或者拖动地图时，不进行地图角度和等级矫正
        onInteractionStateChange: f => {

            if (!currentPosition) return
            if (f.isZooming || f.isDragging) {
                navigate.value = false
                resetTime = window.setTimeout(() => {
                    if (!navigate.value) {
                        navigate.value = true
                        dealVehicleRotate()
                        currentRotation.value = BASE_RAD
                    }
                }, reNavigateTime)
            }
        }
    })
    map.setPitch(40)

    emits('handleSend', {
        type: 'syncMapVersion',
        msg: {}
    })
})

function changeNavigate() {
    navigate.value = true
    dealMapRotate(currentRotation.value, currentPosition, map.getZoom())
}

// 指南针
function changeCompass() {
    dealVehicleRotate(0)
    currentRotation.value = BASE_RAD
    if (navigate.value) {
        setTimeout(() => {
            dealVehicleRotate()
        }, 3 * 1000)
    }
}

function changeMouseDown() {
    ismousedown = true
}

function changeMouseMove() {
    if (ismousedown) {
        ismousemove = true
        if (resetTime) {
            window.clearTimeout(resetTime)
            resetTime = null
        }
    }
}

function changeMouseUp() {
    if (!currentPosition) return
    if (ismousedown && ismousemove) {
        navigate.value = false
        resetTime = window.setTimeout(() => {
            if (!navigate.value) {
                navigate.value = true
                dealVehicleRotate()
                currentRotation.value = BASE_RAD
            }
        }, reNavigateTime)
        ismousedown = false
        ismousemove = false
    }
}

function dealVehicleRotate(rotate) {
    let keys = Object.keys(vehicles)
    let target = keys.find(item => item === deviceId.value)
    let currentTarget = vehicles[target]
    let vehicleHeading = BASE_RAD
    let currentOldHeading = currentTarget && currentTarget.options.vehicleHeading
    if (typeof rotate === 'number') {
        dealMapRotate(0, currentPosition, map.getZoom())
    } else {
        vehicleHeading = currentOldHeading
        currentRotation.value = vehicleHeading
        dealMapRotate(vehicleHeading, currentPosition, map.getZoom())
    }
}

function dealMapLayer(value, color, layerName) {
    if (!value || !map) return

    let result = JSON.parse(value)
    if (!result) return
    let attr = {};
    if (layerName === 'barricade') {
        attr = {
            getLineColor: map.convertColor('#0066ff', 1),
            // width: 5
        }
    } else {
        attr = {
            getLineColor: feature => {
                return layerName === 'lane' ? map.convertColor('#eee', 0.5) : map.convertColor(feature.properties.color, 1)
            },
            getFillColor: feature => {
                let opacity = 0.7;
                if (layerName === 'runablearea') {
                    opacity = 0.5
                }
                return layerName === 'electronicFence' ? map.convertColor(feature.properties.color, opacity) : color ? map.convertColor(color, opacity) : map.convertColor(feature.properties.color, opacity)
            }
        }
    }
    map.createGeoJsonLayer(result, attr, layerName)
    mapStore.setData(layerName, null)
}

function dealMapPath(value, layerName, color) {
    if (value && value.length) {
        let data = JSON.parse(JSON.stringify(value))
        let path = map.createPathLayer([
            {
                path: [...map.coorReverse(data)],
                name: layerName,
            }
        ], {
            getColor: f => map.convertColor('#ffff00'),
        }, layerName)
        mapLayers[layerName] = layerName
        const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(data)))])
        let bufferLine = turf.buffer(lineGeo, 6.5, { units: "meters" })
        const obj = {
            type: "FeatureCollection",
            features: [bufferLine]
        }
        const attr = {
            getFillColor: f => map.convertColor(color, 0.6),
            getLineColor: f => map.convertColor(color, 1)
        }
        // map.setLayerSortName('bufferLayer', layerName + 'Buffer')
        map.createGeoJsonLayer(obj, attr, layerName + 'Buffer')
        mapLayers[`${layerName}Buffer`] = `${layerName}Buffer`
    }
}


function dealDivIcon(value, layerName) {
    if (!value || !map) return
    let result = JSON.parse(value)
    if (!result) return

    const { features } = result

    for (let i = 0; i < features.length; i++) {
        let feature = features[i]
        let { name, id } = feature.properties
        let { coordinates } = feature.geometry
        let markerCoor = null
        if (layerName === 'lanenode') {
            markerCoor = [coordinates[1], coordinates[0]]
        } else {
            let center = turf.center(feature)
            markerCoor = [center.geometry.coordinates[1], center.geometry.coordinates[0]]
        }
        let texData = {
            name: name,
            id: id,
            coordinates: [markerCoor[1], markerCoor[0], 1],
        }
        if (markerLayerGroupLists[`${layerName}${id}`]) {
            markerLayerGroupLists[`${layerName}${id}`] = texData
        } else {
            markerLayerGroupLists[`${layerName}${id}`] = texData
        }
    }
    map.createTextLayer([...Object.values(markerLayerGroupLists)], {
        getSize: 15
    }, 'text')
    // map.setLayerSortName('marker', 'marker')
    map.setLayerSortName('markerLayer', 'marker')
    map.creatCircle(Object.values(markerLayerGroupLists), {
        getFillColor: f => map.convertColor('#2aa1fa', 1),
        getLineColor: f => map.convertColor('#2aa1fa', 0.5),
        getLineWidth: 0.5,
        radius: 1.5
    }, 'marker')

    mapStore.setData(layerName, null)
}

function removeLayer(obj, vehicleID) {
    if (obj[vehicleID]) {
        for (let item in obj[vehicleID]) {
            map.removeLayer('map', obj[vehicleID][item])
        }
    }
}


function dealSafe(vehicleID, safe) {
    if (!map) return

    const { enable, data, dealedData, vehicleType, vehicleLatitude, vehicleLongtitude } = safe
    removeLayer(safeObjects, vehicleID)
    safeObjects[vehicleID] = {}
    if (!enable) return

    let safeType = [1, 20, 21].includes(vehicleType) ? 'circle' : 'rectangle'
    let zIndex = 16
    for (let i = 0; i < dealedData.length; i++) {
        let item = JSON.parse(JSON.stringify(dealedData[i]))
        let layer = safeType === 'rectangle' ? map.creatPolygon({
            coordinates: item,
        }, {
            getFillColor: d => map.convertColor(safe_color),
            getLineColor: d => map.convertColor(safe_color),
            getLineWidth: 1
        }, 'rectangle') : map.creatCircle([vehicleLongtitude, vehicleLatitude], {
            getFillColor: f => map.convertColor(safe_color),
            getLineColor: f => map.convertColor(safe_color),
            getLineWidth: 1,
            radius: data[i]
        }, vehicleID)
        safeObjects[vehicleID][i] = vehicleID
    }
}

function dealTraj(vehicleID, traj) {
    if (!traj || !map) return
    const { trajPathWid, radius, dealedData, vehicleType, vehicleLatitude, vehicleLongtitude } = traj
    removeLayer(safeTrajs, vehicleID)
    safeTrajs[vehicleID] = {}
    // if (!dealedData) return
    // if ((dealedData && !dealedData.length) || !radius)) return
    if ([1, 20, 21].includes(vehicleType && !radius)) return;

    const zIndex = LAYER_PROPOITY['traj']
    if ([1, 20, 21].includes(vehicleType) && radius) {
        map.creatCircle([vehicleLongtitude, vehicleLatitude], {
            getFillColor: f => map.convertColor('#FFA500'),
            getLineColor: f => map.convertColor('#FFA500'),
            getLineWidth: 1,
            radius: radius
        }, vehicleID)
        safeTrajs[vehicleID]['circle'] = vehicleID
    } else {
        if (dealedData && !dealedData.length) return;
        const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(dealedData)))])
        // safeTrajs[vehicleID]['path'] = path
        let bufferLine = turf.buffer(lineGeo, trajPathWid / 2, { units: "meters" })
        const data = {
            type: "FeatureCollection",
            features: [bufferLine]
        }
        const attr = {
            getFillColor: f => map.convertColor(safe_color, 0.4),
            getLineColor: f => map.convertColor(safe_color, 1)
        }
        safeTrajs[vehicleID]['pathBuffer'] = vehicleID + 'Buffer'
        map.setLayerSortName('bufferLayer', vehicleID + 'Buffer', 1)
        map.createGeoJsonLayer(data, attr, vehicleID + 'Buffer')
    }
}


function dealMapRotate(rotationAngle, center, zoom) {
    let rotate = 0
    if (rotationAngle) {
        rotate = turf.radiansToDegrees(rotationAngle - BASE_RAD)
    }
    if (compass.value) {
        compassRotationAngle.value = rotationAngle
        map.setRotation(rotate)
    }
    center && center.length && map.flyTo(center, zoom, { animate: false })
}

function dealVehiclePosition(value) {
    if (!map || !value.length) return

    let zoom = map.getZoom()
    let { iconSize, iconAnchor } = vehicleScale(zoom)
    for (let i = 0; i < value.length; i++) {
        const item = value[i]
        const { vehicleLatitude, vehicleLongtitude, vehicleHeading, vehicleID, image } = item
        let vehiclePosition = [vehicleLatitude, vehicleLongtitude]
        if (vehicles[vehicleID]) {
            let baseObj = {
                name: vehicleID,
                rotate: vehicleHeading,
                coordinates: [vehicleLongtitude, vehicleLatitude],
                image: image,
                options: {
                    vehicleHeading: vehicleHeading
                }
            }

            if (vehicles[vehicleID]) {

                vehicles[vehicleID] = baseObj

            } else {
                vehicles[vehicleID] = baseObj
                list.push({ [vehicleID]: vehicles[vehicleID] })

            }

            if (vehicleID === deviceId.value) {
                currentPosition = vehiclePosition

                if (navigate.value) {

                    currentRotation.value = vehicleHeading
                    dealMapRotate(vehicleHeading, vehiclePosition, zoom <= MAP_ZOOM ? MAP_ZOOM : zoom)

                }
            }
        }
    }
    initCar()
}

function initCar(){
    map.clearLayer('car')
    const nameText = JSON.parse(JSON.stringify(Object.values(vehicles))).map(item => {
        item.coordinates = [item.coordinates[0], item.coordinates[1], 10]
        return item
    })
    map.createTextLayer(nameText, {
        getSize: 15,
        getColor: f => [255, 255, 255, 255],
        fontWeight: 400,
        fontSettings: {
            buffer: 2,
            sdf: true,
            smoothing: 0.5
        },
    }, 'carName')
    let car = {};
    Object.values(vehicles).forEach(item => {
        if (car.hasOwnProperty(item.image)) {
            car[item.image].push(item)
        } else {
            car[item.image] = [item]
        }

    })
    for (let key in car) {
        // map.setLayerSortName('carLayer', key)
        let imageURL = `image/${key}.glb`
        map.createCarModel(car[key], { scenegraph: imageURL }, key)
    }
}

function dealSingleDatas(datas) {
    const { color, coor } = datas
    let data = JSON.parse(JSON.stringify(value))
    const lineGeo = turf.lineString([...map.coorReverse(JSON.parse(JSON.stringify(data)))])
    // safeTrajs[vehicleID]['path'] = path
    let bufferLine = turf.buffer(lineGeo, 2, { units: "meters" })
    const obj = {
        type: "FeatureCollection",
        features: [bufferLine]
    }
    const attr = {
        getFillColor: f => map.convertColor(color, 0.4),
        getLineColor: f => map.convertColor(color, 1)
    }
    map.createGeoJsonLayer(obj, attr, layerName)
    singlePaths.push(layerName)
}

function dealObtacleData(value) {
    if (obstaclePolygons.length) {
        obstaclePolygons.forEach(item => map.removeLayer(item))
        obstaclePolygons = []
    }
    if (value && value.length) {
        for (let i = 0; i < value.length; i++) {
            let obstacle = value[i]
            let { typeColor, coordinate, centerLon, centerLat, centerRadius, centerColor } = obstacle
            let obstaclePolygon = L.polygon(JSON.parse(JSON.stringify(coordinate)), { color: typeColor }).addTo(map)
            let point = turf.point([centerLon, centerLat])
            let obstacleBuffer = turf.buffer(point, centerRadius, { units: "meters" })
            let obstacleCircle = L.geoJSON(obstacleBuffer, {
                style: () => {
                    return {
                        color: centerColor,
                        fillOpacity: .5
                    }
                }
            }).addTo(map)
            obstaclePolygons.push(obstacleCircle)
            obstaclePolygons.push(obstaclePolygon)
        }
    }
}

function dealDynamicDatas(datas) {
    let name = 'dynamicLine';
    if (mapLayers[name]) {
        map.removeLayer(mapLayers[name])
        delete mapLayers[name]
    }
    // if (dynamicPaths.length) {
    //     dynamicPaths.forEach(item => map.removeLayer(item))
    //     dynamicPaths = []
    // }
    const { color, path } = datas
    let pathNew = L.polyline(path, {
        color,
        opacity: 0.3,
        weight: 30,
    }).addTo(map)
    mapLayers[name] = pathNew
    mapLayers[name].bringToFront()
    // dynamicPaths.push(pathNew)
}

// 直接监听props.isShowBasic 不会触发
watch(() => props.isShowBasic, () => {
    nextTick(() => {
        map.invalidateSize(true)
    })
})

watch(lanenode, (value) => {
    dealDivIcon(value, 'lanenode', '#ff0000')
}, { immediate: true, deep: true })

watch(lane, (value) => {
    dealMapLayer(value, '#fff', 'lane')
}, { immediate: true, deep: true })

watch(runablearea, (value) => {
    dealMapLayer(value, '#98a0a0', 'runablearea')
}, { immediate: true, deep: true })

watch(centerline, (value) => {
    dealMapLayer(value, '#eee', 'centerline')
}, { immediate: true, deep: true })

watch(obstacles, (value) => {
    dealDivIcon(value, 'obstacles')
    dealMapLayer(value, '#6f9bdd', 'obstacles')
}, { immediate: true, deep: true })

watch(diggingworkarea, (value) => {
    dealDivIcon(value, 'diggingworkarea')
    dealMapLayer(value, '#886e26', 'diggingworkarea')
}, { immediate: true, deep: true })

watch(dumparea, (value) => {
    dealDivIcon(value, 'dumparea')
    dealMapLayer(value, '#886e26', 'dumparea')
}, { immediate: true, deep: true })

watch(barricade, (value) => {
    dealMapLayer(value, '#0000ff', 'barricade')
}, { immediate: true, deep: true })

watch(stationarea, (value) => {
    dealDivIcon(value, 'stationarea')
    dealMapLayer(value, '#90ee90', 'stationarea')
}, { immediate: true, deep: true })

watch(parkspot, (value) => {
    dealDivIcon(value, 'parkspot')
    dealMapLayer(value, '#7e8185', 'parkspot')
}, { immediate: true, deep: true })

watch(staticobjs, (value) => {
    dealMapLayer(value, '#98a0a0', 'staticobjs')
}, { immediate: true, deep: true })

watch(electronicFence, (value) => {
    dealDivIcon(value, 'electronicFence')
    dealMapLayer(value, '#d51d20', 'electronicFence')
}, { immediate: true, deep: true })

watch(nextPathinfo, (value) => {
    dealMapPath(value, 'nextPath', nextPathAvailable.value === 1 ? '#19ae19' : '#8B4513')
    adjustmentLayer()
}, { immediate: true, deep: true })

watch(currPathinfo, (value) => {
    dealMapPath(value, 'currentPath', '#19ae19')
    adjustmentLayer()
}, { immediate: true, deep: true })

watch(avoidancePath, (value) => {
    dealMapPath(value, 'avoidancePath', 'blue')
}, { immediate: true, deep: true })

watch(vehiclesPositioninfo, (value) => {
    dealVehiclePosition(value)
}, {
    immediate: true,
    deep: true
})

watch(safeObj, (value) => {
    Object.keys(value).forEach(item => {
        dealSafe(item, value[item])
    })
}, {
    immediate: true,
    deep: true
})

watch(trajObj, (value) => {
    Object.keys(value).forEach(item => {
        dealTraj(item, value[item])
    })
}, {
    immediate: true,
    deep: true
})

watch(obstacleData, (value) => {
    dealObtacleData(value)
}, { immediate: true, deep: true })

watch(deleteVehicles, (data) => {
    if (!map) return
    if (data && data.length) {
        data.forEach(item => {
            map.removeLayer(vehicles[item])
            removeLayer(safeTrajs, item)
            removeLayer(safeObjects, item)
            delete vehicles[item]
        })
    }
}, {
    immediate: true
})

watch(() => props.clearSinglePointEnable, (enable) => {
    if (!enable) return
    isSinglePreview.value = true
    singlePaths.forEach(item => map.removeLayer('map',item))
}, { deep: true })

watch([morepoint, morepath], (pointData, pathData) => {
    if (isSinglePreview.value && (pointData || pathData)) {
        let datas = pointData.concat(pathData)
        for (let i = 0; i < datas.length; i++) {
            dealSingleDatas(datas[i])
        }
    }
}, { deep: true })

watch(dynamicPathinfo, (pathData) => {
    if (pathData.length > 0) {
        for (let i = 0; i < pathData.length; i++) {
            dealDynamicDatas(pathData[i])
        }
    }
}, { deep: true })

watch(deviceId, (value) => {
    if (value && map) {
        dealMapRotate(currentRotation.value, currentPosition, map.getZoom())
    }
}, { deep: true })

function doUnmount() {
    Object.keys(mapLayers).map(layer => map.removeLayer(mapLayers[layer]))
    let mapDom = root.value
    mapDom.removeEventListener('mousedown', changeMouseDown)
    mapDom.removeEventListener('mousemove', changeMouseMove)
    mapDom.removeEventListener('mouseup', changeMouseUp)

    map.remove()
    map = null
}

watch(() => props.reloadData, (value) => {
    if (value) {
        doUnmount()
    }
})

onBeforeUnmount(() => {
    doUnmount()
})
</script>

<style lang="less" scope>
.map-contain {
    height: 100%;
    overflow: hidden;
}

#map {
    width: 200%;
    height: 200%;
    top: -50%;
    left: -50%;
    background: #171A23;
    position: relative;
}

.navigate-preview {
    background: #4A75A9;
    box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.25);
    border-radius: 10px;
    opacity: 1;
    display: flex;
    align-items: center;
    justify-content: center;
}

.compass,
.navigate-preview {
    position: absolute;
    width: 4vw;
    height: 4vw;
    z-index: 999;
}

.compass {
    bottom: 88px;
    max-width: 60px;
    max-height: 60px;
}

.navigate-preview {
    bottom: 24px;
    max-width: 60px;
    max-height: 60px;
}
</style>
<style lang="less">
.icon-node {
    width: 120px !important;
    white-space: nowrap;
    color: #676565;
    // padding-left: -2em !important;
    z-index: 1;
    font-size: 12px !important;
    font-family: "Open Sans Regular", "Arial Unicode MS Regular", sans-serif;
    text-transform: uppercase;
    text-align: center;
    -webkit-text-stroke-display: block;
    -webkit-text-stroke-width: 1px;
    -webkit-text-stroke-color: rgba(33, 33, 33, 0.8);
}

@media screen and (min-width: 1700px) {
    .icon-node {
        font-size: 18px !important;
    }
}

.icon-node-name {
    display: inline-block;
    transform: scale(.6);
    padding-left: -20px !important;
}

.hide {
    display: none !important;
}

.middle-line {
    // 为svg设置虚线,数字越大,虚线段越长,
    // 设置2个参数,参数1: 虚线的长度  参数2: 虚线和虚线之间的间隔, 如果只设置一个参数,说明两个值一样大小
    stroke-dasharray: 15 20;
    z-index: 999;
}

.leaflet-tooltip {
    color: #fff !important;
    background-color: rgba(255, 255, 255, 0) !important;
    border: 0 !important;
    box-shadow: rgba(0, 0, 0, 0);
    font-size: 12px !important;
}

.leaflet-control-attribution {
    display: none !important;
}
</style>