import * as ol from 'ol'
import * as style from 'ol/style'
import * as layer from 'ol/layer'
import * as source from 'ol/source'
import * as geom from 'ol/geom'
import * as extent from 'ol/extent'
import * as proj from 'ol/proj'
import * as interaction from 'ol/interaction'
import * as format from "ol/format";
import Modify from "ol/interaction/Modify"
import Draw, {createBox} from "ol/interaction/Draw"
import * as sphere from "ol/sphere";
import {unByKey} from "ol/Observable";
import {formatPosition} from "@/utils/easyMap";
import {isValue} from "@/utils/util";
import {fromCircle} from "ol/geom/Polygon";
import {ElMessage} from "element-plus";

const globalLineDash = [
    [0, 0], //实线
    [15, 15], //长虚线
    [5, 5] //虚线
]
const layerFlag = ['layerName', 'drawViewsLayer']
const drawFlag = ['interactionName', 'drawInteraction']
const modifyFlag = ['interactionName', 'modifyInteraction']
const baseDrawConfig = {
    //  样式字段
    text: null, // 要素上显示的文字,默认无文字
    pointIcon: null, // Point的图标,默认圆形
    pointScale: 1, // Point的缩放,默认1
    pointOffset: [0, 0], // Point的偏移量,默认[0, 0]
    lineColor: '#2860F1', // LineString的线段颜色,默认蓝色
    lineWidth: 1, // LineString的线段宽度,默认1
    lineType: 0, // LineString的线段类型索引,默认0,实线,取globalLineDash数组索引
    lineDash: null, // LineString的线段类型,默认null,优先级比lineType高
    polyColor: 'rgba(20, 129, 241, 0.3)', // Polygon的填充色,默认蓝色
    polyBorderColor: '#2860F1', // Polygon的边框颜色,默认蓝色
    polyBorderWidth: 1, // Polygon的边框宽度,默认1
    polyBorderType: 0, // Polygon的边框类型索引,默认0,实线,取globalLineDash数组索引
    polyBorderDash: null, // Polygon的边框类型,默认null,优先级比polyBorderType高
    //  业务字段
    show: false, // 标绘样式选项是否显示
    featureType: 'Point', // 标绘的要素类型
    isPoint: false, // 是否可以标绘点
    isLineString: false, // 是否可以标绘线
    isPolygon: false, // 是否可以标绘面
    showPosition: false, // 是否显示经纬度输入框
    refreshStyleFunc: () => {}, // 刷新标绘样式方法
}
export const getBaseDrawConfig = () => {
    return JSON.parse(JSON.stringify(baseDrawConfig))
}
/**
 *
 * @param map
 * @param arr   要标绘的数组,每个对象的数据在要素中保存的属性为 'val'
 * @param arr > wkt:wkt格式坐标
 * @param arr > styles:要素的样式,优先级最高
 * @param arr > text:styles为空的时候,要素上显示的文字,默认无文字
 * @param arr > textOffsetY:styles为空的时候,要素上显示的文字Y轴偏移量,默认Point-30,其他0
 * @param arr > pointIcon:styles为空的时候,Point的图标,默认圆形
 * @param arr > pointScale:styles为空的时候,Point的缩放,默认1
 * @param arr > pointOffset:styles为空的时候,Point的偏移量,默认[0, 0]
 * @param arr > lineColor:styles为空的时候,LineString的线段颜色,默认蓝色
 * @param arr > lineWidth:styles为空的时候,LineString的线段宽度,默认1
 * @param arr > lineType:styles为空的时候,LineString的线段类型索引,默认0,实线,取globalLineDash数组索引
 * @param arr > lineDash:styles为空的时候,LineString的线段类型,默认null,优先级比lineType高
 * @param arr > polyColor:styles为空的时候,Polygon的填充色,默认蓝色
 * @param arr > polyBorderColor:styles为空的时候,Polygon的边框颜色,默认蓝色
 * @param arr > polyBorderWidth:styles为空的时候,Polygon的边框宽度,默认1
 * @param arr > polyBorderType:styles为空的时候,Polygon的边框类型索引,默认0,实线,取globalLineDash数组索引
 * @param arr > polyBorderDash:styles为空的时候,Polygon的边框类型,默认null,优先级比polyBorderType高
 */
