# Source: src/js/views/filters/ChoiceFilterView.js

    /*global define */
    define(['jquery', 'underscore', 'backbone',
            'models/filters/ChoiceFilter',
            'views/filters/FilterView',
            'text!templates/filters/choiceFilter.html'],
      function($, _, Backbone, ChoiceFilter, FilterView, Template) {
      'use strict';

      /**
      * @class ChoiceFilterView
      * @classdesc Render a view of a single ChoiceFilter model
      * @classcategory Views/Filters
      * @extends FilterView
      */
      var ChoiceFilterView = FilterView.extend(
        /** @lends ChoiceFilterView.prototype */{

        /**
        * A ChoiceFilter model to be rendered in this view
        * @type {ChoiceFilter} */
        model: null,

        /**
         * @inheritdoc
         */
        modelClass: ChoiceFilter,

        className: "filter choice",

        template: _.template(Template),

        /**
        * When this view is in "uiBuilder" mode, the class name for the handles on each choice
        * row that the user can click and drag to re-order
        * @type {string}
        */
        choiceHandleClass: "handle",

        /**
         * The class to add to the element that a user should click to remove a choice
         * value and label when this view is in "uiEditor" mode
         * @since 2.17.0
         * @type {string}
         */
        removeChoiceClass: "remove-choice",

        /**
         * A function that creates and returns the Backbone events object.
         * @return {Object} Returns a Backbone events object
         */
        events: function () {
          try {
            var events = FilterView.prototype.events.call(this);
            events["change select"] = "handleChange";
            var removeClass = "." + this.removeChoiceClass;
            events["click " + removeClass] = "removeChoice";
            events["mouseover " + removeClass] = "previewRemoveChoice";
            events["mouseout " + removeClass] = "previewRemoveChoice";
            return events
          }
          catch (error) {
            console.log( 'There was an error creating the events object for a ChoiceFilterView' +
              ' Error details: ' + error );
          }
        },

        render: function () {

          var view = this;

          // Renders the template and inserts the FilterEditorView if the mode is uiBuilder
          FilterView.prototype.render.call(this)

          var placeHolderText = this.model.get("placeholder");
          var select = this.$("select");

          if(this.mode === "uiBuilder"){

            // If this is the filter view where the user can edit the filter UI options,
            // like the label, placeholder text, and choices, then render the inputs
            // for these options.

            // The ignore-changes class prevents the editor footer from showing on keypress
            var placeholderInput = $(
              '<input placeholder="placeholder" class="' + this.uiInputClass +
              ' placeholder ignore-changes" data-category="placeholder" value="' +
              (placeHolderText ? placeHolderText : '') +'" />'
            );
            // Replace the select element with the placeholder text element
            placeholderInput.insertAfter(select);

            // Create the interface for a user to edit the value-label choice options
            var choicesEditor = this.createChoicesEditor();
            view.$el.append(choicesEditor);

          } else {
            // For regular search filter views, or the edit filter view, render the dropdown
            // interface

            //Create the placeholder text for the dropdown menu

            //If placeholder text is already provided in the model, use it
            //If not, create placeholder text using the model label
            if (!placeHolderText){
              if (this.model.get("label")){
                //If the label starts with a vowel, use "an"
                var vowels = ["a", "e", "i", "o", "u"];
                if (_.contains(vowels, this.model.get("label").toLowerCase().charAt(0))) {
                  placeHolderText = "Choose an " + this.model.get("label");
                }
                //Otherwise use "a"
                else {
                  placeHolderText = "Choose a " + this.model.get("label");
                }
              }

            }

            //Create the default option
            var defaultOption = $(document.createElement("option"))
                                  .attr("value", "")
                                  .text( placeHolderText );
            select.append(defaultOption);

            //Create an option element for each choice listen in the model
            _.each( this.model.get("choices"), function(choice){
              select.append( $(document.createElement("option"))
                              .attr("value", choice.value)
                              .text(choice.label) );
            }, this );

            //When the ChoiceFilter is changed, update the choice list in the UI
            this.listenTo(this.model, "change:values", this.updateChoices);
            this.listenTo(this.model, "remove", this.updateChoices);
          }

        },

        /**
         * Create the set of inputs where a use can select label-value pairs for the regular
         * choice filter view
         * @since 2.17.0
         */
        createChoicesEditor: function(){

          try {

            var view = this;
            this.choicesEditor = $("<div class='choices-editor'></div>");
            var choicesEditorText = $("<p class='modal-instructions'>Allow people to select from the following search terms</p>");
            var choiceEditorError = $("<p class='notification error' data-category='choices' style='display: none'></p>");
            var labelContainer = $("<div class='choice-editor unsortable'></div>");

            this.choicesEditor.append(choicesEditorText, choiceEditorError, labelContainer)

            labelContainer.append("<p class='ui-builder-container-text choice-label subtle'>Enter the text to display</p>")
            labelContainer.append("<p class='ui-builder-container-text choice-value subtle'>Enter the text to search for</p>")

            _.each(this.model.get("choices"), function (choice) {
              var choiceEditorEl = this.createChoiceEditor(choice);
              this.choicesEditor.append(choiceEditorEl)
            }, this);

            // Create a blank choice at the end
            this.addEmptyChoiceEditor();

            // Initialize choice drag and drop to re-order functionality
            require(['sortable'], function(Sortable){
              Sortable.create(view.choicesEditor[0], {
                direction: 'vertical',
                easing: "cubic-bezier(1, 0, 0, 1)",
                animation: 200,
                handle: "." + view.choiceHandleClass,
                draggable: ".choice-editor:not(.unsortable)",
                onUpdate: function (evt) {
                  // When the choice order is changed, update the filter model
                  view.updateModelChoices()
                },
              })
            })

            return this.choicesEditor
          }
          catch (error) {
            console.log( 'There was an error creating choices editor in a ChoiceFilterView' +
              ' Error details: ' + error );
          }

        },

        /**
         * Create a row where a user can input a value and label for a single choice.
         * @since 2.17.0
         */
        createChoiceEditor: function(choice){
          try {
            if (!choice) {
              return
            }

            var view = this;

            // Create the choice container
            var choiceContainer = $("<div class='choice-editor'></div>");

            // Create the click and drag handle
            var handle = $('<span class="' + view.choiceHandleClass + '">' +
                '<i class= "icon icon-ellipsis-vertical" ></i>' +
                '<i class="icon icon-ellipsis-vertical"></i>' +
              '</span >'
            );
            choiceContainer.append(handle);

            // Create inputs for "value" and "label", insert them in the container
            for (const [attrName, attrValue] of Object.entries(choice)) {
              var inputEl = $('<input>').attr({
                // The ignore-changes class prevents the editor footer from showing on keypress
                class: 'ignore-changes choice-input choice-' + attrName,
                value: attrValue,
                "data-category": attrName
              })
              // Update the values in the model when the user focuses out of an input
              inputEl.on("blur", function(){
                view.updateModelChoices.call(view)
              })
              choiceContainer.append(inputEl);
            }

            // Create the remove "X" button. Save references to the parent choice container so
            // that we can remove it from the view when the button is clicked
            var removeButton = $(
              "<i class='icon icon-remove " +
              this.removeChoiceClass +
              "' title='Remove this choice'></i>"
            ).data({
              choiceEl: choiceContainer
            });

            // Insert the remove button into the choice container
            choiceContainer.append(removeButton);
            return choiceContainer
          }
          catch (error) {
            console.log( 'There was an error  ChoiceFilterView' +
              ' Error details: ' + error );
          }

        },

        /**
         * Create an empty choice editor row
         * @since 2.17.0
         */
        addEmptyChoiceEditor: function () {
          try {
            var view = this;
            var choice = {
              label: "",
              value: ""
            }
            var choiceEditorEl = this.createChoiceEditor(choice);
            this.choicesEditor.append(choiceEditorEl)

            // Don't let users remove or sort the new choice entry fields until some text has
            // been entered
            var removeButton = choiceEditorEl.find("." + this.removeChoiceClass);
            var handle = choiceEditorEl.find("." + this.choiceHandleClass);
            removeButton.hide();
            handle.hide();
            choiceEditorEl.addClass("unsortable");
            // The inputs for value and label
            var inputs = choiceEditorEl.find("input");

            var onInputChange = function () {
              choiceEditorEl.removeClass("unsortable")
              removeButton.show();
              handle.show();
              view.addEmptyChoiceEditor();
              inputs.off("input", onInputChange);
            }
            inputs.on("input", onInputChange);
          }
          catch (error) {
            console.log('There was an error creating a choice editor in a ChoiceFilterView' +
              ' Error details: ' + error);
          }
        },

        /**
         * Indicate to the user that the choice value and label inputs will be removed when
         * they hover over the remove button.
         * @since 2.17.0
         */
        previewRemoveChoice: function (e) {
          try {

            var normalOpacity = 1.0,
                previewOpacity = 0.2,
                speed = 120;

            var removeEl = $(e.target);
            var subElements = removeEl.data("choiceEl").children().not(removeEl);

            if(e.type === "mouseover"){
              subElements.fadeTo(speed, previewOpacity)
              $(removeEl).fadeTo(speed, normalOpacity)
            }
            if(e.type === "mouseout"){
              subElements.fadeTo(speed, normalOpacity)
              $(removeEl).fadeTo(speed, previewOpacity)
            }

          } catch (error) {
            console.log("Error showing a preview of the removal of a Choice editor in a " +
              "Choice Filter View, details: " + error);
          }
        },

        /**
         * Remove a choice editor row and the corresponding label-value pair from the choice
         * Filter Model (TODO)
         * @since 2.17.0
         * @param {Object} e The click event object
         */
        removeChoice: function(e){
          try {
            var choiceEl = $(e.target).data("choiceEl");

            // See how many choice elements there are (subtract one because the label elements
            // are within a choice-editor element)
            var numChoices = this.$el.find(".choice-editor").length - 1

            // Don't allow removing the last choice element. Empty the last element and hide the
            // remove button instead.
            if (numChoices <= 1) {
              choiceEl.find("input").val('')
            } else {
              // Remove the choice editor element from the view, plus any listeners
              choiceEl.off();
              choiceEl.remove();
            }
            // Update the choices in the model
            this.updateModelChoices();
          }
          catch (error) {
            console.log( 'There was an error removing a choice editor in the ChoiceFilterView' +
              ' Error details: ' + error );
          }

        },

        /**
         * Update the choices attribute in the choiceFilter model based on the values in the
         * choices editor
         * @since 2.17.0
         */
        updateModelChoices: function(){
          try {

            // The array of label-value pairs that will be set on the choiceFilter model.
            var newChoices = [];

            // Find each choice editor container, and find the values from the two inputs
            // within.
            this.$el.find(".choice-editor").each(function(){
              var choiceEditor = $(this)
              var valueEl = choiceEditor.find("[data-category='value']")
              var labelEl = choiceEditor.find("[data-category='label']")
              if (valueEl.length && labelEl.length ){
                var newValue = valueEl[0].value
                var newLabel = labelEl[0].value
                // TODO: validate the label/value here and show error if choice is not
                // complete.
                if(!newValue && !newLabel){
                  // Don't add empty choices to the model
                  return
                } else {
                  newChoices.push({
                    label: newLabel,
                    value: newValue
                  })
                }
              }
            });
            // Replace the choices in the model with the new array with new values
            this.model.set("choices", newChoices);
          }
          catch (error) {
            console.log( 'There was an error updating the choices in a ChoiceFilterView' +
              ' Error details: ' + error );
          }
        },

        /**
        * Updates the value set on the ChoiceFilter Model associated with this view.
        * The filter value is grabbed from the select element in this view.
        *
        */
        updateModel: function(){

          //Get the new value from the text input
          var newValue = this.$("select").val();

          //Get the current values array from the model
          var currentValue = this.model.get("values");

          //If the ChoiceFilter allows multiple values to be added,
          // add the new choice to the values array
          if( this.model.get("chooseMultiple") ){

            //Duplicate the current values array
            var newValuesArray = currentValue.slice(0);

            //Add the new value to the array
            newValuesArray.push(newValue);

            //Set the new values array on the model
            this.model.set("values", newValuesArray);

          }
          //If multiple choices are not allowed,
          else{

            //Replace the first index of the array with the new value
            var newValuesArray = currentValue.slice(0);
            newValuesArray[0] = newValue;

            //Set the new values array on the model
            this.model.set("values", newValuesArray);
          }
        },

        /**
        * Update the choices in the select dropdown menu based on which choices are
        * currently selected
        */
        updateChoices: function(){

          //Enable all the choices
          this.$("option").prop("disabled", false);

          //Get the currently-selected choices
          var selectedChoices = this.model.get("values");

          _.each(selectedChoices, function(choice){

            //Find each choice in the dropdown menu and disable it
            this.$("option[value='" + choice + "']").prop("disabled", true);

          }, this);

        },

        /**
         * Show validation errors. This is used for filters that are in "UIBuilder" mode.
         * @param {Object} errors The error messages associated with each attribute that has
         * an error, passed from the Filter model validation function.
         */
        showValidationErrors: function (errors) {
          try {

            var view = this;
            // Select the messages container for the choice error (added in the template)
            var messageContainer = view.el.querySelector(".notification[data-category='choices']");

            // Show errors for label, placeholder, etc (elements common to all FilterViews)
            FilterView.prototype.showValidationErrors.call(this, errors);

            // Show errors in the choices editor
            var inputs = this.choicesEditor.find("input");

            // Add error styling to all the choices inputs. Remove error styling (and input
            // listeners) from all inputs when there is text in at least one of them
            var handleInput = function () {
              inputs.each(function (i, input) {
                view.hideInputError(input, messageContainer)
                input.removeEventListener('input', handleInput)
              })
            }
            if (inputs.length) {
              inputs.each(function (i, input) {
                view.showInputError(input);
                input.addEventListener('input', handleInput);
              })
            }

          }
          catch (error) {
            console.log(
              'There was an error showing validation errors in a FilterView' +
              '. Error details: ' + error
            );
          }
        },

      });
      return ChoiceFilterView;
    });