# Source: src/js/views/AccessRuleView.js

    define(['underscore',
            'jquery',
            'backbone',
            "models/AccessRule"],
    function(_, $, Backbone, AccessRule){

      /**
      * @class AccessRuleView
      * @classdesc Renders a single access rule from an object's access policy
      * @classcategory Views
      * @screenshot views/AccessRuleView.png
      */
      var AccessRuleView = Backbone.View.extend(
        /** @lends AccessRuleView.prototype */{

        /**
        * The type of View this is
        * @type {string}
        */
        type: "AccessRule",

        /**
        * The HTML tag name for this view's element
        * @type {string}
        */
        tagName: "tr",

        /**
        * The HTML classes to use for this view's element
        * @type {string}
        */
        className: "access-rule",

        /**
        * The AccessRule model that is displayed in this view
        * @type {AccessRule}
        */
        model: undefined,

        /**
        * If true, this view represents a new AccessRule that hasn't been added to the AccessPolicy yet
        * @type {boolean}
        */
        isNew: false,

        /**
        * If true, the user can change the AccessRule via this view.
        * If false, the AccessRule will just be displayed.
        * @type {boolean}
        */
        allowChanges: true,

        /**
        * The events this view will listen to and the associated function to call.
        * @type {Object}
        */
        events: {
          "keypress .search input"        : "listenForEnter",
          "click    .add.icon"            : "updateModel",
          "change   .access select"       : "updateModel"
        },

        /**
        * Is executed when a new AccessRuleView is created
        * @param {Object} options - A literal object with options to pass to the view
        */
        initialize: function(options){

        },

        /**
        * Renders a single Access Rule
        */
        render: function(){

          try{

            this.$el.empty();

            //If there's no model, exit now since there's nothing to render
            if( !this.model ){
              return;
            }

            //Get the subjects that should be hidden
            var hiddenSubjects = MetacatUI.appModel.get("hiddenSubjectsInAccessPolicy");
            //If this AccessRule is for a subject that should be hidden,
            if( Array.isArray(hiddenSubjects) &&
                _.contains(hiddenSubjects, this.model.get("subject") ) ){

              var usersGroups = _.pluck(MetacatUI.appUserModel.get("isMemberOf"), "groupId");

              //If the current user is not part of this hidden group or is not the hidden user
              if( !_.contains(hiddenSubjects, MetacatUI.appUserModel.get("username")) &&
                  !_.intersection(hiddenSubjects, usersGroups).length){
                //Remove this view
                this.remove();
                //Exit
                return;
              }

            }

            if( this.isNew ){

              //If we aren't allowing changes to this AccessRule, then don't display
              // anything for new AcccessRule rows
              if( !this.allowChanges ){
                return;
              }

              this.$el.addClass("new");

              //Create a text input for adding a subject or name
              var label = $(document.createElement("label"))
                            .attr("for", "search")
                            .text("Search by name, ORCID, or group name")
                            .addClass("subtle"),
                  input = $(document.createElement("input"))
                            .attr("type", "text")
                            .attr("name", "search")
                            .attr("placeholder", "e.g. Lauren Walker"),
                  hiddenInput = $(document.createElement("input"))
                                  .attr("type", "hidden")
                                  .attr("name", "subject")
                                  .addClass("hidden"),
                  searchCell = $(document.createElement("td"))
                                 .addClass("search")
                                 .attr("colspan", "2")
                                 .append(label, input, hiddenInput),
                  view  = this;

              //Setup the autocomplete widget for the input so users can search for people and groups
              input.autocomplete({
                source: function(request, response){
                  var beforeRequest = function(){
                    //loadingSpinner.show();
                  }

                  var afterRequest = function(){
                    //loadingSpinner.hide();
                  }

                  return MetacatUI.appLookupModel.getAccountsAutocomplete(request, response, beforeRequest, afterRequest)
                },
                select: function(e, ui) {
                  e.preventDefault();

                  var value = ui.item.value;
                  hiddenInput.val(value);
                  input.val(value);

                  view.updateSubject();

                },
                position: {
                  my: "left top",
                  at: "left bottom",
                  of: input,
                  collision: "flip"
                },
                appendTo: searchCell,
                minLength: 2
              });

              this.$el.append( searchCell );
            }
            else{
              try{

                if( this.$el.is(".new") ){
                  this.$el.removeClass("new");
                }

                //Create elements for the 'Name' column of this table row
                var subject = this.model.get("subject"),
                    icon;

                //If the subject is public, don't display an icon
                if( subject == "public" ){
                  icon = "";
                }
                //If this is a group subject, display the group icon
                else if( this.model.isGroup() ){
                  icon = $(document.createElement("i")).addClass("icon icon-on-left icon-group");
                }
                //If this is a username, display the user icon
                else{
                  icon = $(document.createElement("i")).addClass("icon icon-on-left icon-user");
                }

                //Get the user or group's name - or use the subject, as a backup
                var name = this.model.get("name") || subject;

                //Display "You" next to the user's own name, for extra helpfulness
                if( subject == MetacatUI.appUserModel.get("username") ){
                  name += " (You)";
                }

                //Create an element for the name
                var nameEl = $(document.createElement("span")).text(name);

                this.$el.append($(document.createElement("td")).addClass("name").append(icon, nameEl) );
              }
              catch(e){
                console.error("Couldn't render the name column of the AccessRuleView: ", e);
              }

              try{
                //If this subject is an ORCID, display the ORCID and ORCID icon
                if( subject.indexOf("orcid") >= 0 ){
                  //Create the "subject/orcid" column
                  var orcidImg = $(document.createElement("img")).attr("src", MetacatUI.root + "/img/orcid_64x64.png").addClass("orcid icon icon-on-left"),
                      orcid = $(document.createElement("span")).text( this.model.get("subject") );

                  this.$el.append($(document.createElement("td")).addClass("subject").append(orcidImg, orcid) );
                }
                else{
                  //For other subject types, don't show an ORCID icon
                  this.$el.append($(document.createElement("td")).addClass("subject").text( this.model.get("subject") ));
                }
              }
              catch(e){
                console.error("Couldn't render the subject column of the AccessRuleView: ", e);
              }
            }

            try{

              if( this.allowChanges ){
                //Create the access/permission options select dropdown
                var accessOptions = $(document.createElement("select"));

                //Create option elements for each access rule type that is enabled in the app
                _.mapObject(MetacatUI.appModel.get("accessRuleOptions"), function(isEnabled, optionType){
                  if( isEnabled ){
                    var option = $(document.createElement("option")).attr("value", optionType).text( MetacatUI.appModel.get("accessRuleOptionNames")[optionType] );

                    //If this is the access type enabled in this AccessRule, then select this option
                    if( this.model.get(optionType) ){
                      option.prop("selected", "selected");
                    }

                    accessOptions.append(option);
                  }
                }, this);
              }
              else{
                //Create an element to display the access type
                var accessOptions = $(document.createElement("span"));

                //Create option elements for each access rule type that is enabled in the app
                _.mapObject(MetacatUI.appModel.get("accessRuleOptions"), function(isEnabled, optionType){
                  //If this is the access type enabled in this AccessRule, then select this option
                  if( this.model.get(optionType) ){
                    accessOptions.text( MetacatUI.appModel.get("accessRuleOptionNames")[optionType] )
                                 .attr("title", "This cannot be changed.");
                  }
                }, this);
              }

              //Create the table cell and add the access options element
              this.$el.append($(document.createElement("td")).addClass("access").append(accessOptions) );
            }
            catch(e){
              console.error("Couldn't render the access column of the AccessRuleView: ", e);
            }

            //Render the Remove column of the table
            try{

              if( this.isNew ){
                var addIcon = $(document.createElement("i"))
                                .addClass("add icon icon-plus")
                                .attr("title", "Add this access");
                //Create an empty table cell for "new" blank rows
                this.$el.append($(document.createElement("td")).addClass("add-rule").append(addIcon));
              }
              else{
                //Only display a remove icon if we are allowing changes to this AccessRule
                if( this.allowChanges ){
                  //Create a remove icon
                  var userType   = this.model.isGroup()? "group" : "person",
                      removeIcon = $(document.createElement("i"))
                                     .addClass("remove icon icon-remove")
                                     .attr("title", "Remove access for this " + userType);

                  //Create a table cell and append the remove icon
                  this.$el.append($(document.createElement("td")).addClass("remove-rule").append(removeIcon) );
                }
                else{
                  //Add an empty table cell so the other rows don't look weird, if they have remove icons
                  this.$el.append($(document.createElement("td")));
                }
              }
            }
            catch(e){
              console.error("Couldn't render a remove button for an access rule: ", e);
            }

            //If there is no name set on this model, listen to when it may be set, and update the view
            if( !this.model.get("name") ){
              this.listenToOnce(this.model, "change:name", this.updateNameDisplay);
            }

            //Listen to changes on the access options and update the view if they are changed
            this.listenTo(this.model, "change:read change:write change:changePermission", this.updateAccessDisplay);

            //When the model is removed from the collection, remove this view
            this.listenTo(this.model, "remove", this.onRemove);

            //Attach the AccessRule model to the view element
            this.$el.data("model", this.model);
            this.$el.data("view", this);

          }
          catch(e){
            console.error(e);

            //Don't display a message to the user since this view is pretty small. Just remove it from the page.
            this.$el.remove();
          }

        },

        /**
        * Update the name in this view with the name from the model
        */
        updateNameDisplay: function(){
          //If there is no name set on the model, exit now, so that we don't show an empty string or falsey value
          if( !this.model.get("name") ){
            return;
          }

          var name = this.model.get("name");

          //Display "You" next to the user's own name, for extra helpfulness
          if( this.model.get("subject") == MetacatUI.appUserModel.get("username") ){
            name += " (You)";
          }

          //Find the name element and update the text content
          this.$(".name span").text(name);

        },

        /**
        * Update the AccessRule model with the selected access option
        */
        updateAccess: function(){
          try{
            //Get the value of the dropdown
            var selection = this.$(".access select").val();

            //If nothing was selected for some reason, exit now
            if( !selection ){
              return;
            }

            if( selection == "read" ){
              this.model.set("read", true);
              this.model.set("write", null);
              this.model.set("changePermission", null);
            }
            else if( selection == "write" ){
              this.model.set("read", true);
              this.model.set("write", true);
              this.model.set("changePermission", null);
            }
            else if( selection == "changePermission" ){
              this.model.set("read", true);
              this.model.set("write", true);
              this.model.set("changePermission", true);
            }

          }
          catch(e){
            console.error(e);
          }
        },

        /**
        * Update the access in this view with the access from the model
        */
        updateAccessDisplay: function(){

          //Get the select dropdown menu from this view
          var select = this.$(".access select");

          //Update the select dropdown menu with the value from the model
          if( this.model.get("changePermission") ){
            select.val("changePermission");
          }
          else if( this.model.get("write") ){
            select.val("write");
          }
          else{
            select.val("read");
          }

        },

        /**
        * Update the subject of the AccessRule
        */
        updateSubject: function(){
          //Get the subject from the hidden text input, which is populated from the
          // jQueryUI autocomplete widget
          var subject = this.$(".search input.hidden").val();

          //If the hidden input doesn't have a value, get the value from the visible input
          if( !subject ){
            subject = this.$(".search input:not(.hidden)").val();
          }

          //If there is no subject typed in, exit
          if( !subject ){
            return;
          }

          //Set the subject on the model
          this.model.set("subject", subject);

          this.isNew = false;

          this.render();
        },

        /**
        * Updates the model associated with this view
        */
        updateModel: function(){

          //Update the access and the subject
          this.updateAccess();
          this.updateSubject();

        },

        /**
        * Remove this AccessRule from the AccessPolicy
        */
        onRemove: function(){

          //If it is the rightsHolder of the object, don't remove the view
          if(this.model.get("dataONEObject") && this.model.get("dataONEObject").get("rightsHolder") == this.model.get("subject")){
            return;
          }

          //Remove this view from the page
          this.remove();

        },

        /**
        * Handles when the user has typed at least one character in the name search input
        * @param {Event} e - The keypress event
        */
        listenForEnter: function(e){

          try{

            if( !e ){
              return;
            }

            //If Enter was pressed,
            if( e.keyCode == 13 ){
              //Update the subject on this model
              this.updateSubject();
            }
          }
          catch(e){
            MetacatUI.appView.showAlert("This group or person could not be added.", "alert-error", this.$el, 3000);
            console.error("Error while listening to the Enter key in AccessRuleView: ", e);
          }

        }

      });

      return AccessRuleView;

    });