import nanoid from 'nanoid'
import olLayerVector from 'ol/layer/Vector'
import OpenLayersParser from 'geostyler-openlayers-parser'
import olStyleStyle from 'ol/style/Style'
import olStyleFill from 'ol/style/Fill'
import olStyleStroke from 'ol/style/Stroke'
import olStyleCircle from 'ol/style/Circle'
import olGeomPoint from 'ol/geom/Point'
import olGeomLinestring from 'ol/geom/LineString'
import olGeomMultiPoint from 'ol/geom/MultiPoint'
import olGeomMultiLinestring from 'ol/geom/MultiLineString'

/**
 * VectorLayer class extends olLayerVector {@link https://openlayers.org/en/latest/apidoc/module-ol_layer_Vector-VectorLayer.html}
 * @function
 * @category Classes
 * @since 0.1.0
 * @param {Object} [opts] - Object of optional params for olLayerVector
 */
class VectorLayer extends olLayerVector {
  constructor (opts) {
    if (!opts?.className) opts.className = `_ol_kit_vector_layer_${nanoid()}`
    super(opts)

    this.parser = new OpenLayersParser()
    this.userStyles = []
    this.defaultStyles = []
    this._defaultStylesCache = []
    this.isVectorLayer = true
    if (!opts?.style) this._setInitialStyle()
    this.setDefaultVectorStyles()

    return this
  }

  /**
   * A function that returns the VectorLayers attributes
   * @function
   * @since 0.1.0
   * @returns {Array} VectorLayer attributes
   */
  getAttributes () {
    return Object.keys(this.getSource().getFeatures()[0].getProperties())
  }

  /**
   * A function that returns the VectorLayers values of a specific attribute
   * @function
   * @since 0.1.0
   * @param {String} - olFeature property
   * @returns {Array} VectorLayer values of a specific attribute
   */
  fetchValuesForAttribute (attribute) {
    const dupes = this.getSource().getFeatures().map(feature => feature.getProperties()[`${attribute}`])

    return [...new Set(dupes)]
  }

  /**
   * A function that sets custom styles from ManageLayer
   * @function
   * @since 0.1.0
   * @param {Object} - Geostyler OpenLayers Parser rules object {@link https://github.com/geostyler/geostyler-openlayers-parser}
   */
  setUserVectorStyles (styles) {
    this.userStyles = styles

    this._applyVectorStyles()
  }

  /**
   * A function that returns the VectorLayers custom user styles
   * @function
   * @since 0.1.0
   * @returns {Object} Geostyler rules object
   */
  getUserVectorStyles () {
    return this.userStyles
  }

  /**
   * A function that sets default styles of a VectorLayer
   * @function
   * @since 0.1.0
   */
  setDefaultVectorStyles () {
    return this.parser.readStyle(this.getStyleFunction()()).then(style => {
      this._defaultStylesCache = style.rules
      this.defaultStyles = style.rules
    })
  }

  /**
   * A function that returns the VectorLayers default VectorLayer styles
   * @function
   * @since 0.1.0
   * @returns {Object} Geostyler rules object
   */
  getDefaultVectorStyles () {
    return this.defaultStyles
  }

  /**
   * A function that updates default styles of a VectorLayer
   * @function
   * @since 0.1.0
   * @param {Object} - Geostyler OpenLayers Parser rules object {@link https://github.com/geostyler/geostyler-openlayers-parser}
   */
  updateDefaultVectorStyles (styles) {
    this.defaultStyles = styles

    this._applyVectorStyles()
  }

  /**
   * A function that resets default styles of a VectorLayer back to its original default
   * @function
   * @since 0.1.0
   */
  resetDefaultVectorStyles () {
    this.defaultStyles = this._defaultStylesCache

    this._applyVectorStyles()
  }

  _applyVectorStyles () {
    const filteredUserStyles = this.getUserVectorStyles().filter(style => {
      // do a safe check for the filter key
      if (!Array.isArray(style.filter)) return true
      const attributeValue = style.filter[1][2]

      return attributeValue !== ''
    })

    const style = {
      name: this.get('title') || 'Custom Vector Style',
      rules: [
        ...this.getDefaultVectorStyles(),
        ...filteredUserStyles
      ]
    }

    return this.parser
      .writeStyle(style)
      .then(olStyle => {
        // update the sld_body which is what geoserver uses to style the layer
        this.setStyle(olStyle)
      })
  }

  _setInitialStyle () {
    let style = {}
    const hasFeatures = this.getSource().getFeatures().length
    const geomType = hasFeatures ? this.getSource().getFeatures()[0].getGeometry() : null

    if (geomType instanceof olGeomPoint || geomType instanceof olGeomMultiPoint) {
      style = [new olStyleStyle({
        image: new olStyleCircle({
          fill: new olStyleFill({ color: 'rgba(255,255,255,0.4)' }),
          stroke: new olStyleStroke({ color: '#3399CC', width: 2 }),
          radius: 5
        })
      })]
    } else if (geomType instanceof olGeomLinestring || geomType instanceof olGeomMultiLinestring) {
      style = [new olStyleStyle({
        stroke: new olStyleStroke({ color: '#3399CC', width: 2 })
      })]
    } else {
      style = [new olStyleStyle({
        image: new olStyleCircle({
          fill: new olStyleFill({ color: 'rgba(255,255,255,0.4)' }),
          stroke: new olStyleStroke({ color: '#3399CC', width: 2 }),
          radius: 5
        }),
        fill: new olStyleFill({ color: 'rgba(255,255,255,0.4)' }),
        stroke: new olStyleStroke({ color: '#3399CC', width: 2 })
      })]
    }

    this.setStyle(() => style)
  }
}

export default VectorLayer