import MyMap from '../my-map'
import {
  CallBack,
  MapConfig,
  Listeners,
  ControlOptions,
  Location,
  FocusOptions,
  Controls,
  SMapConfig,
  Layers,
  LayerOption,
  ClickCallBack,
  BoundaryOption,
} from '../types'
import { toLines, toPoints, toPolygons } from './util'

/** 测绘院地图 */
export default class S_Map extends MyMap {
  constructor(config: MapConfig<SMapConfig>) {
    if (!config.netType) {
      throw new Error('SMap需要设置netType参数！')
    }
    new SMap.Network().setNet(config.netType)
    const instance = new SMap.Map(config.el, {
      appKey: config.appKey,
      viewMode: config.mode,
      center: config.center,
      zoom: config.zoom,
      zooms: config.zooms,
      pitch: config.pitch,
      mapStyle: config.style,
      showBuildingBlock: config.showBuildingBlock,
      rotateEnable: config.rotateEnable,
    })
    super(instance)
    this._setListeners()
    this._setControls()
    this._setLayers()
    this.on('load', this._clearFooter)
  }

  /** 清除地图自带的脚注 */
  private _clearFooter() {
    const footer = document.querySelector(
      '.esri-ui-manual-container>.esri-component',
    )
    footer && ((footer as HTMLElement).style.display = 'none')
  }

  /** 设置监听事件 */
  private _setListeners() {
    const _listeners: Partial<Listeners> = {
      load: (cb: CallBack) => {
        this.map.on(SMap.MapEvent.maploaded, cb)
      },
      zoom: (cb: CallBack) => {
        this.map.on(SMap.MapEvent.zoomchanged, cb)
      },
      move: (cb: CallBack) => {
        this.map.on(SMap.MapEvent.centerchanged, cb)
      },
      blur: (cb: CallBack) => {
        this.map.on(SMap.MapEvent.blur, cb)
      },
      focus: (cb: CallBack) => {
        this.map.on(SMap.MapEvent.focus, cb)
      },
      drag: (cb: CallBack) => {
        this._addEvent(SMap.MapEvent.drag, cb)
      },
      resize: (cb: CallBack) => {
        this.map.on(SMap.MapEvent.resize, cb)
      },
      click: (cb: CallBack) => {
        this._addEvent(SMap.MapEvent.click, cb)
      },
      dblclick: (cb: CallBack) => {
        this._addEvent(SMap.MapEvent.doubleclick, cb)
      },
      mousewheel: (cb: CallBack) => {
        this._addEvent(SMap.MapEvent.mousewheel, cb)
      },
    }
    this._listeners = Object.assign(this._listeners, _listeners)
  }

  /** 特殊的可能会带自定义数据的事件 */
  private _addEvent(event: unknown, cb: ClickCallBack) {
    this.map.on(event, (view: any, e: any) => {
      const arg = {
        type: e.type,
        center: [e.mapPoint?.longitude, e.mapPoint?.latitude],
        event: e.native,
        clientX: e.x,
        clientY: e.y,
        x: e?.mapPoint?.x,
        y: e?.mapPoint?.y,
        originData: e,
      }
      view.hitTest(e).then((res: any) => {
        cb(arg, res?.results?.[0]?.graphic?.attributes || null)
      })
    })
  }

  /** 设置控制器 */
  private _setControls() {
    const makeControl = (constructorFn: any, options?: ControlOptions) => {
      this.map.addControl(
        new constructorFn({
          visible: options?.show,
          position: options?.position,
        }),
      )
    }
    const _controls: Partial<Controls> = {
      home: (options?: ControlOptions) => {
        makeControl(SMap.Home, options)
      },
      compass: (options?: ControlOptions) => {
        makeControl(SMap.Compass, options)
      },
      zoom: (options?: ControlOptions) => {
        makeControl(SMap.Zoom, options)
      },
      fullScreen: (options?: ControlOptions) => {
        makeControl(SMap.Fullscreen, options)
      },
      layerList: (options?: ControlOptions) => {
        makeControl(SMap.LayerListControl, options)
      },
      measureLine: (options?: ControlOptions) => {
        makeControl(SMap.MeasureLine, options)
      },
      measureArea: (options?: ControlOptions) => {
        makeControl(SMap.MeasureArea, options)
      },
      basemapToggle: (options?: ControlOptions) => {
        makeControl(SMap.BasemapToggle, options)
      },
      underguroundSwitch: (options?: ControlOptions) => {
        makeControl(SMap.UndergroundSwitch, options)
      },
      bMapGallery: (options?: ControlOptions) => {
        makeControl(SMap.BMapGallery, options)
      },
      bMapGalleryexpand: (options?: ControlOptions) => {
        makeControl(SMap.BMapGalleryExpand, options)
      },
    }
    this._controls = Object.assign(this._controls, _controls)
  }

  /** 设置覆盖物 */
  private _setLayers() {
    const makeLayer = (group: unknown[]) => {
      const layer = new SMap.OverlayGroup(group, {})
      this.map.add(layer)
      return layer
    }
    const _layers: Partial<Layers> = {
      point: (data: LayerOption) => makeLayer(toPoints(data)),
      line: (data: LayerOption) => makeLayer(toLines(data)),
      polygon: (data: LayerOption) => makeLayer(toPolygons(data)),
    }
    this._layers = Object.assign(this._layers, _layers)
  }

  zoomIn() {
    this.map.zoomIn()
  }
  zoomOut() {
    this.map.zoomOut()
  }
  zoomTo(level: number) {
    this.map.setZoom(level)
  }
  focus(location: Location, options?: FocusOptions) {
    const level = options?.zoom || this.map.getZoom()
    this.map.setZoomAndCenter(level, location)
  }
  remove(layer: unknown) {
    layer && this.map.remove(layer)
  }

  /** 添加边界 */
  addBoundary({
    name = '',
    type = 'jd_boundary',
    weight = 10,
    count = 10,
    color = 'rgba(51,145,255,.6)',
    maskColor = [0, 17, 33, 0.9],
  }: BoundaryOption) {
    const boundary = new Plugins.MaskBoundary(this.map.view)
    boundary.add({
      boundaryType: type,
      boundaryDefinition: `name like '%${name}%'`,
      boundarydistance: weight,
      bounarycount: count,
      boundaryColor: color,
      maskColor: maskColor,
    })
    return boundary
  }
}
