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 proj from 'ol/proj'
import * as format from 'ol/format'
import * as extent from 'ol/extent'
import * as Coordinate from 'ol/coordinate'
import store from '@/store/index'

let hasPointClick = false
let zIndex = 999
let ctrlHideListGlobal: any = []
const activeFeatureMap = new Map()
const hoverFeatureMap = new Map()
let moveFlag = false

export const initShape = ({map, layerName, list, clickEl, hoverEl, hoverClick = false, clickHandle = (feature: any) => {}, layerZIndex}) => {
  const _layers = map.getLayers().getArray().filter(v => v.get('easyMapLayerName') === layerName)
  let realLayer = _layers.length > 0 ? _layers[0] : null
  const features: any = []
  const featuresMap = new Map()
  list.forEach((v, i) => {
    try {
      const feat = new format.WKT().readFeature(v.easyMapParams.position)
      feat.set('layerName', layerName)
      feat.set('easyMap', v)
      feat.set('_geom', feat.getGeometry())
      feat.setStyle(v.easyMapParams.normalStyle)
      feat.set('normalStyle', v.easyMapParams.normalStyle)
      feat.set('activeStyle', v.easyMapParams.activeStyle ? v.easyMapParams.activeStyle : v.easyMapParams.normalStyle)
      feat.set('hoverStyle', v.easyMapParams.hoverStyle ? v.easyMapParams.hoverStyle : (v.easyMapParams.activeStyle ? v.easyMapParams.activeStyle : v.easyMapParams.normalStyle))
      feat.set('backStyle', v.easyMapParams.normalStyle)
      feat.set('layerZ', realLayer ? realLayer.getZIndex() : layerZIndex ? layerZIndex : zIndex)
      feat.set('featureZ', i)
      feat.set('value', v.value)
      feat.setId(v.easyMapParams.id)
      v.easyMapParams.featureSetHandle?.(feat)
      features.push(feat)
      featuresMap.set(v.easyMapParams.id, feat)
    } catch (e) {
      console.log('v：\n%o  e：\n%o', v, e)
    }
  })
  const vectorSource = new source.Vector({
    features: features,
    wrapX: false
  });
  let clickDialog = clickEl ? map.getOverlayById(clickEl.id) : null
  let hoverDialog = hoverEl ? map.getOverlayById(hoverEl.id) : null
  const clickClose = () => {
    clickDialog?.setPosition(undefined)
  }
  const hoverClose = () => {
    hoverDialog?.setPosition(undefined)
  }
  const setActive = (feature, isClick = false, e = null) => {
    if (feature) {
      feature.set('backStyle', feature.getStyle())
      feature.setStyle(hoverFeatureMap.get(layerName)?.getId() === feature.getId() ? feature.get('hoverStyle') : feature.get('activeStyle'))
      activeFeatureMap.set(layerName, feature)
      store.dispatch('easyMap/LOAD_INFO', {
        type: 'click',
        data: {
          layerName: layerName,
          value: feature.get('easyMap'),
          feature: feature,
          isClick,
          e
        }
      })
    }
  }
  const setHover = (feature) => {
    if (feature) {
      if (hoverDialog && !(hoverClick || activeFeatureMap.get(layerName)?.getId() !== hoverFeatureMap.get(layerName)?.getId())) {
        hoverClose()
      }
      feature.set('backStyle', feature.getStyle())
      feature.setStyle(feature.get('hoverStyle'))
      hoverFeatureMap.set(layerName, feature)
      store.dispatch('easyMap/LOAD_INFO', {
        type: 'hover',
        data: {
          layerName: layerName,
          value: feature.get('easyMap'),
          feature: feature
        }
      })
    }
  }
  if (hoverFeatureMap.get(layerName)) {
    hoverFeatureMap.set(layerName, vectorSource.getFeatureById(hoverFeatureMap.get(layerName).getId()))
    setHover(hoverFeatureMap.get(layerName))
  } else {
    hoverFeatureMap.set(layerName, null)
  }
  if (activeFeatureMap.get(layerName)) {
    activeFeatureMap.set(layerName, vectorSource.getFeatureById(activeFeatureMap.get(layerName).getId()))
    setActive(activeFeatureMap.get(layerName))
  } else {
    activeFeatureMap.set(layerName, null)
  }
    if (realLayer) {
    realLayer.setSource(vectorSource)
  } else {
      realLayer = new layer.VectorImage(<any>{
      source: vectorSource,
      easyMapLayerName: layerName,
      layerType: 'easyMap',
      zIndex: layerZIndex ? layerZIndex : zIndex--
    })
    map.addLayer(realLayer)
    map.on('movestart', e => {
      moveFlag = true
      map.un('pointermove', mapPointerMove)
      map.un('singleclick', mapSingleClick)
      map.un('contextmenu', mapContextmenu)
    })
    map.on('moveend', e => {
      map.on('singleclick', mapSingleClick)
      map.on('pointermove', mapPointerMove)
      map.on('contextmenu', mapContextmenu)
      moveFlag = false
    })
    if (clickEl) {
      clickDialog = setOverlay(clickEl)
      map.addOverlay(clickDialog)
    }
    const mapSingleClick = e => {
      let pointFlag = true
      clickDialog?.setPosition(undefined)
      // activeFeatureMap.get(layerName)?.setStyle(activeFeatureMap.get(layerName).get('normalStyle'))
      let activeLayer = {
        zIndex: 0,
        layerName: null,
      }
      map.forEachFeatureAtPixel(e.pixel, (feature) => {
        if (feature.get('layerZ') >= activeLayer.zIndex) {
          activeLayer = {
            zIndex: feature.get('layerZ'),
            layerName: feature.get('layerName'),
          }
        }
      }, {
        hitTolerance: 0,
      });
      map.forEachFeatureAtPixel(e.pixel, (feature) => {
        if (activeLayer.layerName === layerName && feature.get('layerName') === layerName && !hasPointClick) {
          if (pointFlag) {
            hasPointClick = true
            if (activeFeatureMap.get(layerName)) {
              activeFeatureMap.get(layerName).set('backStyle', activeFeatureMap.get(layerName).getStyle())
              activeFeatureMap.get(layerName).setStyle(feature.get('normalStyle'))
            }
            pointFlag = false
            if (!hoverClick) {
              hoverClose()
              hoverFeatureMap.delete(layerName)
            }
            setActive(feature, true, e)
            clickHandle(feature)
            if (clickDialog) {
              if (feature.getGeometry().getType() !== 'Point') {
                clickDialog.setPosition(e.coordinate)
              } else {
                clickDialog.setPosition(feature.getGeometry().getCoordinates())
              }
            }
            setTimeout(() => {
              hasPointClick = false
            }, 100)
          }
        }
      }, {
        hitTolerance: 0,
      });
    }
    map.on('singleclick', mapSingleClick)
    if (hoverEl) {
      hoverDialog = setOverlay(hoverEl)
      map.addOverlay(hoverDialog)
    }
    let pointerMoveTime: any = null
    const mapPointerMove = e => {
      clearTimeout(pointerMoveTime)
      pointerMoveTime = setTimeout(() => {
        if (!moveFlag) {
          let isFeature = false
          let hoverLayer = {
            zIndex: 0,
            layerName: null
          }
          map.forEachFeatureAtPixel(e.pixel, (feature) => {
            if (feature.get('layerZ') > hoverLayer.zIndex) {
              hoverLayer = {
                zIndex: feature.get('layerZ'),
                layerName: feature.get('layerName')
              }
            }
            isFeature = true
          }, {
            hitTolerance: 0,
          });
          const reset = () => {
            hoverDialog?.setPosition(undefined)
            hoverFeatureMap.get(layerName)?.setStyle(hoverFeatureMap.get(layerName).get('backStyle'))
            hoverFeatureMap.delete(layerName)
          }
          if (layerName !== hoverLayer.layerName) {
            reset()
          }
          if (!isFeature) {
            reset()
          }
          let pointFlag = true
          map.forEachFeatureAtPixel(e.pixel, (feature) => {
            if (feature.get('layerName') === layerName) {
              if (pointFlag) {
                if (layerName !== hoverLayer.layerName) {
                  hoverDialog?.setPosition(undefined)
                } else {
                  pointFlag = false
                  if (hoverFeatureMap.get(layerName)?.getId() !== feature.getId()) {
                    if (feature.getId() !== hoverFeatureMap.get(layerName)?.getId()) {
                      reset()
                    }
                    setHover(feature)
                    if (hoverDialog && (hoverClick || activeFeatureMap.get(layerName)?.getId() !== hoverFeatureMap.get(layerName)?.getId())) {
                      if (feature.getGeometry().getType() !== 'Point') {
                        hoverDialog.setPosition(e.coordinate)
                      } else {
                        hoverDialog.setPosition(feature.getGeometry().getCoordinates())
                      }
                    }
                  }
                }
              }
            }
          }, {
            hitTolerance: 0,
          });
        }
      }, 10)
    }
    map.on('pointermove', mapPointerMove)
    let ctrlHideList: any = []
    const mapContextmenu = e => {
      let isFeature = false
      let hoverLayer = {
        zIndex: 0,
        layerName: null
      }
      map.forEachFeatureAtPixel(e.pixel, (feature) => {
        if (feature.get('layerZ') > hoverLayer.zIndex) {
          hoverLayer = {
            zIndex: feature.get('layerZ'),
            layerName: feature.get('layerName')
          }
        }
        isFeature = true
      }, {
        hitTolerance: 0,
      });
      // @ts-ignore
      if (ctrlHideList.length > 0 && !window?.event?.ctrlKey && !window?.event.altKey) {
        if (ctrlHideListGlobal[ctrlHideListGlobal.length - 1] === ctrlHideList[ctrlHideList.length - 1]) {
          setTimeout(() => {
            switchVisible([ctrlHideList.pop()], true)
            ctrlHideListGlobal.pop()
          }, 100)
        }
      } else { // @ts-ignore
        if (ctrlHideList.length > 0 && window?.event.altKey) {
          switchVisible(ctrlHideList, true)
          ctrlHideList = []
          ctrlHideListGlobal = []
        }
      }
      let pointFlag = true
      map.forEachFeatureAtPixel(e.pixel, (feature) => {
        if (feature.get('layerName') === layerName) {
          if (pointFlag) {
            if (feature.get('layerName') === hoverLayer.layerName) {
              pointFlag = false
              // @ts-ignore
              if (window?.event?.ctrlKey) {
                switchVisible([feature.get('easyMap').easyMapParams.id], false)
                ctrlHideList.push(feature.get('easyMap').easyMapParams.id)
                ctrlHideListGlobal.push(feature.get('easyMap').easyMapParams.id)
              }
            }
          }
        }
      }, {
        hitTolerance: 0,
      });
    }
    map.on('contextmenu', mapContextmenu)
  }
  const switchVisible = (arr, show) => {
    arr.forEach(v => {
      const feat = featuresMap.get(v)
      if (show) {
        feat.setGeometry(feat.get('_geom'))
      } else {
        if (feat.getGeometry().getCoordinates().toString() !== [0, 0].toString()) {
          feat.setGeometry(new geom.Point([0, 0]))
        }
      }
    })
  }
  const removeRealLayer = () => {
    map.removeOverlay(clickDialog)
    map.removeOverlay(hoverDialog)
    map.removeLayer(realLayer)
  }
  return {
    clickDialog, hoverDialog, clickClose, hoverClose, switchVisible, features, realLayer, removeRealLayer, setActive
  }
}