export const drawViews = (map, arr, isAuto = true) => {
    let _source
    const realLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
    if (realLayer[0]) {
        _source = realLayer[0].getSource()
    } else {
        _source = new source.Vector(); //图层数据源
        const _vector = new layer.Vector({
            zIndex: 9999,
            source: _source,
        });
        _vector.set(layerFlag[0], layerFlag[1])
        map.addLayer(_vector);
    }
    _source.clear() //  清空标绘
    const autoPosition: any = []
    const features = arr.filter(v => {
         try {
             new format.WKT().readFeature(v.wkt)
             return true
         } catch (e) {
             console.error('错误坐标:' + v.wkt)
             return false
         }
    }).map(v => {
        const feat: any = new format.WKT().readFeature(v.wkt)
        const type = feat.getGeometry().getType()
        switch (type) {
            case 'Point': autoPosition.push(feat.getGeometry().getCoordinates())
                break
            case 'LineString': autoPosition.push(...feat.getGeometry().getCoordinates())
                break
            case 'Polygon': autoPosition.push(...feat.getGeometry().getCoordinates()[0])
                break
        }
        for (const [kVal, vVal] of Object.entries(v)) {
            // @ts-ignore
            if (vVal === null || vVal === undefined || (typeof vVal === 'string' && vVal.trim() === '') || (typeof vVal === 'object' && vVal?.length === 0)) {
                delete v[kVal]
            }
        }
        const {
            textOffsetY = (type === 'Point' ? -30 : 0),
            pointIcon, pointScale, pointOffset,
            lineColor, lineWidth, lineType, lineDash,
            polyColor, polyBorderColor, polyBorderWidth, polyBorderType, polyBorderDash
        } = Object.assign(getBaseDrawConfig(), v)
        let styles: any = []
        if (v.styles) {
            styles = v.styles
        } else {
            if (type === 'Point') {
                if (pointIcon) {
                    styles.push(new style.Style({
                        image: new style.Icon({
                            src: pointIcon,
                            scale: pointScale,
                            displacement: pointOffset
                        })
                    }))
                } else {
                    styles.push(new style.Style({
                        image: new style.Circle({
                            radius: 10,
                            fill: new style.Fill({
                                color: '#e810dd',
                            }),
                            scale: pointScale,
                            displacement: pointOffset
                        })
                    }))
                }
            } else if (type === 'LineString') {
                styles.push(new style.Style({
                    stroke: new style.Stroke({
                        color: lineColor,
                        width: lineWidth,
                        lineDash: lineDash ?? globalLineDash[Number(lineType)]
                    })
                }))
            } else if (type === 'Polygon') {
                styles.push(new style.Style({
                    stroke: new style.Stroke({
                        color: polyBorderColor,
                        width: polyBorderWidth,
                        lineDash: polyBorderDash ?? globalLineDash[Number(polyBorderType)]
                    }),
                    fill: new style.Fill({
                        color: polyColor,
                    }),
                }))
            }
            if (v.text) {
                styles.push(new style.Style({
                    text: new style.Text({
                        font: "16px bold 微软雅黑",
                        text: v.text,
                        fill: new style.Fill({
                            color: '#ffffff'
                        }),
                        stroke: new style.Stroke({
                            color: '#D26CDB',
                            width: 2
                        }),
                        offsetY: textOffsetY,
                    }),
                }))
            }
        }
        feat.set('val', v)
        feat.setStyle(styles)
        return feat
    })
    _source.addFeatures(features)
    if (isAuto) {
        getShapeView(map, autoPosition)
    }
}

