/*	Copyright (c) 2016 Jean-Marc VIGLINO, 
  released under the CeCILL-B license (French BSD license)
  (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt).
*/

import ol_interaction_DragOverlay from './DragOverlay.js'
import {unByKey as ol_Observable_unByKey} from 'ol/Observable.js'
import ol_interaction_Interaction from 'ol/interaction/Interaction.js'

import ol_ext_element from '../util/element.js'
import ol_Overlay_Fixed from '../overlay/Fixed.js'

/** Handle a touch cursor to defer event position on overlay position
 * It can be used as abstract base class used for creating subclasses. 
 * The TouchCursor interaction modifies map browser event coordinate and pixel properties to force pointer on the graphic cursor on the screen to any interaction that them.
 * @constructor
 * @extends {ol_interaction_DragOverlay}
 * @param {olx.interaction.InteractionOptions} options Options
 *  @param {string} [options.className] cursor class name
 *  @param {ol.coordinate} [options.coordinate] position of the cursor
 *  @param {strin} [options.anchor="left"] anchor position (left, center or right)
 *  @param {Array<*>} [options.buttons] an array of buttons
 *  @param {number} [options.maxButtons=5] maximum number of buttons (default 5)
 */
var ol_interaction_TouchCursor = class olinteractionTouchCursor extends ol_interaction_DragOverlay {
  constructor(options) {
    options = options || {}

    // Add Overlay
    var overlay = new ol_Overlay_Fixed({
      className: ('ol-touch-cursor ' + (options.className || '')).trim(),
      positioning: 'top-left',
      element: ol_ext_element.create('DIV', {}),
      stopEvent: false,
    })

    super({
      centerOnClick: false,
      //offset: [-20,-20],
      overlays: overlay
    })

    this.overlay = overlay;
    this.setAnchor(options.anchor || 'left')

    // List of listerner on the object
    this._listeners = {}

    // Interaction to defer position on top of the interaction 
    // this is done to enable other coordinates manipulation inserted after the interaction (snapping)
    this.ctouch = new ol_interaction_Interaction({
      handleEvent: function (e) {
        if (!/drag/.test(e.type) && this.getMap()) {
          e.coordinate = this.overlay.getPosition()
          e.pixel = this.getMap().getPixelFromCoordinate(e.coordinate)
          this._lastEvent = e
        } else {
          var res = e.frameState.viewState.resolution
          var cosa = Math.cos(e.frameState.viewState.rotation)
          var sina = Math.sin(e.frameState.viewState.rotation)
          var width = this.getOverlayElement().clientWidth / 2;
          var style = getComputedStyle(this.getOverlayElement());
          var offset = [-width - parseInt(style.marginLeft), -width - parseInt(style.marginTop)]
          if (this._anchor === 'center') offset[0] = 0;
          e.coordinate = [
            e.coordinate[0] + cosa * offset[0] * res + sina * offset[1] * res,
            e.coordinate[1] + sina * offset[0] * res - cosa * offset[1] * res
          ]
          e.pixel = this.getMap().getPixelFromCoordinate(e.coordinate)
        }
        return true
      }.bind(this)
    })
    // Force interaction on top
    this.ctouch.set('onTop', true)

    this.setPosition(options.coordinate, true)
    this.set('maxButtons', options.maxButtons || 5)

    if (options.buttons) {
      if (options.buttons.length > this.get('maxButtons')){
        this.set('maxButtons', options.buttons.length)
      }
      var elt = this.overlay.element
      var begin = options.buttons.length > 4 ? 0 : 1
      options.buttons.forEach((function (b, i) {
        if (i < 5) {
          ol_ext_element.create('DIV', {
            className: ((b.className || '') + ' ol-button ol-button-' + (i + begin)).trim(),
            html: ol_ext_element.create('DIV', { html: b.html }),
            click: b.click,
            on: b.on,
            parent: elt
          })
        }
      }))
    }

    // Replace events to handle click
    var dragging = false
    var start = false
    this.on('dragstart', function (e) {
      this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition())
      start = e
      return !e.overlay
    })
    this.on('dragend', function (e) {
      this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition())
      if (!e.overlay)
        return true
      if (dragging) {
        this.dispatchEvent({
          type: 'dragend',
          dragging: dragging,
          originalEvent: e.originalEvent,
          frameState: e.frameState,
          pixel: this._pixel,
          coordinate: this.overlay.getPosition()
        })
        dragging = false
      } else {
        if (e.originalEvent.target === this.overlay.element) {
          this.dispatchEvent({
            type: 'click',
            dragging: dragging,
            originalEvent: e.originalEvent,
            frameState: e.frameState,
            pixel: this._pixel,
            coordinate: this.overlay.getPosition()
          })
        }
      }
      return false
    }.bind(this))
    this.on('dragging', function (e) {
      this._pixel = this.getMap().getPixelFromCoordinate(this.overlay.getPosition())
      if (!e.overlay){
        return true
      }
      dragging = true
      if (start) {
        this.dispatchEvent({
          type: 'dragstart',
          dragging: dragging,
          originalEvent: start.originalEvent,
          frameState: e.frameState,
          pixel: this._pixel,
          coordinate: start.coordinate
        })
        start = false
      }
      this.dispatchEvent({
        type: 'dragging',
        dragging: dragging,
        originalEvent: e.originalEvent,
        frameState: e.frameState,
        pixel: this._pixel,
        coordinate: this.overlay.getPosition()
      })
      return false
    }.bind(this))
  }
  /**
   * Remove the interaction from its current map, if any,  and attach it to a new
   * map, if any. Pass `null` to just remove the interaction from the current map.
   * @param {_ol_Map_} map Map.
   * @api stable
   */
  setMap(map) {
    // Reset
    if (this.getMap()) {
      this.getMap().removeInteraction(this.ctouch)
      if (this.getActive())
        this.getMap().removeOverlay(this.overlay)
    }
    for (let l in this._listeners) {
      ol_Observable_unByKey(this._listeners[l])
    }
    this._listeners = {}

    super.setMap(map)

    // Set listeners
    if (map) {
      if (this.getActive()) {
        map.addOverlay(this.overlay)
        setTimeout(function () {
          this.setPosition(this.getPosition() || map.getView().getCenter())
        }.bind(this))
      }
      map.addInteraction(this.ctouch)
      this._listeners.addInteraction = map.getInteractions().on('add', function (e) {
        // Move on top
        if (!e.element.get('onTop')) {
          map.removeInteraction(this.ctouch)
          map.addInteraction(this.ctouch)
        }
      }.bind(this))
    }
  }
  /** Set anchor position
   * @param {string} pos "left", "center" or "right"
   * @api
   */
  setAnchor(pos) {
    var positions = ['left', 'right', 'center']
    if (positions.indexOf(pos) >= 0) {
      positions.forEach(function(k) {
        this.getOverlayElement().classList.remove('ol-touch-cursor-'+k)
      }.bind(this))
      this.getOverlayElement().classList.add('ol-touch-cursor-'+pos)
      this._anchor = pos;
    }
  }
  /**
   * Activate or deactivate the interaction.
   * @param {boolean} active Active.
   * @param {ol.coordinate|null} position position of the cursor (when activating), default viewport center.
   * @observable
   * @api
   */
  setActive(b, position) {
    if (b !== this.getActive()) {
      if (this.ctouch) this.ctouch.setActive(b)
      if (!b) {
        this.setPosition()
        this.overlay.element.classList.remove('active')
        if (this._activate)
          clearTimeout(this._activate)
        if (this.getMap())
          this.getMap().removeOverlay(this.overlay)
      } else {
        if (this.getMap()) {
          this.getMap().addOverlay(this.overlay)
        }
        if (position) {
          this.setPosition(position)
        } else if (this.getMap()) {
          this.setPosition(this.getMap().getView().getCenter())
        }
        this._activate = setTimeout(function () {
          this.overlay.element.classList.add('active')
        }.bind(this), 100)
      }
      super.setActive(b)
    } else if (position) {
      this.setPosition(position)
    } else if (this.getMap()) {
      this.setPosition(this.getMap().getView().getCenter())
    }
  }
  /** Set the position of the target
   * @param {ol.coordinate} coord
   */
  setPosition(coord) {
    this.overlay.setPosition(coord, true)
    if (this.getMap() && coord) {
      this._pixel = this.getMap().getPixelFromCoordinate(coord)
    }
  }
  /** Offset the target position
   * @param {ol.coordinate} coord
   */
  offsetPosition(coord) {
    var pos = this.overlay.getPosition()
    if (pos) {
      this.overlay.setPosition([pos[0] + coord[0], pos[1] + coord[1]])
    }
  }
  /** Get the position of the target
   * @return {ol.coordinate}
   */
  getPosition() {
    return this.overlay.getPosition()
  }
  /** Get pixel position
   * @return {ol.pixel}
   */
  getPixel() {
    if (this.getMap()) {
      return this.getMap().getPixelFromCoordinate(this.getPosition())
    }
  }
  /** Get cursor overlay
   * @return {ol.Overlay}
   */
  getOverlay() {
    return this.overlay
  }
  /** Get cursor overlay element
   * @return {Element}
   */
  getOverlayElement() {
    return this.overlay.element
  }
  /** Get cursor button element
   * @param {string|number} button the button className or the button index
   * @return {Element}
   */
  getButtonElement(button) {
    if (typeof (button) === 'number')
      return this.getOverlayElement().getElementsByClassName('ol-button')[button]
    return this.getOverlayElement().getElementsByClassName(button)[0]
  }
  /** Remove a button element
   * @param {string|number|undefined} button the button className or the button index, if undefined remove all buttons, default remove all
   * @return {Element}
   */
  removeButton(button) {
    if (button === undefined) {
      var buttons = this.getOverlayElement().getElementsByClassName('ol-button')
      for (var i = buttons.length - 1; i >= 0; i--) {
        this.getOverlayElement().removeChild(buttons[i])
      }
    } else {
      var elt = this.getButtonElement(button)
      if (elt)
        this.getOverlayElement().removeChild(elt)
    }
  }
  /** Add a button element
   * @param {*} button
   *  @param {string} options.className button class name
   *  @param {DOMElement|string} options.html button content
   *  @param {function} options.click onclick function
   *  @param {*} options.on an object with
   *  @param {boolean} options.before
   */
  addButton(b) {
    var buttons = this.getOverlayElement().getElementsByClassName('ol-button')
    var max = (this.get('maxButtons') || 5)

    if (buttons.length >= max) {
      console.error('[ol/interaction/TouchCursor~addButton] too many button on the cursor (max=' + max + ')...')
      return
    }
    var button = ol_ext_element.create('DIV', {
      className: ((b.className || '') + ' ol-button').trim(),
      html: ol_ext_element.create('DIV', { html: b.html }),
      click: b.click,
      on: b.on
    })
    if (!b.before || buttons.length === 0) {
      this.getOverlayElement().appendChild(button)
    } else {
      this.getOverlayElement().insertBefore(button, buttons[0])
    }
    // Reorder buttons
    var start = buttons.length >= max ? 0 : 1
    for (let i = 0; i < buttons.length; i++) {
      buttons[i].className = buttons[i].className.replace(/ol-button-\d/g, '').trim() + ' ol-button-' + (i + start)
    }
  }
}

export default ol_interaction_TouchCursor