const setOverlay = (el) => {
  const over = new ol.Overlay({
    id: el.id,
    element: el.element,
    autoPan: false,
    offset: el.offset,
    positioning: el.positioning,
    stopEvent: true,
    autoPanAnimation: {
      duration: 250
    }
  })
  return over
}

/**
 *
 * @param map         地图实例，传了直接定位到中心点最大可视范围，不传返回中心点和最大分辨率
 * @param position    坐标数组
 * @param L           缩放比例系数，数值越小，要素边界越靠近中心
 */
export 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) {
    map.getView().animate({
      center, resolution
    })
  }
  return {
    center, resolution
  }
}

export const getShapeView2 = (map, source, L = 600) => {
  const features = source.getFeatures();
  const bounce = extent.boundingExtent(
    features.map((feature) => feature.getGeometry().getExtent())
  );
  const center = extent.getCenter(bounce);

  let x = 0;
  let y = 0;
  [[bounce[0], bounce[1]], [bounce[2], bounce[3]]].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) {
    map.getView().animate({
      center,
      resolution,
    });
  }
  return {
    center,
    resolution,
  };
};

export const getShapeView3 = (map, source, L = 600) => {
  map.getView().fit(source.getExtent(), {
    size: map.getSize(),
    padding: [50, 50, 50, 50],
    duration: 1000,
  });
};

