    import * as L from '@mapgis/leaflet'
    // eslint-disable-next-line import/no-extraneous-dependencies
    import * as T from '@turf/turf'
    import { defaultValue, FetchMethod } from '@mapgis/webclient-common'

    L.TileLayer.Clip = L.TileLayer.extend({
      options: {
        clippingArea: null
      },
      initialize(url, options) {
        L.TileLayer.prototype.initialize.call(this, url, options)
        // 缓存clippingArea像素边界
        this._clippingAreaPx = null
        // 缓存瓦片裁剪区
        this._TileClippingGeometryCache = {}
        // 设置请求方式
        this.httpMethod = defaultValue(options.httpMethod, FetchMethod.get)
      },
      createTile(coords, done) {
        const src = this.getTileUrl(coords)
        return this._drawClippingTile(src, coords, done)
      },
      /**
       * @description: 设置裁剪区域
       * @param {*} data
       * @return {*}
       */
      setClippingArea(data) {
        this.options.clippingArea = data
        this._clearClippingCache()
        this.redraw()
        return this
      },
      /**
       * @description: 获取裁剪区域
       * @return {*}
       */
      getClippingArea() {
        return this.options.clippingArea
      },
      /**
       * @description: 根据瓦片行列号获取空间裁剪区
       * @param {*} x
       * @param {*} y
       * @param {*} z
       * @return {*}
       */
      _getTileGeometryAndState(x, y, z) {
        // 不设置裁剪区默认不裁剪
        if (!this.options.clippingArea) {
          return {
            in: true,
            geometry: null
          }
        }
        // 缓存瓦片id
        const cacheId = `${x},${y},${z}`
        // 返回缓存的几何
        if (this._TileClippingGeometryCache[cacheId]) {
          return {
            intersect: true,
            geometry: this._TileClippingGeometryCache[cacheId]
          }
        }

        const clippingAreaPx = this._getClippingAreaPx()
        const tileSize = this.options.tileSize
        const zLevel = Math.pow(2, z - this.options.zoomOffset)
        // 计算0级范围
        const x1 = (x * tileSize) / zLevel
        const y1 = (y * tileSize) / zLevel
        const x2 = ((x + 1) * tileSize) / zLevel
        const y2 = ((y + 1) * tileSize) / zLevel
        const tileBbox = T.polygon([
          [
            [x1, y1],
            [x2, y1],
            [x2, y2],
            [x1, y2],
            [x1, y1]
          ]
        ])

        // 判断裁剪区>瓦片范围
        if (T.booleanContains(clippingAreaPx, tileBbox)) {
          return {
            in: true,
            geometry: null
          }
        }

        // 判断裁剪区<瓦片范围
        if (T.booleanContains(tileBbox, clippingAreaPx)) {
          this._TileClippingGeometryCache[cacheId] = clippingAreaPx
          return {
            intersect: true,
            geometry: clippingAreaPx
          }
        }

        // 判断是否相交
        if (!T.booleanOverlap(clippingAreaPx, tileBbox)) {
          return {
            out: true,
            geometry: null
          }
        }

        const intersectResult = T.intersect(clippingAreaPx, tileBbox)
        if (!intersectResult) {
          return {
            out: true,
            geometry: null
          }
        }

        this._TileClippingGeometryCache[cacheId] = intersectResult

        return {
          intersect: true,
          geometry: intersectResult
        }
      },
      /**
       * 获取合并后的点集
       * @param {Number[][][]} polygon 面
       * @return {Number[][][]} 计算像素坐标系点
       */
      _toMercGeometry(polygons) {
        // 缓存0级地图下几何的相对位置
        const rings = []
        for (let j = 0; j < polygons.length; j++) {
          const ring = []
          for (let k = 0; k < polygons[j].length; k++) {
            const p = this._map.project(
              L.latLng(polygons[j][k][1], polygons[j][k][0]),
              0
            )
            ring.push([p.x, p.y])
          }
          rings.push(ring)
        }
        return rings
      },
      /**
       * 计算裁剪区像素坐标几何以及更新其四至
       * @return {Object|null} 合并后的点集
       */
      _getClippingAreaPx() {
        if (this._clippingAreaPx) {
          return this._clippingAreaPx
        }
        if (this.options.clippingArea) {
          this._clippingAreaPx = T.polygon(
            this._toMercGeometry(this.options.clippingArea)
          )
          this._clippingAreaPxBounds = T.bbox(this._clippingAreaPx)
        } else {
          this._clippingAreaPx = null
          this._clippingAreaPxBounds = null
        }
        return this._clippingAreaPx
      },
      /**
       * @description: 清空缓存
       * @return {*}
       */
      _clearClippingCache() {
        this._clippingAreaPx = null
        this._clippingAreaPxBounds = null
        this._TileClippingGeometryCache = {}
      },

      /**
       * @description: 重绘图片
       * @param {*} url
       * @param {*} coords
       * @param {*} done
       * @return {*}
       */
      _drawClippingTile(url, coords, done) {
        const { x, y, z } = coords
        const state = this._getTileGeometryAndState(x, y, z)
        // 创建tile
        const tile = document.createElement('img')
        // 处理状态
        if (state.in) {
          return this._getTileImage(tile, url, done)
        }

        if (state.out) {
          const canvas = document.createElement('canvas')
          canvas.width = this.options.width
          canvas.height = this.options.height
          return this._getTileImage(tile, null, done)
        }

        if (state.intersect && state.geometry) {
          this._getTileImage(tile, url, done)
          // 创建绘制的canvas
          const canvas = document.createElement('canvas')
          canvas.width = this.options.tileSize
          canvas.height = this.options.tileSize
          const ctx = canvas.getContext('2d')
          const tileSize = this.options.tileSize
          const tileX = tileSize * x
          const tileY = tileSize * y
          const zLevel = Math.pow(2, z - this.options.zoomOffset)
          const geometryCoords = T.coordAll(state.geometry)
          // 图像加载完毕绘制canvas
          tile.onload = function () {
            canvas.complete = true
            // 定时器
            setTimeout(() => {
              const pattern = ctx.createPattern(tile, 'repeat')
              let drawX
              let drawY
              // 开始绘制
              ctx.beginPath()
              // 移动到开始点
              drawX = geometryCoords[0][0] * zLevel - tileX
              drawY = geometryCoords[0][1] * zLevel - tileY
              ctx.moveTo(drawX, drawY)
              for (let j = 1; j < geometryCoords.length; j++) {
                drawX = geometryCoords[j][0] * zLevel - tileX
                drawY = geometryCoords[j][1] * zLevel - tileY
                // 开始绘制
                ctx.lineTo(drawX, drawY)
              }
              // 设置裁剪
              ctx.clip()
              ctx.beginPath()
              ctx.rect(0, 0, canvas.width, canvas.height)
              ctx.fillStyle = pattern
              ctx.fill()
              done()
            }, 0)
          }

          if (this.options.crossOrigin) {
            tile.crossOrigin = ''
          }
          return canvas
        }
      },
      _getTileImage(tile, url, done) {
        if (!url) return tile
        L.DomEvent.on(tile, 'load', L.Util.bind(this._tileOnLoad, this, done, tile))
        L.DomEvent.on(
          tile,
          'error',
          L.Util.bind(this._tileOnError, this, done, tile)
        )
        if (this.options.crossOrigin || this.options.crossOrigin === '') {
          tile.crossOrigin =
            this.options.crossOrigin === true ? '' : this.options.crossOrigin
        }
        // for this new option we follow the documented behavior
        // more closely by only setting the property when string
        if (typeof this.options.referrerPolicy === 'string') {
          tile.referrerPolicy = this.options.referrerPolicy
        }
        // The alt attribute is set to the empty string,
        // allowing screen readers to ignore the decorative image tiles.
        // https://www.w3.org/WAI/tutorials/images/decorative/
        // https://www.w3.org/TR/html-aria/#el-img-empty-alt
        tile.alt = ''

        // 验证是否为base64字符串
        let imageUrl = url
        if (!/data:image\/jpg;base64/.test(url)) {
          imageUrl = this._getInterceptorsUrl(url)
          const config = this._map.options.config
          tile.onload = function () {
            L.InterceptorsUtils.serviceConfigResponseInterceptors(
              config,
              imageUrl,
              tile
            )
          }
        }

        if (this.httpMethod === FetchMethod.post) {
          this._postImage(tile, url)
        } else {
          tile.src = imageUrl
        }
        return tile
      },

      // 由子类实现
      _postImage(tile, options) {},

      _getInterceptorsUrl(url) {
        // 处理拦截器代码
        const config = this._map.options.config
        const tokenStr = `&${L.InterceptorsUtils.serviceConfigToken(config)}`
        let imageUrl = L.InterceptorsUtils.serviceConfigRequestInterceptors(
          config,
          url
        )
        if (tokenStr !== '&') {
          imageUrl += tokenStr
        }
        return imageUrl
      }
    })

    L.TileLayer.clip = function (url, options) {
      return new L.TileLayer.Clip(url, options)
    }