import on from 'ember-evented/on';
import $ from 'jquery';
import computed from 'ember-computed';
import Component from 'ember-component';
import { A as emberA } from 'ember-array/utils';

/*
  Component representing the collection of suggestions.

  If you provide a DOM element to listen on, it will bind the following:
    Tab and Enter: finalize and select a suggestion
    Up and Down arrows: move between suggestion choices.
*/

export default Component.extend({
  classNames: ['suggestions'],
  /* Properties passed in */
  suggestions() { return []; },
  wordToSearchFor: '',
  numberOfSuggestionsToDisplay: 5,
  elementToBindOn: undefined,
  eventHandler: undefined,
  suggestionIndex: 0,
  sortProperties: ['displayName'],

  /* The suggestions array is snapshotted so that individual suggestions don't change indices
     or disappear while you're typing/completing the suggestion. */
  snapshotSuggestions: on('init', function () {
    this.set('suggestionsSnapshot', this.get('suggestions')());
  }),

  updateSuggestionIndex: function (change) {
    let max = this.get('numberOfSuggestionsToDisplay'),
        numSuggestions = this.get('suggestionsSnapshot').length;

    numSuggestions = numSuggestions > max ? max : numSuggestions;
    this.set('suggestionIndex', (this.get('suggestionIndex') + change + numSuggestions) % numSuggestions);
  },
  /* NOTE: If changes to suggestions are made (specifically, the 200 suggestions limit)
    Considering revisiting performance issues here and/or debouncing. */
  filteredSuggestions: computed('wordToSearchFor', 'suggestionsSnapshot', function () {
    let regex = new RegExp(`^${this.get('wordToSearchFor')}`, 'i');
    return emberA(this.get('suggestionsSnapshot').filter(function (suggestionObject) {
      return regex.test(suggestionObject.displayName) || regex.test(suggestionObject.id);
    }));
  }),

  sortedSuggestions: computed.sort('filteredSuggestions', 'sortProperties'),

  currentSuggestions: computed('sortedSuggestions.[]', 'suggestionIndex', function () {
    let suggestions = emberA(this.get('sortedSuggestions')).uniq().slice(0, this.get('numberOfSuggestionsToDisplay')),
        suggestionIndex = this.get('suggestionIndex');

    return emberA(suggestions.map(function (suggestion, idx) {
      return {
        highlighted: idx === suggestionIndex,
        name: suggestion.displayName,
        index: idx, id: suggestion.id
      };
    }));
  }),

  actions: {
    updateSuggestionIndex(index) {
      this.set('suggestionIndex', index);
    }
  },

  /* Helpers */

  // Bind keyboard events for suggestions
  _prepareInputs: on('didInsertElement', function () {
    let eventHandler = (e) => {
      let evt = e || window.event,
          key = evt.charCode || evt.keyCode,
          suggestion;

      switch (key) {
        // Tab and Enter
        case 9:
        case 13:
          suggestion = this.get('currentSuggestions')[this.get('suggestionIndex')];
          this.set('suggestionIndex', 0);
          if (!suggestion) {
            this.sendAction('dismissSuggestions');
          } else {
            this.sendAction('selectSuggestion', suggestion.name);
          }
          break;

        // Up and Down arrows
        case 38:
        case 40:
          this.updateSuggestionIndex(key === 38 ? -1 : 1);
      }
    };

    $(this.get('elementToBindOn')).on('keydown.suggestionsHandler', eventHandler);
  }),

  _unprepareInputs: on('willDestroyElement', function () {
    $(this.get('elementToBindOn')).off('keydown.suggestionsHandler');
  })
});
