# Source: src/js/views/metadata/EMLMeasurementScaleView.js

    /* global define */
    define(['underscore', 'jquery', 'backbone',
            'models/DataONEObject',
            'models/metadata/eml211/EMLMeasurementScale',
            'text!templates/metadata/eml-measurement-scale.html',
            'text!templates/metadata/codelist-row.html',
            'text!templates/metadata/nonNumericDomain.html',
            'text!templates/metadata/textDomain.html'],
        function(_, $, Backbone, DataONEObject, EMLMeasurementScale,
        		EMLMeasurementScaleTemplate, CodeListRowTemplate, NonNumericDomainTemplate, TextDomainTemplate){

            /**
            * @class EMLMeasurementScaleView
            * @classdesc An EMLMeasurementScaleView displays the info about one the measurement scale or category of an eml attribute
            * @classcategory Views/Metadata
            * @extends Backbone.View
            */
            var EMLMeasurementScaleView = Backbone.View.extend(
              /** @lends EMLMeasurementScaleView.prototype */{

                tagName: "div",

                className: "eml-measurement-scale",

                id: null,

                /* The HTML template for a measurement scale */
                template: _.template(EMLMeasurementScaleTemplate),
                codeListRowTemplate: _.template(CodeListRowTemplate),
                nonNumericDomainTemplate: _.template(NonNumericDomainTemplate),
                textDomainTemplate: _.template(TextDomainTemplate),

                /* Events this view listens to */
                events: {
                	"click  .category"        : "switchCategory",
                	"change .datetime-string" : "toggleCustomDateTimeFormat",
                	"change .possible-text"   : "toggleNonNumericDomain",
                	"keyup  .new .codelist"   : "addNewCodeRow",
                	"click .code-row .remove" : "removeCodeRow",
                	"mouseover .code-row .remove" : "previewCodeRemove",
                	"mouseout .code-row .remove"  : "previewCodeRemove",
                	"change .units"           : "updateModel",
                	"change .datetime" 		  : "updateModel",
                	"change .codelist"        : "updateModel",
                	"change .textDomain"      : "updateModel",
                	"focusout .code-row"      : "showValidation",
                	"focusout .units.input"   : "showValidation"
                },

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

                	this.isNew = (options.isNew === true) ? true : this.model? false : true;
                	this.model = options.model || EMLMeasurementScale.getInstance();
                	this.parentView = options.parentView || null;
                },

                render: function(){

                	//Render the template
                	var viewHTML = this.template(this.model.toJSON());

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

                	//Insert the template HTML
                	this.$el.html(viewHTML);

                	//Render any nonNumericDomain models
            		this.$(".non-numeric-domain").append( this.nonNumericDomainTemplate(this.model.get("nonNumericDomain")) );

            		//Render the text domain choices and details
            		this.$(".text-domain").html( this.textDomainTemplate() );

            		//If this attribute is already defined as nonNumericDomain, then fill in the metadata
            		_.each(this.model.get("nonNumericDomain"), function(domain){

            			var nominalTextDomain = this.$(".nominal-options .text-domain"),
            				ordinalTextDomain = this.$(".ordinal-options .text-domain");

            			if(domain.textDomain){
                			if(this.model.get("measurementScale") == "nominal"){
                				nominalTextDomain.html( this.textDomainTemplate(domain.textDomain) );
                			}
                			else{
                				ordinalTextDomain.html( this.textDomainTemplate(domain.textDomain) );
                			}

                		}
            			else if(domain.enumeratedDomain){
            				this.renderCodeList(domain.enumeratedDomain);
            			}

            		}, this);

            		//Add the new code rows in the code list table
        			this.addNewCodeRow("nominal");
        			this.addNewCodeRow("ordinal");

                },

                postRender: function(){
                	//Determine which category to select
                	//Interval measurement scales will be displayed as ratio
                	var selectedCategory = this.model.get("measurementScale") == "interval" ? "ratio" : this.model.get("measurementScale");

                	//Set the category
        			this.$(".category[value='" + selectedCategory + "']").prop("checked", true);
            		this.switchCategory();

            		this.renderUnitDropdown();

                	this.chooseDateTimeFormat();

                	this.chooseNonNumericDomain();
                },

                /*
                 * Render the table of code definitions from the enumeratedDomain node of the EML
                 */
                renderCodeList: function(codeList){

                	var scaleType  = this.model.get("measurementScale"),
                		$container = this.$("." + scaleType + "-options .enumeratedDomain.non-numeric-domain-type .table");

                	_.each(codeList.codeDefinition, function(definition){
                		var row = this.codeListRowTemplate(definition);

                		//Add the row to the table
                		$container.append(row);
                	}, this);

                },

                showValidation: function(e){

    				//Reset the error messages and styling
    				this.$(".error").removeClass("error");
    				this.$(".notification").text("");

    				//If the measurement scale model is NOT valid
    				if( !this.$(".category:checked").length ){
    					this.$(".category-container")
    						.addClass("error")
    						.find(".notification")
    						.text("Choose a category")
    						.addClass("error");

    					//Trigger the invalid event on the attribute model
                    	this.model.get("parentModel").trigger("invalid", this.model.get("parentModel"));

    				}
    				else if( !this.model.isValid() ){
                		//Get the errors
                		var errors = this.model.validationError,
                			modelType = this.model.get("measurementScale");

                		//Display error messages for each type of error
                		_.each(Object.keys(errors), function(attr){

                			//If this is an enumeratedDomain error
                			if(attr == "enumeratedDomain"){

                				var view = this;

                				//Give the user a few milliseconds to focus on a new element
                				setTimeout(function(){

                					//Highlight the inputs in code rows that are empty
                    				var emptyInputs = view.$("." + modelType + "-options .codelist.input")
    					                					.not(document.activeElement)
    					                					.filter(function(){
    					                						if( $(this).val() ) return false;
    					                						else return true;
    					                					});
                    				emptyInputs.addClass("error");

                    				if(emptyInputs.length)
                    					view.$("." + modelType + "-options [data-category='enumeratedDomain'] .notification").text(errors[attr]).addClass("error");

                            	}, 200);

                			}
                			//For all other attributes, just display the errors the same way
                			else{
                    			this.$("." + modelType + "-options [data-category='" + attr + "'] .notification").text(errors[attr]).addClass("error");
                    			this.$("." + modelType + "-options .input[data-category='" + attr + "']").addClass("error");
                			}

                			//Highlight the border of the non numeric domain container
                			if(attr == "nonNumericDomain"){
                				this.$("." + modelType + "-options.non-numeric-domain").addClass("error");
                			}

                		}, this);

                		//Trigger the invalid event on the attribute model
                    //	this.model.get("parentModel").trigger("invalid", this.model.get("parentModel"));

                	}
                	else{
                		//Trigger the valid event on the attribute model
                	//	this.model.get("parentModel").trigger("valid", this.model.get("parentModel"));
                	}

                },

                switchCategory: function(){
                	//Switch the category in the view
                	var chosenCategory = this.$("input[name='measurementScale']:checked").val();

                	//Show the new category options
                	this.$(".options").hide();
                	this.$("." + chosenCategory + "-options.options").show();

                	//Get the current category
                	var modelCategory = this.model.get("measurementScale");

                	//Get the parent attribute model
                	var parentEMLAttrModel = this.model.get("parentModel");

                	//Switch the model type, if needed
                	if(chosenCategory && (modelCategory != chosenCategory) && !(modelCategory == "interval" && chosenCategory == "ratio")){
                		var newModel;

                		if(typeof this.modelCache != "object"){
                			this.modelCache = {};
                		}

                		//Get the model type from this view's cache
                		if(this.modelCache[chosenCategory])
                			newModel = this.modelCache[chosenCategory];
                    else if( chosenCategory == "ratio" && this.modelCache["interval"] )
                      newModel = this.modelCache["interval"];
                		//Get a new model instance based on the type
                		else
                			newModel = EMLMeasurementScale.getInstance(chosenCategory);

                		//Save this model for later in case the user switches back
                		if(modelCategory)
                			this.modelCache[modelCategory] = this.model;

                		//save the new model
                		this.model = newModel;

                		//Set references to and from this model and the parent attribute model
                		this.model.set("parentModel", parentEMLAttrModel);
                		parentEMLAttrModel.set("measurementScale", this.model);

                		//Update the codelist values, if needed
                		if(chosenCategory == "nominal" || chosenCategory == "ordinal" &&
                				this.model.get("nonNumericDomain").length &&
                				this.model.get("nonNumericDomain")[0].enumeratedDomain){
                			this.updateCodeList();
                		}
                	}

                },

                renderUnitDropdown: function(){
                	if(this.$("select.units").length) return;

                	//Create a dropdown menu
                	var select = $(document.createElement("select"))
                					.addClass("units full-width input")
                					.attr("data-category", "unit");

                  var eml = this.model.getParentEML();

                	//Get the units collection or wait until it has been fetched
                	if(!eml.units.length){
                		this.listenTo(eml.units, "sync", this.renderUnitDropdown);
                		return;
                	}

                	//Create a default option
                	var defaultOption = $(document.createElement("option"))
    										.text("Choose a standard unit");
    				select.append(defaultOption);

    				//Create an "Other" option to show at the top
    				var otherOption = $(document.createElement("option"))
    									.text("Other / None")
    									.attr("value", "dimensionless");
    				select.append(otherOption);

                	//Create each unit option in the unit dropdown
                	eml.units.each(function(unit){
                		var option = $(document.createElement("option"))
                						.val(unit.get("_name"))
                						.text(unit.get("_name").charAt(0).toUpperCase() +
                								unit.get("_name").slice(1) +
                								" (" + unit.get("description") + ")")
                						.data({ model: unit });
                		select.append(option);
                	}, this);

                	//Add the dropdown to the page
                	this.$(".units-container").append(select);

                	//Select the unit from the EML, if there is one
                	var currentUnit = this.model.get("unit");
                	if(currentUnit && currentUnit.standardUnit){

                		//Get the dropdown for this measurement scale
                    // (We default interval to ratio in the editor)
                    var currentDropdown = this.$(".ratio-options select");

                		//Select the unit from the EML
                		currentDropdown.val(currentUnit.standardUnit);
                	}
                  //If this unit is a custom unit
                  else if( currentUnit && currentUnit.customUnit ){
                    //Create an <option> for this custom unit
                    var customUnitOption = $(document.createElement("option"))
                                            .val( currentUnit.customUnit )
                                            .text( currentUnit.customUnit )
                                            .addClass("custom");

                    //Add it to the <select> and select it as the active option
                    select.append(customUnitOption)
                          .val(currentUnit.customUnit);
                  }
                },

                /*
                 *  Chooses the date-time format from the dropdown menu
                 */
                chooseDateTimeFormat: function(){
                	if(this.model.type == "EMLDateTimeDomain"){
                    	var formatString = this.model.get("formatString");

                    	//Go back to the default option when the model isn't set yet
                    	if(!formatString){
                    		var options = this.$("select.datetime-string option");
                    		this.$("select.datetime-string").val(options.first().val());
                    		return;
                    	}

                    	var matchingOption = this.$("select.datetime-string [value='" + formatString + "']");

                    	if(matchingOption.length){
                    		this.$("select.datetime-string").val(formatString);
                    		this.$(".datetime-string-custom-container").hide();
                    	}
                    	else{
                    		this.$("select.datetime-string").val("custom");
                    		this.$(".datetime-string-custom").val(formatString);
                    		this.$(".datetime-string-custom-container").show();
                    	}

                	}
                },

                toggleCustomDateTimeFormat: function(e){
                	var choice = this.$("select.datetime-string").val();

                	if(choice == "custom"){
                		this.$(".datetime-string-custom-container").show();
                	}
                	else{
                		this.$(".datetime-string-custom-container").hide();
                	}

                },

                chooseNonNumericDomain: function(){

                	if(this.model.get("nonNumericDomain") && this.model.get("nonNumericDomain").length){

                		//Hide all the details first
                		this.$(".non-numeric-domain-type").hide();

                		//Get the domain from the model
                		var domain = this.model.get("nonNumericDomain")[0];

                		//If the domain type is text, select it and show it
                		if( domain.textDomain ){

                			//If the pattern is just a wildcard, then check the "anything" radio button
                			if(domain.textDomain.pattern && domain.textDomain.pattern.length && domain.textDomain.pattern[0] == "*")
                				this.$("." + this.model.get("measurementScale") + "-options .possible-text[value='anything']").prop("checked", true);
                			//Otherwise, check the pattern radio button
                			else{
                				this.$("." + this.model.get("measurementScale") + "-options .possible-text[value='pattern']").prop("checked", true);
                				this.$("." + this.model.get("measurementScale") + "-options .non-numeric-domain-type.pattern").show();
                			}

                		}
                		//If the domain type is a code list, select it and show it
                		else if( domain.enumeratedDomain ){
                			this.$("." + this.model.get("measurementScale") + "-options .possible-text[value='enumeratedDomain']").prop("checked", true);
                			this.$(".non-numeric-domain-type.enumeratedDomain").show();
                		}
                	}
                },

                toggleNonNumericDomain: function(e){
                	//Hide the domain type details
            		this.$(".non-numeric-domain-type").hide();

            		//Get the new value selected
                	var value = this.$(".non-numeric-domain .possible-text:checked").val();

                	var activeScale = this.$(".nominal-options").is(":visible")? "nominal" : "ordinal";

                	//Show the form elements for that non numeric type
                	this.$("." + activeScale + "-options .non-numeric-domain-type." + value).show();

                	this.updateModel(e);

                },

                addNewCodeRow: function(e){
                	if(typeof e == "object"){
    	            	var $row 	   = $(e.target).parents(".code-row"),
    	            		code 	   = $row.find(".code").val(),
    	            		definition = $row.find(".definition").val();

    	            	//Only add a row when there is a value for the code and code definition
    	            	if(!code || !definition) return false;

    	            	$row.removeClass("new");

    	            	var newRow = this.addCodeRow();
                	}
                	else if(typeof e == "string"){
    	            	var newRow = this.addCodeRow(e);
                	}

                	newRow.addClass("new");
                },

                addCodeRow: function(scaleType){
                	if(!scaleType)
                		var scaleType = this.model.get("measurementScale");

            		var	$container = this.$("." + scaleType + "-options .enumeratedDomain.non-numeric-domain-type .table");

                	//Create a code list row from the template
                	var row = $(this.codeListRowTemplate({ code: "", definition: ""}));

                	$container.append(row);

                	return row;
                },

                removeCodeRow: function(e){
                	var codeRow = $($(e.target).parents(".code-row")),
                		allRows = codeRow.parents(".enumerated-domain").find(".code-row"),
                		index   = allRows.index(codeRow);

                	this.model.removeCode(index);

                	codeRow.remove();

                	this.showValidation();

                	this.parentView.showValidation();

                },

                /*
                 * When the user changes the value of the form, update the model
                 */
                updateModel: function(e){

                	var updatedInput = $(e.target);

                  var emlModel = this.model.getParentEML();

                	//Update the standard unit
                	if(updatedInput.is(".units")){
                		var chosenUnit = updatedInput.val(),
                        chosenOption = updatedInput.children("[value='" + chosenUnit + "']");

                    if( chosenOption.is(".custom") ){
                      this.model.set("unit", {customUnit: chosenUnit});
                    }
                    else{
                      this.model.set("unit", {standardUnit: chosenUnit});
                    }

                    // Hard-code the numberType for now
                    this.model.set("numericDomain", {numberType: "real"});

                    //Trickle up the change to the most parent-level metadata model
                    this.model.trickleUpChange();
                	}
                	//Update the datetime format
                	else if(updatedInput.is(".datetime")){
                		var format = emlModel? emlModel.cleanXMLText( updatedInput.val() ) : updatedInput.val();

                		if(format == "custom"){
                			format = emlModel? emlModel.cleanXMLText( this.$(".datetime-string-custom").val() ) : this.$(".datetime-string-custom").val();
                		}

                    //If no format string was provided, then set the default value
                    if( typeof format == "string" && !format.trim().length )
                      this.model.set("formatString", this.model.defaults().formatString);
                    else
                      this.model.set("formatString", format);
                	}
                	else if(updatedInput.is(".possible-text")){
                		var possibleText = emlModel? emlModel.cleanXMLText( updatedInput.val() ) : updatedInput.val();

                		if(possibleText == "enumeratedDomain"){

            				//Update the code list
            				this.updateCodeList();

                		}
                		else if(possibleText == "pattern"){
                			if(!this.model.get("nonNumericDomain").length || !this.model.get("nonNumericDomain")[0].textDomain){

    	            			var textDomain = {
    	            					definition: null,
    	            					pattern: [],
    	            					source: null
    	            			}

    	            			this.model.set("nonNumericDomain", [{ textDomain: textDomain }]);
                			}
                			else{
                        //Get the value of the text input fields for the definition and pattern
                        var definition = this.$("." + this.model.get("measurementScale") + "-options .textDomain[data-category='definition']").val(),
                            pattern = this.$("." + this.model.get("measurementScale") + "-options .textDomain[data-category='pattern']").val();

                        definition = emlModel? emlModel.cleanXMLText( definition ) : definition;
                        pattern = emlModel? emlModel.cleanXMLText( pattern ) : pattern;

                        // If the pattern is an empty string, then set an empty array on the model
                        if( typeof pattern == "string" && !pattern.trim().length ){
                          pattern = new Array();
                        }
                        // For all other values, put it in an array
                        else {
                          pattern = [pattern];
                        }

                        // If the definition is a string of space characters, then set it to an empty string
                        if( typeof definition == "string" && !definition.trim().length ){
                          definition = "";
                        }

                				var textDomain = {
                						definition: definition,
                						pattern: pattern,
                						source: null
                				}
                				this.model.set("nonNumericDomain", [{ textDomain: textDomain }]);
                			}
                		}
                		else if(possibleText == "anything"){
                			var textDomain = {
                					definition: "Any text",
                					pattern: ["*"],
                					source: null
                			}

                			this.model.set("nonNumericDomain", [{ textDomain: textDomain }]);
                		}
                	}
                	else if(updatedInput.is(".textDomain")){

                    // If there is no nonNumericDomain object set on the model, create a new empty one
                    if(typeof this.model.get("nonNumericDomain")[0] != "object"){
                			this.model.get("nonNumericDomain")[0] = { textDomain: { definition: null, pattern: [], source: null } };
                    }

                    //Get the textDomain object
                		var textDomain = this.model.get("nonNumericDomain")[0].textDomain;

                    //If the text definition was updated...
                		if(updatedInput.attr("data-category") == "definition"){

                      //Get the value that was input by the user
                      var definition = emlModel? emlModel.cleanXMLText( updatedInput.val() ) : updatedInput.val();

                      // If the definition is a string of space characters, then set it to an empty string
                      if( typeof definition == "string" && !definition.trim().length ){
                        definition = "";
                      }

                      //Update the textDomain object
                    	textDomain.definition = definition;
                    }
                    //If the text pattern was updated...
                		else if(updatedInput.attr("data-category") == "pattern"){
                      //Get the value that was input by the user
                      var pattern = emlModel? emlModel.cleanXMLText( updatedInput.val() ) : updatedInput.val();

                      // If the pattern is a string of space characters, then set it to an empty string
                      if( typeof pattern == "string" && !pattern.trim().length ){
                        textDomain.pattern = [];
                      }
                      //Put the value inside a new array and update the textDomain object
                      else{
                        textDomain.pattern = [pattern];
                      }
                    }

                    //Manually trigger a change on the nonNumericDomain attribute
                    this.model.trigger("change:nonNumericDomain");

                	}
                	else if(updatedInput.is(".codelist")){
                		var row = updatedInput.parents(".code-row"),
                			index = this.$("." + this.model.get("measurementScale") + "-options .code-row").index(row);

                		this.updateCodeList(index);
                	}

                	//Add this EMLMeasurementScale model to the EMLAttribute model when it is updated in the view
                	var attributeModel = this.model.get("parentModel");

                	if( attributeModel )
                		attributeModel.set("measurementScale", this.model);
                },

                updateCodeList: function(rowNum){

                	//If the model is not set as an enumerated domain yet
            			if(!this.model.get("nonNumericDomain").length ||
            					!this.model.get("nonNumericDomain")[0] ||
            					!this.model.get("nonNumericDomain")[0].enumeratedDomain){

          				var isEmpty = false;

                  var emlModel = this.model.getParentEML();

          				//Go through each code row in this view and grab the values
          				_.each(this.$("." + this.model.get("measurementScale") + "-options .code-row"), function(row, i, rows){
          					var $row = $(row),
          						code = $row.find(".code").val(),
          						def  = $row.find(".definition").val();

                    code = emlModel? emlModel.cleanXMLText( code ) : code;
                    def  = emlModel? emlModel.cleanXMLText( def ) : def;

          					//Update the enumerated domain with this code
          					if(code || def){
              					this.model.updateEnumeratedDomain(code, def, i);
          					}
          					//If there is only one row and it has no code or definition,
          					//then this is an empty code list
          					else if( rows.length == 1 && i == 0){
          						isEmpty = true;
          					}

          				}, this);

          				//If there are no codes in the list, update the enumerated domain with blank values
          				if( isEmpty ){
          					this.model.updateEnumeratedDomain(null, null, rowNum);
          				}
          			}
          			else if(rowNum > -1){
          				var $row = $(this.$("." + this.model.get("measurementScale") + "-options .code-row")[rowNum]),
          						code = $row.find(".code").val(),
          						def  = $row.find(".definition").val();

                  code = emlModel? emlModel.cleanXMLText( code ) : code;
                  def  = emlModel? emlModel.cleanXMLText( def ) : def;

        					if(code || def){
        						this.model.updateEnumeratedDomain(code, def, rowNum);
        					}
          			}

              },

              previewCodeRemove: function(e){
              	$(e.target).parents(".code-row").toggleClass("remove-preview");
              }

            });

            return EMLMeasurementScaleView;
    });