import ol_Object from 'ol/Object.js';
import ol_ext_element from '../element.js'

/** A list element synchronize with a Collection. 
 * Element in the list can be reordered interactively and the associated Collection is kept up to date.
 * @constructor
 * @fires item:select
 * @fires item:dblclick
 * @fires item:keydown
 * @fires item:order
 * @extends {ol_Object}
 * @param {*} options
 *  @param {Element} [options.target] 
 *  @param {Collection} [options.collection]  the collection to display in the list
 *  @param {function} [options.getTitle] a function that takes a collection item and returns an Element or a string
 */
var ol_ext_input_Collection = class olextinputCollection extends ol_Object {
  constructor(options) {
    super();
    this.element = ol_ext_element.create('UL', {
      className: ('ol-collection-list ' + (options.className || '')).trim(),
      parent: options.target
    });
    this._title = (typeof (options.getTitle) === 'function' ? options.getTitle : function (elt) { return elt.title; });
    this.setCollection(options.collection);
  }
  /** Remove current collection (listeners)
   * /!\ remove collection when input list is removed from the DOM
   */
  removeCollection() {
    if (this.collection) {
      this.collection.un('change:length', this._update);
      this.collection = null;
    }
  }
  /** Set the collection
   * @param {ol_Collection} collection
   */
  setCollection(collection) {
    this.removeCollection();
    this.collection = collection;
    this.refresh();
    if (this.collection) {
      this._update = function () {
        if (!this._reorder) {
          this.refresh();
          var pos = this.getSelectPosition();
          if (pos < 0) {
            this.dispatchEvent({ type: 'item:select', position: -1, item: null });
          } else {
            this.dispatchEvent({ type: 'item:order', position: pos, item: this._currentItem });
          }
        }
      }.bind(this);
      this.collection.on('change:length', this._update);
    }
  }
  /** Select an item
   * @param {*} item
   */
  select(item) {
    if (item === this._currentItem)
      return;
    var pos = -1;
    this._listElt.forEach(function (l, i) {
      if (l.item !== item) {
        l.li.classList.remove('ol-select');
      } else {
        l.li.classList.add('ol-select');
        pos = i;
      }
    });
    this._currentItem = (pos >= 0 ? item : null);
    this.dispatchEvent({ type: 'item:select', position: pos, item: this._currentItem });
  }
  /** Select an item at
   * @param {number} n
   */
  selectAt(n) {
    this.select(this.collection.item(n));
  }
  /** Get current selection
   * @returns {*}
   */
  getSelect() {
    return this._currentItem;
  }
  /** Get current selection
   * @returns {number}
   */
  getSelectPosition() {
    if (!this.collection)
      return -1;
    return this.collection.getArray().indexOf(this._currentItem);
  }
  /** Redraw the list
   * @param {*} [focus] item to focus on
   */
  refresh(focus) {
    this.element.innerHTML = '';
    this._listElt = [];
    if (!this.collection)
      return;

    this.collection.forEach((item, pos) => {
      var li = ol_ext_element.create('LI', {
        html: this._title(item),
        className: this._currentItem === item ? 'ol-select' : '',
        'data-position': pos,
        on: {
          click: function () {
            this.select(item);
          }.bind(this),
          dblclick: function () {
            this.dispatchEvent({ type: 'item:dblclick', position: pos, item: item });
          }.bind(this),
        },
        parent: this.element
      });
      // Accessibility
      var check = ol_ext_element.create('INPUT', {
        'aria-label': this._title(item),
        type: 'checkbox',   
        className: 'ol-input-focus',
        on: {
          keydown: function(e) {
            switch (e.key) {
              // Move up dans down
              case 'ArrowUp':
              case 'ArrowDown': {
                if (e.ctrlKey) {
                  e.preventDefault();
                  e.stopPropagation();
                  this.select(item);
                  var newPos = (e.key === 'ArrowUp' ? pos-1 : pos+1);
                  if (newPos >= 0 && newPos < this.collection.getLength()) {
                    this._reorder = true;
                    this.collection.removeAt(pos);
                    this.collection.insertAt(newPos, item);
                    this._reorder = false;
                    this.dispatchEvent({ type: 'item:order', position: newPos, oldPosition: pos, item: item });
                    this.refresh(item);
                  }
                }
                break;
              }
              // Select
              case ' ': 
              case 'Space': {
                e.preventDefault();
                e.stopPropagation();
                this.select(item);
                break;
              }
              case 'Tab': {
                break;
              }
              default: {
                this.dispatchEvent({ type: 'item:keydown', key: e.key, originalEvent: e, position: pos, item: item })
                break;
              }
            }
          }.bind(this)
        },
        parent: li
      })
      if (focus === item) {
        check.focus();
      }
      this._listElt.push({ li: li, item: item });
      var order = ol_ext_element.create('DIV', {
        className: 'ol-noscroll ol-order',
        parent: li
      });
      var current = pos;
      var move = function (e) {
        // Get target
        var target = e.pointerType === 'touch' ? document.elementFromPoint(e.clientX, e.clientY) : e.target;
        while (target && target.parentNode !== this.element) {
          target = target.parentNode;
        }
        if (target && target !== li) {
          var over = parseInt(target.getAttribute('data-position'));
          if (target.getAttribute('data-position') < current) {
            target.insertAdjacentElement('beforebegin', li);
            current = over;
          } else {
            target.insertAdjacentElement('afterend', li);
            current = over + 1;
          }
        }
      }.bind(this);
      var stop = function () {
        document.removeEventListener('pointermove', move);
        document.removeEventListener('pointerup', stop);
        document.removeEventListener('pointercancel', stop);
        if (current !== pos) {
          this._reorder = true;
          this.collection.removeAt(pos);
          this.collection.insertAt(current > pos ? current - 1 : current, item);
          this._reorder = false;
          this.dispatchEvent({ type: 'item:order', position: current > pos ? current - 1 : current, oldPosition: pos, item: item });
          this.refresh();
        }
      }.bind(this);
      order.addEventListener('pointerdown', function () {
        this.select(item);
        document.addEventListener('pointermove', move);
        document.addEventListener('pointerup', stop);
        document.addEventListener('pointercancel', stop);
      }.bind(this));
    });
  }
}

export default ol_ext_input_Collection