let baseDrawTooltipElement;
let baseDrawHelpTooltipElement;
export const drawEdits = (map, obj, emitWkt, isAuto = true) => {
    return new Promise((resolve => {
        if (!isValue(obj.textOffsetY)) {
            obj.textOffsetY = (obj.featureType === 'Point' ? -30 : 0)
        }
        let commonStyle = (showText) => {
            if (obj.featureType === 'Point') {
                return new style.Style({
                    image: obj.pointIcon ? new style.Icon({
                        src: obj.pointIcon,
                        scale: obj.pointScale,
                        displacement: obj.pointOffset
                    }) : new style.Circle({
                        radius: 10,
                        fill: new style.Fill({
                            color: '#e810dd',
                        }),
                        scale: obj.pointScale,
                        displacement: obj.pointOffset
                    }),
                    text: (showText && obj.text) ? new style.Text({
                        font: "16px bold 微软雅黑",
                        text: obj.text,
                        fill: new style.Fill({
                            color: '#ffffff'
                        }),
                        stroke: new style.Stroke({
                            color: '#D26CDB',
                            width: 2
                        }),
                        offsetY: obj.textOffsetY,
                    }) : undefined,
                })
            } else if (obj.featureType === 'LineString') {
                return new style.Style({
                    stroke: new style.Stroke({
                        color: obj.lineColor,
                        width: obj.lineWidth,
                        lineDash: obj.lineDash ?? globalLineDash[Number(obj.lineType)]
                    }),
                    text: (showText && obj.text) ? new style.Text({
                        font: "16px bold 微软雅黑",
                        text: obj.text,
                        fill: new style.Fill({
                            color: '#ffffff'
                        }),
                        stroke: new style.Stroke({
                            color: '#D26CDB',
                            width: 2
                        }),
                        offsetY: obj.textOffsetY,
                    }) : undefined,
                })
            } else if (obj.featureType === 'Polygon') {
                return new style.Style({
                    stroke: new style.Stroke({
                        color: obj.polyBorderColor,
                        width: obj.polyBorderWidth,
                        lineDash: obj.polyBorderDash ?? globalLineDash[Number(obj.polyBorderType)]
                    }),
                    fill: new style.Fill({
                        color: obj.polyColor,
                    }),
                    text: (showText && obj.text) ? new style.Text({
                        font: "16px bold 微软雅黑",
                        text: obj.text,
                        fill: new style.Fill({
                            color: '#ffffff'
                        }),
                        stroke: new style.Stroke({
                            color: '#D26CDB',
                            width: 2
                        }),
                        offsetY: obj.textOffsetY,
                    }) : undefined,
                })
            }
        }
        const getWkt = (feature) => {
            const gm = feature.getGeometry()
            let wkt = ''
            const gType = gm.getType()
            if (gType === 'Polygon') {
                wkt = formatPosition.cpnTwpn(gm.getCoordinates())
            } else if (gType === 'LineString') {
                wkt = formatPosition.clTwl(gm.getCoordinates())
            } else if (gType === 'Circle') {
                const circlePoly = fromCircle(gm, 128)
                feature.setGeometry(circlePoly)
                wkt = formatPosition.cpnTwpn(circlePoly.getCoordinates())
            } else if (gType === 'Point') {
                wkt = formatPosition.cptTwpt(gm.getCoordinates())
            }
            emitWkt(wkt)
        }
        const reset = () => {
            const oldLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
            if (oldLayer) {
                map.removeLayer(oldLayer[0])
            }
            const oldDraw = map.getInteractions().getArray().filter(v => v.get(drawFlag[0]) === drawFlag[1])
            if (oldDraw) {
                map.removeInteraction(oldDraw[0])
            }
            const oldModify = map.getInteractions().getArray().filter(v => v.get(modifyFlag[0]) === modifyFlag[1])
            if (oldModify) {
                map.removeInteraction(oldModify[0])
            }
        }
        if (!baseDrawTooltipElement) {
            reset()
            let _source
            const realLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
            if (realLayer[0]) {
                _source = realLayer[0].getSource()
            } else {
                _source = new source.Vector(); //图层数据源
                const _vector = new layer.Vector({
                    zIndex: 9999,
                    source: _source,
                    style: commonStyle(true),
                });
                _vector.set(layerFlag[0], layerFlag[1])
                map.addLayer(_vector);
            }
            if (obj.wkt) {
                try {
                    const feat: any = new format.WKT().readFeature(obj.wkt)
                    _source.addFeature(feat)
                    const autoPosition: any = []
                    const type = feat.getGeometry().getType()
                    switch (type) {
                        case 'Point': autoPosition.push(feat.getGeometry().getCoordinates())
                            break
                        case 'LineString': autoPosition.push(...feat.getGeometry().getCoordinates())
                            break
                        case 'Polygon': autoPosition.push(...feat.getGeometry().getCoordinates()[0])
                            break
                    }
                    if (isAuto) {
                        getShapeView(map, autoPosition)
                    }
                } catch (e) {
                    obj.wkt = ''
                    ElMessage.warning('坐标格式错误,请重新标绘!')
                }
            }
            const modifyInteraction = new Modify({
                source: _source,
            });
            modifyInteraction.set(modifyFlag[0], modifyFlag[1])
            map.addInteraction(modifyInteraction)
            modifyInteraction.on('modifyend', evt => {
                try {
                    const feat = evt.features.item(0)
                    getWkt(feat)
                } catch {
                }
            })
            if (!obj.wkt) {
                let sketch;
                let helpTooltip;
                let measureTooltip;
                let continueMsg = '双击结束标绘';
                const geodesicCheckbox = true;//测地学方式对象
                const createMeasureTooltip = () => {
                    const id = 'baseDrawTooltipElementId'
                    if (baseDrawTooltipElement) {
                        map.removeOverlay(map.getOverlayById(id))
                        baseDrawTooltipElement.parentNode.removeChild(baseDrawTooltipElement);
                    }
                    baseDrawTooltipElement = document.createElement('div');
                    baseDrawTooltipElement.className = 'tooltip tooltip-measure';
                    measureTooltip = new ol.Overlay({
                        id,
                        element: baseDrawTooltipElement,
                        offset: [0, -15],
                        positioning: 'bottom-center'
                    });
                    map.addOverlay(measureTooltip);
                }
                const createHelpTooltip = () => {
                    const id = 'baseDrawHelpTooltipElementId'
                    if (baseDrawHelpTooltipElement) {
                        map.removeOverlay(map.getOverlayById(id))
                        baseDrawHelpTooltipElement.parentNode.removeChild(baseDrawHelpTooltipElement);
                    }
                    baseDrawHelpTooltipElement = document.createElement('div');
                    baseDrawHelpTooltipElement.className = 'tooltip hidden';
                    helpTooltip = new ol.Overlay({
                        id,
                        element: baseDrawHelpTooltipElement,
                        offset: [15, 0],
                        positioning: 'center-left'
                    });
                    map.addOverlay(helpTooltip);
                }
                const formatLength = (line) => {
                    // 获取投影坐标系
                    const sourceProj = map.getView().getProjection();
                    // ol/sphere里有getLength()和getArea()用来测量距离和区域面积,默认的投影坐标系是EPSG:3857, 其中有个options的参数,可以设置投影坐标系
                    const length = sphere.getLength(line, {projection: sourceProj});
                    // const length = getLength(line);
                    let output;
                    if (length > 100) {
                        const km = Math.round((length / 1000) * 100) / 100;
                        output = `${km} 千米 <br>${parseFloat(String(km * 0.53995)).toFixed(2)} 海里`;
                    } else {
                        output = `${Math.round(length * 100) / 100} m`;
                    }
                    return output;
                };
                //获取圆的面积
                const getCircleArea = (circle, projection) => {
                    const P = 3.14
                    const radius = getCircleRadius(circle, projection)
                    return P * radius * radius
                }
                //获取圆的半径
                const getCircleRadius = (circle, projection) => {
                    return circle.getRadius() * projection.getMetersPerUnit()
                }
                const formatArea = (polygon, type= 'polygon') => {
                    let area
                    const sourceProj = map.getView().getProjection();
                    // 获取投影坐标系
                    if (type === 'polygon') {
                        area = sphere.getArea(polygon, {
                            projection: sourceProj,
                        });
                    } else if (type === 'circle') {
                        area = getCircleArea(polygon, sourceProj)
                    }
                    let output;
                    if (area > 10000) {
                        const km = Math.round((area / 1000000) * 100) / 100;
                        output = `${km} 平方公里<br>${parseFloat(String(km * 0.38610)).toFixed(
                            2
                        )} 平方英里`;
                    } else {
                        output = `${Math.round(area * 100) / 100} ` + " m<sup>2</sup>";
                    }
                    return output;
                };
                const addInteraction = () => {
                    const id = 'baseDrawName'
                    let drawLastPoint = ''
                    let drawLastPointTimer: any = null
                    let drawCircleDBClickTimer: any = null
                    const draw = new interaction.Draw({
                        stopClick: true,
                        condition: (e) => {
                            // 圆形单击即触发finishCondition,跳过
                            if (obj.featureType === 'Circle') {
                                return true
                            }
                            const str = e.coordinate.join(',')
                            let flag = true
                            // 进行延时判断,避免只要鼠标不移动,单击后间隔很久也会视为双击,
                            if (!drawLastPointTimer && str === drawLastPoint) {
                                flag = false
                            } else {
                                if (drawLastPointTimer) {
                                    clearTimeout(drawLastPointTimer)
                                }
                                drawLastPoint = str
                            }
                            drawLastPointTimer = setTimeout(() => {
                                drawLastPointTimer = null
                            }, 1000)
                            return flag
                        },
                        finishCondition: (e) => {
                            if (obj.featureType !== 'Circle') {
                                return true
                            }
                            let flag = true
                            //  圆形进行双击延时监听判断
                            if (!drawCircleDBClickTimer) {
                                flag = false
                            }
                            if (drawCircleDBClickTimer) {
                                clearTimeout(drawCircleDBClickTimer)
                            }
                            drawCircleDBClickTimer = setTimeout(() => {
                                drawCircleDBClickTimer = null
                            }, 1000)
                            return flag
                        },
                        source: _source,//测量绘制层数据源
                        type: obj.featureType,  //几何图形类型
                        // geometryFunction: typeSelect === 'rectangle' ? createBox() : null,
                        style: commonStyle(obj.featureType === 'Point' ? true : false),
                    });
                    draw.set('showText', obj.featureType === 'Point' ? true : false)
                    draw.set(drawFlag[0], drawFlag[1])
                    draw.set(id, id)
                    createMeasureTooltip(); //创建测量工具提示框
                    createHelpTooltip(); //创建帮助提示框
                    map.addInteraction(draw);
                    let listener;
                    //绑定交互绘制工具开始绘制的事件
                    const drawstartHandle = (evt) => {
                        sketch = evt.feature; //绘制的要素
                        let tooltipCoord = evt.coordinate;// 绘制的坐标
                        //绑定change事件,根据绘制几何类型得到测量长度值或面积值,并将其设置到测量工具提示框中显示
                        listener = sketch.getGeometry().on('change', function (evt) {
                            const geom = evt.target
                            let output;
                            if (geom.getType() === 'LineString') {
                                output = formatLength(geom);//长度值
                                tooltipCoord = geom.getLastCoordinate();//坐标
                            } else if (geom.getType() === 'Polygon') {
                                output = formatArea(geom);//面积值
                                tooltipCoord = geom.getInteriorPoint().getCoordinates();//坐标
                            } else if (geom.getType() === 'Circle') {
                                output = formatArea(geom, 'circle');//面积值
                                tooltipCoord = geom.getCenter()
                            }
                            baseDrawTooltipElement.innerHTML = output;//将测量值设置到测量工具提示框中显示
                            measureTooltip.setPosition(tooltipCoord);//设置测量工具提示框的显示位置
                        });
                    }
                    draw.on('drawstart', drawstartHandle);
                    //绑定交互绘制工具结束绘制的事件
                    const copy = (value) => {
                        const str = document.createElement('input')
                        str.setAttribute('value', value)
                        document.body.appendChild(str)
                        str.select()
                        document.execCommand('copy')
                        document.body.removeChild(str)
                    }
                    const drawendHandle = (evt) => {
                        map.removeInteraction(map.getInteractions().getArray().filter(v => v.get(id) === id)[0]);
                        // 标绘的时候不需要最终结果dom
                        map.removeOverlay(map.getOverlayById('baseDrawHelpTooltipElementId'))
                        baseDrawTooltipElement.parentNode.removeChild(baseDrawTooltipElement);
                        sketch = null; //置空当前绘制的要素对象
                        baseDrawTooltipElement = null; //置空测量工具提示框对象
                        baseDrawHelpTooltipElement.parentNode.removeChild(baseDrawHelpTooltipElement);
                        baseDrawHelpTooltipElement = null; //置空测量工具提示框对象
                        unByKey(listener);
                        draw.un('drawstart', drawstartHandle);
                        draw.un('drawend', drawendHandle);
                        map.removeInteraction(map.getInteractions().getArray().filter(v => v.get(id) === id)[0]);
                        map.un('pointermove', pointerMoveHandler)
                        const baseDrawFeature = evt.feature
                        getWkt(baseDrawFeature)
                    }
                    draw.on('drawend', drawendHandle);
                }
                addInteraction(); //调用加载绘制交互控件方法,添加绘图进行测量
                const pointerMoveHandler = (evt) => {
                    if (evt.dragging) {
                        return;
                    }
                    let helpMsg = '单击开始标绘';//当前默认提示信息
                    //判断绘制几何类型设置相应的帮助提示信息
                    if (sketch) {
                        const geom = sketch.getGeometry()
                        helpMsg = continueMsg;
                        // if (geom.getType() === 'Polygon') {
                        //     helpMsg = continueMsg; //绘制多边形时提示相应内容
                        // } else if (geom.getType() === 'LineString') {
                        //     helpMsg = continueMsg; //绘制线时提示相应内容
                        // }
                    }
                    if (baseDrawHelpTooltipElement) {
                        baseDrawHelpTooltipElement.innerHTML = helpMsg; //将提示信息设置到对话框中显示
                        helpTooltip.setPosition(evt.coordinate);//设置帮助提示框的位置
                        baseDrawHelpTooltipElement.classList.remove('hidden');//移除帮助提示框的隐藏样式进行显示
                    }
                };
                map.on('pointermove', pointerMoveHandler); //地图容器绑定鼠标移动事件,动态显示帮助提示框内容
                //地图绑定鼠标移出事件,鼠标移出时为帮助提示框设置隐藏样式
                try {
                    map.getViewport().on('mouseout', () => {
                        baseDrawHelpTooltipElement.addClass('hidden');
                    });
                } catch (e) {
                }
            }
        }
        resolve(() => {
            const oldLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
            if (oldLayer) {
                oldLayer[0].setStyle(commonStyle(true))
            }
            // const oldDraw = map.getInteractions().getArray().filter(v => v.get(drawFlag[0]) === drawFlag[1])
            // if (oldDraw) {
            //     oldDraw[0].setStyle(commonStyle(oldDraw[0].get('showText')))
            // }
        })
    }))
}
export const drawExit = (map) => {
    if (baseDrawTooltipElement) {
        baseDrawTooltipElement.parentNode?.removeChild?.(baseDrawTooltipElement);
        baseDrawTooltipElement = null
    }
    if (baseDrawHelpTooltipElement) {
        baseDrawHelpTooltipElement.parentNode?.removeChild?.(baseDrawHelpTooltipElement);
        baseDrawHelpTooltipElement = null
    }
    const oldLayer = map.getLayers().getArray().filter(v => v.get(layerFlag[0]) === layerFlag[1])
    if (oldLayer) {
        map.removeLayer(oldLayer[0])
    }
    const oldDraw = map.getInteractions().getArray().filter(v => v.get(drawFlag[0]) === drawFlag[1])
    if (oldDraw) {
        map.removeInteraction(oldDraw[0])
    }
    const oldModify = map.getInteractions().getArray().filter(v => v.get(modifyFlag[0]) === modifyFlag[1])
    if (oldModify) {
        map.removeInteraction(oldModify[0])
    }
}

const getShapeView = (map, position, L = 600) => {
    const center = extent.getCenter(extent.boundingExtent(position))
    let x = 0
    let y = 0
    position.forEach(v => {
        if (Math.abs(v[0] - center[0]) > x) {
            x = Math.abs(v[0] - center[0])
        }
        if (Math.abs(v[1] - center[1]) > y) {
            y = Math.abs(v[1] - center[1])
        }
    })
    const resolution = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) / (L / document.body.clientWidth * document.body.clientHeight)
    if (map) {
        if (position.length > 1) {
            map.getView().animate({
                center, resolution
            })
        } else {
            map.getView().animate({
                center, zoom: 12
            })
        }
    }
    return {
        center, resolution
    }
}