/*global define */
define(['jquery', 'underscore', 'backbone', 'jws', 'models/Search', "collections/SolrResults"], 				
	function($, _, Backbone, JWS, SearchModel, SearchResults) {
	'use strict';

	// User Model
	// ------------------
	var UserModel = Backbone.Model.extend({
		defaults: function(){
			return{
				type: "person", //assume this is a person unless we are told otherwise - other possible type is a "group"
				checked: false, //Set to true when we have checked the status of this user
				basicUser: false, //Set to true to only query for basic info about this user - prevents sending queries for info that will never be displayed in the UI
				lastName: null,
				firstName: null,
				fullName: null,
				email: null,
				logo: null,
				description: null,
				verified: null,
				username: null,
				usernameReadable: null,
				orcid: null,
				searchModel: null,
				searchResults: null,
				loggedIn: false,
				ldapError: false, //Was there an error logging in to LDAP
				registered: false,
				isMemberOf: [],
				isOwnerOf: [],
				identities: [],
				identitiesUsernames: [],
				pending: [],
				token: null,
				expires: null,
				rawData: null
			}
		},
		
		initialize: function(options){
			if(typeof options !== "undefined"){
				if(options.username) this.set("username", options.username);
				if(options.rawData)  this.set(this.parseXML(options.rawData));
			}
			
			this.on("change:identities", this.pluckIdentityUsernames);
						
			this.on("change:username change:identities change:type", this.updateSearchModel);
			this.createSearchModel();
			
			this.on("change:username", this.createReadableUsername());
						
			//Create a search results model for this person
			var searchResults = new SearchResults([], { rows: 5, start: 0 });
			this.set("searchResults", searchResults);
		},
		
		createSearchModel: function(){			
			//Create a search model that will retrieve data created by this person
			this.set("searchModel", new SearchModel());	
			this.updateSearchModel();
		},
		
		updateSearchModel: function(){	
			if(this.get("type") == "node"){
				this.get("searchModel").set("dataSource", [this.get("node").identifier]);
				this.get("searchModel").set("username", []);
			}
			else{
				//Get all the identities for this person
				var ids = [this.get("username")];
				
				_.each(this.get("identities"), function(equivalentUser){
					ids.push(equivalentUser.get("username"));
				});
				this.get("searchModel").set("username", ids);
			}
			
			this.trigger("change:searchModel");
		},
		
		parseXML: function(data){
			var model = this,
				username = this.get("username");
			
			//Reset the group list so we don't just add it to it with push()
			this.set("isMemberOf", this.defaults().isMemberOf, {silent: true});
			this.set("isOwnerOf", this.defaults().isOwnerOf, {silent: true});
			//Reset the equivalent id list so we don't just add it to it with push()
			this.set("identities", this.defaults().identities, {silent: true});
			
			//Find this person's node in the XML
			var userNode = null;
			if(!username)
				var username = $(data).children("subject").text();			
			if(username){			
				var subjects = $(data).find("subject");
				for(var i=0; i<subjects.length; i++){
					if($(subjects[i]).text().toLowerCase() == username.toLowerCase()){
						userNode = $(subjects[i]).parent();
						break;
					}
				}
			}
			if(!userNode)
				userNode = $(data).first();
			
			//Get the type of user - either a person or group
			var type = $(userNode).prop("tagName");
			if(type) type = type.toLowerCase();
			
			if(type == "group"){
				var fullName = $(userNode).find("groupName").first().text();
			}
			else if(type){
				//Find the person's info
				var	firstName  = $(userNode).find("givenName").first().text(),
					lastName   = $(userNode).find("familyName").first().text(),
					email      = $(userNode).find("email").first().text(),
					verified   = $(userNode).find("verified").first().text(),
					memberOf   = this.get("isMemberOf"),
					ownerOf	   = this.get("isOwnerOf"),
					identities = this.get("identities");
				
				//Sometimes names are saved as "NA" when they are not available - translate these to false values
				if(firstName == "NA")
					firstName = null;
				if(lastName == "NA")
					lastName = null;
				
				//Construct the fullname from the first and last names, but watch out for falsely values
				var fullName = "";
					fullName += firstName? firstName : "";
					fullName += lastName? (" " + lastName) : "";
					
				if(!fullName)
					fullName = this.getNameFromSubject(username);
					
				//Don't get this detailed info about basic users
				if(!this.get("basicUser")){
					//Get all the equivalent identities for this user
					var equivalentIds = $(userNode).find("equivalentIdentity");
					if(equivalentIds.length > 0)
						var allPersons = $(data).find("person subject");
	
					_.each(equivalentIds, function(identity, i){
						//push onto the list
						var username = $(identity).text(),
							equivUserNode;
						
						//Find the matching person node in the response
						_.each(allPersons, function(person){
							if($(person).text().toLowerCase() == username.toLowerCase()){
								equivUserNode = $(person).parent().first();
								allPersons = _.without(allPersons, person);
							}
						});
						
						var equivalentUser = new UserModel({ username: username, basicUser: true, rawData: equivUserNode });						
						identities.push(equivalentUser);
					});
				}
				
				//Get each group and save
				_.each($(data).find("group"), function(group, i){
					//Save group ID
					var groupId = $(group).find("subject").first().text(),
						groupName = $(group).find("groupName").text();
					
					memberOf.push({ groupId: groupId, name: groupName });
									
					//Check if this person is a rightsholder
					var allRightsHolders = [];
					_.each($(group).children("rightsHolder"), function(rightsHolder){
						allRightsHolders.push($(rightsHolder).text().toLowerCase());
					});
					if(_.contains(allRightsHolders, username.toLowerCase()))
						ownerOf.push(groupId);
				});
			}
			
			return {
				isMemberOf: memberOf,
				isOwnerOf: ownerOf,
				identities: identities,
				verified: verified,
				username: username,
				firstName: firstName,
				lastName: lastName,
				fullName: fullName,
				email: email,
				registered: true,
				type: type,
				rawData: data
			}
		},
		
		getInfo: function(){
			var model = this;
			
			//If the accounts service is not on, flag this user as checked/completed
			if(!appModel.get("accountsUrl")){
				this.set("fullName", this.getNameFromSubject());
				this.set("checked", true);
				return;
			}
			
			//Only proceed if there is a username
			if(!this.get("username")) return;
				
				/*
			//Check if this is an ORCID
			if(this.isOrcid()){
				//Get the person's info from their ORCID bio
				appLookupModel.orcidGetBio({ 
					userModel: this,
					success: function(){
						model.set("checked", true);
					},
					error: function(){
						model.set("checked", true);						
					}
				});
				return;
			}*/
			

			//Get the user info using the DataONE API
			var url = appModel.get("accountsUrl") + encodeURIComponent(this.get("username"));
			
			var requestSettings = {
				type: "GET",
				url: url, 
				success: function(data, textStatus, xhr) {	
					//Parse the XML response to get user info
					var userProperties = model.parseXML(data);
					//Filter out all the falsey values
					_.each(userProperties, function(v, k) {
				      if(!v) {
				        delete userProperties[k];
				      }
				    });
					model.set(userProperties);
					
					 //Trigger the change events
					model.trigger("change:isMemberOf");
					model.trigger("change:isOwnerOf");
					model.trigger("change:identities");
					
					model.set("checked", true);
				},
				error: function(xhr, textStatus, errorThrown){
					// Sometimes the node info has not been received before this getInfo() is called.
					// If the node info was received while this getInfo request was pending, and this user was determined
					// to be a node, then we can skip any further action here.
					if(model.get("type") == "node")
						return;
					
					if((xhr.status == 404) && nodeModel.get("checked")){
						model.set("fullName", model.getNameFromSubject());
						model.set("checked", true);
					}
					else if((xhr.status == 404) && !nodeModel.get("checked")){
						model.listenToOnce(nodeModel, "change:checked", function(){
							if(!model.isNode()){
								model.set("fullName", model.getNameFromSubject());
								model.set("checked", true);
							}
						});
					}
					else{
						//As a backup, search for this user instead
						var requestSettings = {
								type: "GET",
								url: appModel.get("accountsUrl") + "?query=" + encodeURIComponent(model.get("username")), 
								success: function(data, textStatus, xhr) {	
									//Parse the XML response to get user info
									model.set(model.parseXML(data));
									
									 //Trigger the change events
									model.trigger("change:isMemberOf");
									model.trigger("change:isOwnerOf");
									model.trigger("change:identities");
									
									model.set("checked", true);
								},
								error: function(){
									//Set some blank values and flag as checked
									model.set("username", "");
									model.set("fullName", "");
									model.set("notFound", true);
									model.set("checked", true);
								}
							}
						//Send the request
						$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));	
						
					}
				}
			}
			
			//Send the request
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));			
		},
		
		//Get the pending identity map requests, if the service is turned on
		getPendingIdentities: function(){
			if(!appModel.get("pendingMapsUrl")) return false;
			
			var model = this;
			
			//Get the pending requests			
			var requestSettings = {
				url: appModel.get("pendingMapsUrl") + encodeURIComponent(this.get("username")),
				success: function(data, textStatus, xhr){
					//Reset the equivalent id list so we don't just add it to it with push()
					model.set("pending", model.defaults().pending);
					var pending = model.get("pending");
					_.each($(data).find("person"), function(person, i) {
						
						//Don't list yourself as a pending map request
						var personsUsername = $(person).find("subject").text();
						if(personsUsername.toLowerCase() == model.get("username").toLowerCase())
							return;
						
						//Create a new User Model for this pending identity
						var pendingUser = new UserModel({ rawData: person });
						
						if(pendingUser.isOrcid())
							pendingUser.getInfo();
						
						pending.push(pendingUser);
					});
					model.set("pending", pending);	
					model.trigger("change:pending"); //Trigger the change event
				},
				error: function(xhr, textStatus){
					if(xhr.responseText.indexOf("error code 34")){
						model.set("pending", model.defaults().pending);
						model.trigger("change:pending"); //Trigger the change event
					}
				}
			}
			
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));			
		},
				
		getNameFromSubject: function(username){
			var username  = username || this.get("username"),
				fullName = "";
			
			if(!username) return;
			
			var ldapUidAttribute = appModel.get("ldapUidAttribute") || "uid" || "";
			
			if((username.indexOf(ldapUidAttribute + "=") > -1) && (username.indexOf(",") > -1))
				fullName = username.substring(username.indexOf(ldapUidAttribute + "=") + 4, username.indexOf(","));
			else if((username.indexOf("CN=") > -1) && (username.indexOf(",") > -1))
				fullName = username.substring(username.indexOf("CN=") + 3, username.indexOf(","));
			
			//Cut off the last string after the name when it contains digits - not part of this person's names 
			if(fullName.lastIndexOf(" ") > fullName.indexOf(" ")){
				var lastWord = fullName.substring(fullName.lastIndexOf(" "));
				if(lastWord.search(/\d/) > -1)
					fullName = fullName.substring(0, fullName.lastIndexOf(" "));
			}
			
			//Default to the username
			if(!fullName) fullName = this.get("fullname") || username;
						
			return fullName;
		},
		
		isOrcid: function(orcid){
			var username = (typeof orcid === "string")? orcid : this.get("username");
			
			//Have we already verified this?
			if((typeof orcid == "undefined") && (username == this.get("orcid"))) return true;
			
			//Checks for ORCIDs using the orcid base URL as a prefix
			if(username.indexOf("http://orcid.org/") == 0){
				this.set("orcid", username);
				return true;
			}

			//If the ORCID base url is not present, we will check if this is a 19-digit ORCID ID
			//A simple and fast check first
			//ORCiDs are 16 digits and 3 dashes - 19 characters
			if(username.length != 19) return false;
								
			/* The ORCID checksum algorithm to determine is a character string is an ORCiD 
			 * http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
			 */	
			var total = 0,
				baseDigits = username.replace(/-/g, "").substr(0, 15);
			
			for(var i=0; i<baseDigits.length; i++){
				var digit = parseInt(baseDigits.charAt(i));
				total = (total + digit) * 2;
			}
			
			var remainder = total % 11,
				result = (12 - remainder) % 11,
				checkDigit = (result == 10) ? "X" : result.toString(),
				isOrcid = (checkDigit == username.charAt(username.length-1));
			
			if(isOrcid)
				this.set("orcid", username);
			
			return isOrcid;
		},
		
		isNode: function(){
			var node = _.where(nodeModel.get("members"), { shortIdentifier: this.get("username") });
			return (node && node.length)
		},
		
		// Will check if this user is a Member Node. If so, it will save the MN info to the model
		saveAsNode: function(){
			if(!this.isNode()) return;
			
			var node = _.where(nodeModel.get("members"), { shortIdentifier: this.get("username") })[0];
			
			this.set({
				type: "node",
				logo: node.logo,
				description: node.description,
				node: node,
				fullName: node.name,
				usernameReadable: this.get("username")
			});
			this.updateSearchModel();
			this.set("checked", true);
		},
		
		loginLdap: function(formData, success, error){
			if(!formData || !appModel.get("signInUrlLdap")) return false;
			
			var model = this;
			
			var requestSettings = {
				type: "POST",
				url: appModel.get("signInUrlLdap") + window.location.href, 
				data: formData, 
				success: function(data, textStatus, xhr){
					if(success)
						success(this);
					
					model.getToken();
					
					//Direct to the Ldap sign in
					//window.location = appModel.get("signInUrlLdap") + window.location.href;
				},
				error: function(){
					/*if(error)
						error(this);
					*/
					model.getToken();
				}
			}
			
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));			
		},
		
		logout: function(){			
			//Logout via the registry script if we are not using tokens
			if((typeof appModel.get("tokenUrl") == "undefined") || !appModel.get("tokenUrl")){
				appView.registryView.logout();
				return;
			}
			
			//Construct the sign out url and redirect
			var signOutUrl = appModel.get('signOutUrl'),
				target = Backbone.history.location.href;
			
			// DO NOT include the route otherwise we have an infinite redirect
			target  = target.split("#")[0];
			
			// make sure to include the target
			signOutUrl += "?target=" + target;
			
			// do it!
			window.location = signOutUrl;
		},
		
		// call Metacat or the DataONE CN to validate the session and tell us the user's name
		checkStatus: function(onSuccess, onError) {
			var model = this;
			
			if (!appModel.get("tokenUrl")) {				
				// look up the URL
				var metacatUrl = appModel.get('metacatServiceUrl');
				
				// ajax call to validate the session/get the user info
				var requestSettings = {
					type: "POST",
					url: metacatUrl,
					data: { action: "validatesession" },
					success: function(data, textStatus, xhr) {						
						// the Metacat (XML) response should have a fullName element
						var username = $(data).find("name").text();
						
						// set in the model
						model.set('username', username);
						
						//Are we logged in?
						if(username){
							model.set("loggedIn", true);
							model.getInfo();
						}
						else{
							model.set("loggedIn", false);
							model.trigger("change:loggedIn");
							model.set("checked", true);
						}

						if(onSuccess) onSuccess(data);

					},
					error: function(data, textStatus, xhr){						
						//User is not logged in
						model.reset();
						
						if(onError) onError();
					}
				}
				
				$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));			
			} else {
				// use the token method for checking authentication
				this.getToken();
			}			
		},
		
		getToken: function(customCallback) {

			var tokenUrl = appModel.get('tokenUrl');
			var model = this;
			
			if(!tokenUrl) return false;
			
			//Set up the function that will be called when we retrieve a token
			var callback = (typeof customCallback === "function") ? customCallback : function(data, textStatus, xhr) {
				
				// the response should have the token
				var payload = model.parseToken(data),
					username = payload ? payload.userId : null,
					fullName = payload ? payload.fullName : model.getNameFromSubject(username) || null,
					token    = payload ? data : null,
					loggedIn = payload ? true : false;

				// set in the model
				model.set('fullName', fullName);
				model.set('username', username);
				model.set("token", token);
				model.set("loggedIn", loggedIn);

				model.getTokenExpiration(payload);
				
				if(username)
					model.getInfo();
				else
					model.set("checked", true);
			};
			
			// ajax call to get token
			var requestSettings = {
				type: "GET",
				dataType: "text",
				xhrFields: {
					withCredentials: true
				},
				url: tokenUrl,
				data: {},
				success: callback,
				error: function(xhr, textStatus, errorThrown){
					model.set("checked", true);
				}
			}
			
			$.ajax(requestSettings);			
		},
		
		getTokenExpiration: function(payload){
			if(!payload && this.get("token")) var payload = this.parseToken(this.get("token"));
			if(!payload) return;
			
			//The exp claim should be standard - it is in UTC seconds 
			var expires = payload.exp? new Date(payload.exp * 1000) : null;
				
			//Use the issuedAt and ttl as a backup (only used in d1 2.0.0 and 2.0.1)
			if(!expires){
				var issuedAt = payload.issuedAt? new Date(payload.issuedAt) : null,
					lifeSpan = payload.ttl? payload.ttl : null;
				
				if(issuedAt && lifeSpan && (lifeSpan > 99999)) 
					issuedAt.setMilliseconds(lifeSpan);
				else if(issuedAt && lifeSpan)
					issuedAt.setSeconds(lifeSpan);
				
				expires = issuedAt;
			}
							
			this.set("expires", expires);
		},
		
		checkToken: function(onSuccess, onError){
						
			//First check if the token has expired
			if(appUserModel.get("expires") > new Date()){				
				if(onSuccess) onSuccess();
								
				return;
			}
			
			var model = this;
			
			var url = appModel.get("tokenUrl");			
			if(!url) return;
			
			var requestSettings = {
					type: "GET",
					url: url,
					success: function(data, textStatus, xhr){
						if(data){
							// the response should have the token
							var payload = model.parseToken(data),
								username = payload ? payload.userId : null,
								fullName = payload ? payload.fullName : null,
								token    = payload ? data : null,
								loggedIn = payload ? true : false;

							// set in the model
							model.set('fullName', fullName);
							model.set('username', username);
							model.set("token", token);
							model.set("loggedIn", loggedIn);

							model.getTokenExpiration(payload);
							
							appUserModel.set("checked", true);
							
							if(onSuccess) onSuccess(data, textStatus, xhr);
						}
						else if(onError) 
							onError(data, textStatus, xhr);
					},
					error: function(data, textStatus, xhr){
						//If this token in invalid, then reset the user model/log out 
						appUserModel.reset();
						
						if(onError) onError(data, textStatus, xhr);
					}
			}
			
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));	
		},
		
		parseToken: function(token) {
			if(typeof token == "undefined")
				var token = this.get("token");
			
			var jws = new KJUR.jws.JWS();
			var result = 0;
			try {
				result = jws.parseJWS(token);
			} catch (ex) {
			    result = 0;
			}
				
			if(!jws.parsedJWS) return "";
				
			var payload = $.parseJSON(jws.parsedJWS.payloadS);
			return payload;			
		},
		
		verifyLoginStatus: function(){
			if(!appUserModel.get("loggedIn")) return;
						
			if(appModel.get("tokenUrl")){
				//If the user's token is no longer valid, then refresh the page
				appUserModel.checkToken();
			}
			else{
				appUserModel.checkStatus();
			}
		},
		
		update: function(onSuccess, onError){
			var model = this;
			
			var person = 
				'<?xml version="1.0" encoding="UTF-8"?>'
				+ '<d1:person xmlns:d1="http://ns.dataone.org/service/types/v1">'
					+ '<subject>' + this.get("username") + '</subject>'
					+ '<givenName>' + this.get("firstName") + '</givenName>'
					+ '<familyName>' + this.get("lastName") + '</familyName>'
					+ '<email>' + this.get("email") + '</email>'
				+ '</d1:person>';
			
			var xmlBlob = new Blob([person], {type : 'application/xml'});
			var formData = new FormData();
			formData.append("subject", this.get("username"));
			formData.append("person", xmlBlob, "person");

			var updateUrl = appModel.get("accountsUrl") + encodeURIComponent(this.get("username"));
						
			// ajax call to update
			var requestSettings = {
				type: "PUT",
				cache: false,
			    contentType: false,
			    processData: false,
				url: updateUrl,
				data: formData,
				success: function(data, textStatus, xhr) {
					if(typeof onSuccess != "undefined") 
						onSuccess(data);
					
					//model.getInfo();
				},
				error: function(data, textStatus, xhr) {
					if(typeof onError != "undefined") 
						onError(data);					
				}
			}
			
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));			
		},
		
		confirmMapRequest: function(otherUsername, onSuccess, onError){
			if(!otherUsername) return;

			var mapUrl = appModel.get("pendingMapsUrl") + encodeURIComponent(otherUsername),
				model = this;	

			if(!onSuccess)
				var onSuccess = function(){};
			if(!onError)
				var onError = function(){};
			
			// ajax call to confirm map
			var requestSettings = {
				type: "PUT",
				url: mapUrl,
				success: function(data, textStatus, xhr) {
					if(onSuccess)
						onSuccess(data, textStatus, xhr);
					
					//Get updated info
					model.getInfo();
				},
				error: function(xhr, textStatus, error) {
					if(onError)
						onError(xhr, textStatus, error);
				}
			}
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));
		},
		
		denyMapRequest: function(otherUsername, onSuccess, onError){
			if(!otherUsername) return;
			
			var mapUrl = appModel.get("pendingMapsUrl") + encodeURIComponent(otherUsername),
				model = this;	
		
			// ajax call to reject map
			var requestSettings = {
				type: "DELETE",
				url: mapUrl,
				success: function(data, textStatus, xhr) {
					if(typeof onSuccess == "function")
						onSuccess(data, textStatus, xhr);
					
					model.getInfo();
				},
				error: function(xhr, textStatus, error) {
					if(typeof onError == "function")
						onError(xhr, textStatus, error);
				}
			}
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));			
		},
		
		addMap: function(otherUsername, onSuccess, onError){
			if(!otherUsername) return;
			
			var mapUrl = appModel.get("pendingMapsUrl"),
				model = this;
			
			// ajax call to map
			var requestSettings = {
				type: "POST",
				xhrFields: {
					withCredentials: true
				},
				headers: {
			        "Authorization": "Bearer " + this.get("token")
			    },
				url: mapUrl,
				data: {
					subject: otherUsername
				},
				success: function(data, textStatus, xhr) {
					if(typeof onSuccess == "function")
						onSuccess(data, textStatus, xhr);
					
					model.getInfo();
				},
				error: function(xhr, textStatus, error) {
					if(typeof onError == "function")
						onError(xhr, textStatus, error);
				}
			}
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));			
		},
		
		removeMap: function(otherUsername, onSuccess, onError){
			if(!otherUsername) return;
			
			var mapUrl = appModel.get("accountsMapsUrl") + encodeURIComponent(otherUsername),
				model = this;
						
			// ajax call to remove mapping
			var requestSettings = {
				type: "DELETE",
				url: mapUrl,
				success: function(data, textStatus, xhr) {					
					if(typeof onSuccess == "function")
						onSuccess(data, textStatus, xhr);
					
					model.getInfo();
				},
				error: function(xhr, textStatus, error) {
					if(typeof onError == "function")
						onError(xhr, textStatus, error);
				}
			}
			$.ajax(_.extend(requestSettings, appUserModel.createAjaxSettings()));			
		},
		
		failedLdapLogin: function(){
			this.set("loggedIn", false);
			this.set("checked", true);
			this.set("ldapError", true);
		},
		
		pluckIdentityUsernames: function(){
			var models = this.get("identities"),
				usernames = [];
			
			_.each(models, function(m){
				usernames.push(m.get("username").toLowerCase());
			});
			
			this.set("identitiesUsernames", usernames);
			this.trigger("change:identitiesUsernames");
		},
		
		createReadableUsername: function(){
			if(!this.get("username")) return;
			
			var username = this.get("username"),
				readableUsername = username.substring(username.indexOf("=")+1, username.indexOf(",")) || username;
			
			this.set("usernameReadable", readableUsername);
		},
		
		createAjaxSettings: function(){
			if(!this.get("token")){
				return { 
					xhrFields: {
						withCredentials: true
					}
				}
			}
			
			return { xhrFields: {
					withCredentials: true
					},
				  headers: {
			        "Authorization": "Bearer " + this.get("token")
				  }
				}
		},
		
		reset: function(){			
			var defaults = _.omit(this.defaults(), ["searchModel", "searchResults"]);
			this.set(defaults);
		}
	});
	
	return UserModel;
});