# Source: views/metadata/EMLEntityView.js

    /* global define */
    define(['underscore', 'jquery', 'backbone', 'localforage',
            'models/DataONEObject', 'models/metadata/eml211/EMLAttribute', 'models/metadata/eml211/EMLEntity',
            'views/DataPreviewView',
            'views/metadata/EMLAttributeView',
            'text!templates/metadata/eml-entity.html',
            'text!templates/metadata/eml-attribute-menu-item.html'],
        function(_, $, Backbone, LocalForage, DataONEObject, EMLAttribute, EMLEntity,
        		DataPreviewView,
        		EMLAttributeView,
        		EMLEntityTemplate,
        		EMLAttributeMenuItemTemplate){

            /*
                An EMLEntityView shows the basic attributes of a DataONEObject, as described by EML
            */
            var EMLEntityView = Backbone.View.extend({

                tagName: "div",

                className: "eml-entity modal hide fade",

                id: null,

                /* The HTML template for an entity */
                template: _.template(EMLEntityTemplate),
                attributeMenuItemTemplate: _.template(EMLAttributeMenuItemTemplate),

                /* Events this view listens to */
                events: {
                	"change" : "saveDraft",
                	"change input" : "updateModel",
                	"change textarea" : "updateModel",
                	"click .nav-tabs a" : "showTab",
                	"click .attribute-menu-item" : "showAttribute",
                	"mouseover .attribute-menu-item .remove" : "previewAttrRemove",
                	"mouseout .attribute-menu-item .remove"  : "previewAttrRemove",
                	"click .attribute-menu-item .remove" : "removeAttribute"
                },

                initialize: function(options){
                	if(!options)
                		var options = {};

                	this.model = options.model || new EMLEntity();
                	this.DataONEObject = options.DataONEObject;
                },

                render: function(){

                	this.renderEntityTemplate();

                	this.renderPreview();

                	this.renderAttributes();

                  this.listenTo(this.model, "invalid", this.showValidation);
                  this.listenTo(this.model, "valid", this.showValidation);

                },

                renderEntityTemplate: function(){
                	var modelAttr = this.model.toJSON();

                	if(!modelAttr.entityName)
                		modelAttr.title = "this data";
                	else
                		modelAttr.title = modelAttr.entityName;

                	modelAttr.uniqueId = this.model.cid;

                	this.$el.html(this.template( modelAttr ));

                	//Initialize the modal window
                	this.$el.modal();

           		  	//Set the menu height
                	var view = this;
           		  	this.$el.on("shown", function(){
           		  		view.adjustHeight();
           		  		view.setMenuWidth();

           		  		window.addEventListener('resize', function(event){
    	       		  		view.adjustHeight();
    	       		  		view.setMenuWidth();
    	       		  	});
           		  	});

                  this.$el.on("hidden", function(){
                    view.showValidation();
                  });

                },

                renderPreview: function(){
                	//Get the DataONEObject model
                	if(this.DataONEObject){
                		var dataPreview = new DataPreviewView({
                			model: this.DataONEObject
                		});
                		dataPreview.render();
                		this.$(".preview-container").html(dataPreview.el);

                		if(dataPreview.$el.children().length){
                			this.$(".description").css("width", "calc(100% - 310px)");
                		}
                		else
                			dataPreview.$el.remove();
                	}
                },

                renderAttributes: function(){
                	//Render the attributes
                	var attributes      = this.model.get("attributeList"),
                		attributeListEl = this.$(".attribute-list"),
                		attributeMenuEl = this.$(".attribute-menu");

                	_.each(attributes, function(attr){

                		//Create an EMLAttributeView
                		var view = new EMLAttributeView({
                			model: attr
                		});

                		//Create a link in the attribute menu
                		var menuItem = $(this.attributeMenuItemTemplate({
    	            			attrId: attr.cid,
    	            			attributeName: attr.get("attributeName"),
    	            			classes: ""
    	            		})).data({
    							model: attr,
    							attributeView: view
    							});
                		attributeMenuEl.append(menuItem);
                		menuItem.find(".tooltip-this").tooltip();

                		this.listenTo(attr, "change:attributeName", function(attr){
                			menuItem.find(".name").text(attr.get("attributeName"));
                		});

                		view.render();

                		attributeListEl.append(view.el);

                		view.$el.hide();

                		this.listenTo(attr, "change",  this.addAttribute);
                		this.listenTo(attr, "invalid", this.showAttributeValidation);
                		this.listenTo(attr, "valid",   this.hideAttributeValidation);

                	}, this);

                	//Add a new blank attribute view at the end
                	this.addNewAttribute();

                	//If there are no attributes in this EML model yet,
                	//then make sure we show a new add attribute button when the user starts typing
                	if(attributes.length == 0){
                		var onlyAttrView = this.$(".attribute-menu-item").first().data("attributeView"),
                			view = this,
                			keyUpCallback = function(){
                				//This attribute is no longer new
                				view.$(".attribute-menu-item").first().removeClass("new");

                				//Add a new attribute link and view
                				view.addNewAttribute();

                				//Don't listen to keyup anymore
                				onlyAttrView.$el.off("keyup", keyUpCallback);
                			};

                		onlyAttrView.$el.on("keyup", keyUpCallback);
                	}

            		//Activate the first navigation item
            		var firstAttr = this.$(".side-nav-item").first();
            		firstAttr.addClass("active");

            		//Show the first attribute view
            		firstAttr.data("attributeView").$el.show();

            		firstAttr.data("attributeView").postRender();

                },

                updateModel: function(e){
                	var changedAttr = $(e.target).attr("data-category");

                	if(!changedAttr) return;

                  var emlModel = this.model.getParentEML(),
                      newValue = emlModel? emlModel.cleanXMLText($(e.target).val()) : $(e.target).val();

                	this.model.set(changedAttr, newValue);

                  this.model.trickleUpChange();

                },

                addNewAttribute: function(){
                	var newAttrModel = new EMLAttribute({
                			parentModel: this.model,
                            xmlID: DataONEObject.generateId()
                		}),
                		newAttrView  = new EMLAttributeView({
                			isNew: true,
                			model: newAttrModel
                		});

                	newAttrView.render();
                	this.$(".attribute-list").append(newAttrView.el);
                	newAttrView.$el.hide();

                	//Change the last menu item if it still says "Add attribute"
                	if(this.$(".attribute-menu-item").length == 1){
                		var firstAttrMenuItem = this.$(".attribute-menu-item").first();

                		if( firstAttrMenuItem.find(".name").text() == "Add attribute" ){
                			firstAttrMenuItem.find(".name").text("New attribute");
                			firstAttrMenuItem.find(".add").hide();
                		}
                	}

                	//Create the new menu item
                	var menuItem = $(this.attributeMenuItemTemplate({
    	            		attrId: newAttrModel.cid,
    	            		attributeName: "Add attribute",
    	            		classes: "new"
    	            	})).data({
    						model: newAttrModel,
    						attributeView: newAttrView
    					});
                	menuItem.find(".add").show();
                	this.$(".attribute-menu").append(menuItem);
                	menuItem.find(".tooltip-this").tooltip();

                	//When the attribute name is changed, update the navigation
            		this.listenTo(newAttrModel, "change:attributeName", function(attr){
            			menuItem.find(".name").text(attr.get("attributeName"));
            			menuItem.find(".add").hide();
            		});

                	this.listenTo(newAttrModel, "change",  this.addAttribute);
                	this.listenTo(newAttrModel, "invalid", this.showAttributeValidation);
                	this.listenTo(newAttrModel, "valid",   this.hideAttributeValidation);
                },

                addAttribute: function(emlAttribute){
                	//Add the attribute to the attribute list in the EMLEntity model
                	if( !_.contains(this.model.get("attributeList"), emlAttribute) )
                		this.model.addAttribute(emlAttribute);
                },

                removeAttribute: function(e){
                	var removeBtn = $(e.target);

                	var menuItem  = removeBtn.parents(".attribute-menu-item"),
                		attrModel = menuItem.data("model");

                	if(attrModel){
                		//Remove the attribute from the model
                		this.model.removeAttribute(attrModel);

                		//If this menu item is active, then make the next attribute active instead
                		if(menuItem.is(".active")){
                			var nextMenuItem = menuItem.next();

                			if(!nextMenuItem.length || nextMenuItem.is(".new")){
                				nextMenuItem = menuItem.prev();
                			}

                			if(nextMenuItem.length){
    	            			nextMenuItem.addClass("active");

    	            			this.showAttribute(nextMenuItem.data("model"));
                			}
                		}

                		//Remove the elements for this attribute from the page
                		menuItem.remove();
    	            	this.$(".eml-attribute[data-attribute-id='" + attrModel.cid + "']").remove();
    	            	$(".tooltip").remove();

    	            	this.model.trickleUpChange();
                	}
                },

                adjustHeight: function(e){
                	var contentAreaHeight = this.$(".modal-body").height() - this.$(".nav-tabs").height();

                	this.$(".attribute-menu, .attribute-list").css("height", contentAreaHeight + "px");
                },

                setMenuWidth: function(){

                	this.$(".entity-container .nav").width( this.$el.width() );

                },

                /*
                 * Shows the attribute in the attribute editor
                 * Param e - JS event or attribute model
                 */
                showAttribute: function(e){

                	if(e.target){
                       	var clickedEl = $(e.target),
                       		  menuItem = clickedEl.is(".attribute-menu-item") || clickedEl.parents(".attribute-menu-item");

                    	if(clickedEl.is(".remove"))
                    		return;
                	}
                	else{
                		var menuItem = this.$(".attribute-menu-item[data-attribute-id='" + e.cid + "']");
                	}

                	if(!menuItem)
                		return;

                  //Validate the previously edited attribute
                  //Get the current active attribute
                  var activeAttrTab = this.$(".attribute-menu-item.active");

                  //If there is a currently-active attribute tab,
                  if( activeAttrTab.length ){
                    //Get the attribute list from this view's model
                    var emlAttributes = this.model.get("attributeList");

                    //If there is an EMLAttribute list,
                    if( emlAttributes && emlAttributes.length ){

                      //Get the active EMLAttribute
                      var activeEMLAttribute = _.findWhere(emlAttributes, { cid: activeAttrTab.attr("data-attribute-id") });

                      //If there is an active EMLAttribute model, validate it
                      if( activeEMLAttribute ){
                        activeEMLAttribute.isValid();
                      }

                    }

                  }

                	//If the user clicked on the add attribute link
                	if( menuItem.is(".new") && this.$(".new.attribute-menu-item").length < 2 ){

                		//Change the attribute menu item
                		menuItem.removeClass("new").find(".name").text("New attribute");
                		this.$(".eml-attribute.new").removeClass("new");
                		menuItem.find(".add").hide();

                		//Add a new attribute view and menu item
                		this.addNewAttribute();

                    //Scroll the attribute menu to the bottom so that the "Add New" button is always visible
                    var attrMenuHeight = this.$(".attribute-menu").scrollTop() + this.$(".attribute-menu").height();
                    this.$(".attribute-menu").scrollTop( attrMenuHeight );
                	}

                	//Get the attribute view
                	var attrView = menuItem.data("attributeView");

                	//Change the active attribute in the menu
                	this.$(".attribute-menu-item.active").removeClass("active");
                	menuItem.addClass("active");

                	//Hide the old attribute view
                	this.$(".eml-attribute").hide();
                	//Show the new attribute view
                	attrView.$el.show();

                  //Scroll to the top of the attribute view
                  this.$(".attribute-list").scrollTop(0);

                	attrView.postRender();
                },

                /*
                 * Show the attribute validation errors in the attribute navigation menu
                 */
                showAttributeValidation: function(attr){

                	var attrLink = this.$(".attribute-menu-item[data-attribute-id='" + attr.cid + "']").find("a");

                	//If the validation is already displayed, then exit
                	if(attrLink.is(".error")) return;

                	var errorIcon = $(document.createElement("i")).addClass("icon icon-exclamation-sign error icon-on-left");

                	attrLink.addClass("error").prepend(errorIcon);
                },

                /*
                 * Hide the attribute validation errors from the attribute navigation menu
                 */
                hideAttributeValidation: function(attr){
                	this.$(".attribute-menu-item[data-attribute-id='" + attr.cid + "']")
                		.find("a").removeClass("error").find(".icon.error").remove();
                },

                /*
                 * Show the user what will be removed when this remove button is clicked
                 */
                previewAttrRemove: function(e){
                	var removeBtn = $(e.target);

                	removeBtn.parents(".attribute-menu-item").toggleClass("remove-preview");
                },

                /*
                * function showValidation
                *
                * Will display validation styling and messaging. Should be called after
                * this view's model has been validated and there are error messages to display
                */
                showValidation: function(){

                  //Reset the error messages and styling
                  //Only change elements inside the overview-container which contains only the
                  // EMLEntity metadata. The Attributes will be changed by the EMLAttributeView.
        					this.$(".overview-container .notification").text("");
                  this.$(".overview-tab .icon.error, .attributes-tab .icon.error").remove();
                  this.$(".overview-container, .overview-tab a, .attributes-tab a, .overview-container .error").removeClass("error");

                  var overviewTabErrorIcon  = false,
                      attributeTabErrorIcon = false;

                  _.each( this.model.validationError, function(errorMsg, category){

                    if( category == "attributeList" ){

                      //Create an error icon for the Attributes tab
                      if( !attributeTabErrorIcon ){
                        var errorIcon = $(document.createElement("i"))
                                          .addClass("icon icon-on-left icon-exclamation-sign error")
                                          .attr("title", "There is missing information in this tab");

                        //Add the icon to the Overview tab
                        this.$(".attributes-tab a").prepend(errorIcon).addClass("error");
                      }

                      return;
                    }

                    //Get all the elements for this category and add the error class
                    this.$(".overview-container [data-category='" + category + "']").addClass("error");
                    //Get the notification element for this category and add the error message
                    this.$(".overview-container .notification[data-category='" + category + "']").text(errorMsg);

                    //Create an error icon for the Overview tab
                    if( !overviewTabErrorIcon ){
                      var errorIcon = $(document.createElement("i"))
                                        .addClass("icon icon-on-left icon-exclamation-sign error")
                                        .attr("title", "There is missing information in this tab");

                      //Add the icon to the Overview tab
                      this.$(".overview-tab a").prepend(errorIcon).addClass("error");

                      overviewTabErrorIcon = true;
                    }

                  }, this);

                },

                /*
                 * Show the entity overview or attributes tab
                 * depending on the click target
                 */
                showTab: function(e){
                	e.preventDefault();

                	//Get the clicked link
           		  	var link = $(e.target);

           		  	//Remove the active class from all links and add it to the new active link
           		  	this.$(".nav-tabs li").removeClass("active");
           		  	link.parent("li").addClass("active");

           		  	//Hide all the panes and show the correct one
           		  	this.$(".tab-pane").hide();
           		  	this.$(link.attr("href")).show();

                },

                /*
                 * Show the entity in a modal dialog
                 */
                show: function(){

                	this.$el.modal('show');

                },

                /*
                 * Hide the entity modal dialog
                 */
                hide: function(){
                	this.$el.modal('hide');
                },

                /**
                 * Save a draft of the parent EML model
                 */
                saveDraft: function() {
                	var view = this;

                	try {
                		var model = this.model.getParentEML();
                		var title = model.get("title") || "No title";

                		LocalForage.setItem(model.get("id"),
                		{
                			id: model.get("id"),
                			datetime: (new Date()).toISOString(),
                			title: Array.isArray(title) ? title[0] : title,
                			draft: model.serialize()
                		}).then(function() {
                			view.clearOldDrafts();
                		});
                	} catch (ex) {
                		console.log("Error saving draft:", ex);
                	}
                },

                /**
               	* Clear older drafts by iterating over the sorted list of drafts
               	* stored by LocalForage and removing any beyond a hardcoded limit.
               	*/
               	clearOldDrafts: function() {
               		var drafts = [];

              		try {
              			LocalForage.iterate(function(value, key, iterationNumber) {
              			// Extract each draft
              			drafts.push({
              					key: key,
              					value: value
              				});
              			}).then(function(){
              				// Sort by datetime
              				drafts = _.sortBy(drafts, function(draft) {
              					return draft.value.datetime.toString();
              				}).reverse();
              			}).then(function() {
              				_.each(drafts, function(draft, i) {
              					var age = (new Date()) - new Date(draft.value.datetime);
              					var isOld = (age / 2678400000) > 1; // ~31days
              					// Delete this draft is not in the most recent 100 or
              					// if older than 31 days
              					var shouldDelete = i > 100 || isOld;
    											if (!shouldDelete) {
    												return;
    											}

    											LocalForage.removeItem(draft.key).then(function() {
    												// Item should be removed
    											});
    										})
    									});
    							}
    							catch (ex) {
    								console.log("Failed to clear old drafts: ", ex);
    							}
    						}
    	        });

            return EMLEntityView;
    });