export const formatPosition = {
  wptTwl: (arr) => {  // WKT POINT ARR TO WKT LINESTRING
    // @ts-ignore
    const temp = arr.map(v => (new format.WKT().readFeature(v).getGeometry().getCoordinates()))
    return new format.WKT().writeGeometry(new geom.LineString(proj.fromLonLat(temp, 'EPSG:4326')), {
      dataProjection: 'EPSG:4326'
    })
  },
  cptTwpt: (cpt) => {  // coordinates POINT TO WKT POINT
    return `POINT(${cpt[0]} ${cpt[1]})`
  },
  wptTcpt: (wpt) => {  // WKT POINT TO coordinates POINT
    // @ts-ignore
    return new format.WKT().readFeature(wpt).getGeometry().getCoordinates()
  },
  wptTcptF: (wpt, digits = 0) => {  // WKT POINT TO coordinates format东经北纬 POINT
    // @ts-ignore
    const coor = new format.WKT().readFeature(wpt).getGeometry().getCoordinates()
    const f = Coordinate.toStringHDMS(coor, digits).split(' ')
    const formatLongitude = `${f[4]}${f[5]}${f[6]}${f[7]}`
    const formatLatitude = `${f[0]}${f[1]}${f[2]}${f[3]}`
    return [formatLongitude, formatLatitude]
  },
  wpnTcpn: (wpn) => {  // WKT POLYGON TO coordinates POLYGON
    // @ts-ignore
    return new format.WKT().readFeature(wpn).getGeometry().getCoordinates()
  },
  cpnTwpn: (cpn) => {  // coordinates POLYGON TO WKT POLYGON
    return `POLYGON(${cpn.map(v => '(' + v.map(c => `${c[0]} ${c[1]}`).join(',') + ')')})`
  },
  cmpnTwmpn: (cmpt) => {  // coordinates MULTIPOLYGON TO WKT MULTIPOLYGON
    return `MULTIPOLYGON(${cmpt.map(v1 => `(${v1.map(v => '(' + v.map(c => `${c[0]} ${c[1]}`).join(',') + ')')})`)})`
  },
  wmpnTcmpn: (wmpn) => {  // WKT MULTIPOLYGON TO coordinates MULTIPOLYGON
    // @ts-ignore
    return new format.WKT().readFeature(wmpn).getGeometry().getCoordinates()
  },
  wlTcl: (wl) => {  // WKT LINESTRING TO coordinates LINESTRING
    // @ts-ignore
    return new format.WKT().readFeature(wl).getGeometry().getCoordinates()
  },
  clTwl: (cl) => {  // coordinates LINESTRING TO WKT LINESTRING
    return `LINESTRING(${cl.map(c => `${c[0]} ${c[1]}`).join(',')})`
  }
}
