/*global define */
define(['jquery', 'underscore', 'backbone', 'models/UserModel'],
	function($, _, Backbone, UserModel) {
	'use strict';
	/**
	 * @class UserGroup
	 * @classdesc The collection of Users that represent a DataONE group
   * @classcategory Collections
	 */
	var UserGroup = Backbone.Collection.extend(
    /** @lends UserGroup.prototype */{
		// Reference to this collection's model.
		model: UserModel,
		//Custom attributes of groups
		groupId: "",
		name: "",
		nameAvailable: null,
		url: function(){
			return MetacatUI.appModel.get("accountsUrl") + encodeURIComponent(this.groupId);
		},
		comparator: "lastName", //Sort by last name
		initialize: function(models, options) {
			if((typeof models == "undefined") || !models)
				var models = [];
			if(typeof options !== "undefined"){
				//Save our options
				$.extend(this, options);
				this.groupId = options.groupId || "";
				this.name    = options.name    || "";
				this.pending = (typeof options.pending === "undefined") ? false : options.pending;
				//If raw data is passed, parse it to get a list of users to be added to this group
				if(options.rawData){
					//Get a list of UserModel attributes to add to this collection
					var toAdd = this.parse(options.rawData);
					//Create a UserModel for each user
					_.each(toAdd, function(modelAttributes){
						//Don't pass the raw data to the UserModel creation because it is redundant-
						//We already parsed the raw data when we called add() above
						var rawDataSave = modelAttributes.rawData;
						modelAttributes.rawData = null;
						//Create the model then add the raw data back
						var member = new UserModel(modelAttributes);
						member.set("rawData", rawDataSave);
						models.push(member);
					});
				}
			}
			//Add all our models to this collection
			this.add(models);
		},
		/*
		 * Gets the group from the server. Options object uses the BackboneJS options API
		 */
		getGroup: function(options){
			if(!this.groupId && this.name){
				this.groupId = "CN=" + this.name + ",DC=dataone,DC=org";
			}
			this.fetch(options);
			return this;
		},
		/*
		 * Fetches the group info from the server. Should not be called directly - use getGroup() instead
		 */
		fetch: function (options) {
	        options = options || { silent: false, reset: false, remove: false };
	        options.dataType = "xml";
	        options.error = function(collection, response, options){
	        	//If this group is not found, then the name is available
	        	if((response.status == 404) && (response.responseText.indexOf("No Such Object") > -1)){
	        		collection.nameAvailable = true;
	        		collection.trigger("nameChecked", collection);
	        	}
	        }
	        return Backbone.Collection.prototype.fetch.call(this, options);
	    },
	    /*
	     * Backbone.js override - parses the XML reponse from DataONE and creates a JSON representation that will
	     * be used to create UserModels
	     */
		parse: function(response, options){
			if(!response) return;
			//This group name is not available/already taken
			this.nameAvailable = false;
			this.trigger("nameChecked", this);
			var group = $(response).find("group subject:contains('" + this.groupId + "')").parent("group"),
				people = $(response).find("person"),
				collection = this,
				toAdd = new Array(),
				existing = this.pluck("username");
			if(!people.length)
				people = $(group).find("hasMember");
			//Make all existing usernames lowercase for string matching
			if(existing.length) existing = _.invoke(existing, "toLowerCase");
			this.name = $(group).children("groupName").text();
			_.each(people, function(person){
				//The tag name is "hasMember" if we retrieved info about this group from the group nodes only
				if(person.tagName == "hasMember"){
					var username = $(person).text();
					//If this user is already in the group, skip adding it
					if(_.contains(existing, username.toLowerCase())) return;
					var user = new UserModel({ username: username }),
						userAttr = user.toJSON();
					toAdd.push(userAttr);
				}
				//The tag name is "person" if we retrieved info about this group through the /accounts service, which includes all nodes about all members
				else{
					//If this user is not listed as a member of this group, skip it
					if($(person).children("isMemberOf:contains('" + collection.groupId + "')").length < 1)
						return;
					//Username of this person
					var username = $(person).children("subject").text();
					//If this user is already in the group, skip adding it
					if(_.contains(existing, username.toLowerCase())) return;
					//User attributes - pass the full response for the UserModel to parse
					var userAttr = new UserModel({username: username}).parseXML(response);
					//Add to collection
					toAdd.push(userAttr);
				}
			});
			return toAdd;
		},
		/*
		 * An alternative to Backbone sync
		 * - will send a POST request to DataONE CNIdentity.createGroup() to create this collection as a new DataONE group
		 * or
		 * - will send a PUT request to DataONE CNIdentity.updateGroup() to update this existing DataONE group
		 *
		 *  If this group is marked as pending, then the group is created, otherwise it's updated
		 */
		save: function(onSuccess, onError){
			if(this.pending && (this.nameAvailable == false)) return false;
			var memberXML = "",
				ownerXML = "",
				collection = this;
			//Create the member and owner XML
			this.forEach(function(member){
				//Don't list yourself as an owner or member (implied)
				if(MetacatUI.appUserModel == member) return;
				var username = member.get("username") ? member.get("username").trim() : null;
				if(!username) return;
				memberXML += "" + username + "";
				if(collection.isOwner(member))
					ownerXML += "" + username + "";
			});
			//Create the group XML
			var groupXML =
				''
				+ ''
					+ ''   + this.groupId + ''
					+ '' + this.name    + ''
					+ memberXML
					+ ownerXML
				+ '';
			var xmlBlob = new Blob([groupXML], {type : 'application/xml'});
			var formData = new FormData();
			formData.append("group", xmlBlob, "group");
			// AJAX call to update
			$.ajax({
				type: this.pending? "POST" : "PUT",
				cache: false,
			    contentType: false,
			    processData: false,
				xhrFields: {
					withCredentials: true
				},
				headers: {
			        "Authorization": "Bearer " + MetacatUI.appUserModel.get("token")
			    },
				url: MetacatUI.appModel.get("groupsUrl"),
				data: formData,
				success: function(data, textStatus, xhr) {
					if(typeof onSuccess != "undefined")
						onSuccess(data);
					collection.pending = false;
					collection.nameAvailable = null;
					collection.getGroup();
				},
				error: function(xhr, textStatus, error) {
					if(typeof onError != "undefined")
						onError(xhr);
				}
			});
			return true;
		},
		/*
		 * For pending groups only (those in the creation stage)
		 * Will check if the given name/id is available
		 */
		checkName: function(name){
			//Only check the name for pending groups
			if(!this.pending) return;
			//Reset the name and ID
			this.name = name || this.name;
			this.groupId = null;
			this.nameAvailable = null;
			//Get group info/check name availablity
			this.getGroup({ add: false });
		},
		/*
		 * Retrieves the UserModels that are rightsHolders of this group
		 */
		getOwners: function(){
			var groupId = this.groupId;
			return _.filter(this.models, function(user){
				return _.contains(user.get("isOwnerOf"), groupId);
			});
		},
		/*
		 * Shortcut function - will check if a specified User is an owner of this group
		 */
		isOwner: function(model){
			if(typeof model === "undefined") return false;
			if(this.pending && (model == MetacatUI.appUserModel)) return true;
			var usernames = [];
			_.each(this.getOwners(), function(user){ usernames.push(user.get("username")); });
			return _.contains(usernames, model.get("username"));
		}
	});
	return UserGroup;
});