/**
 * Uploader allows for multiple files to be uploaded at once via AJAX
 * 1 value are submitted to the INTRIGO_UPLOADER:
 * - file: the file info being uploaded (ex.  $_FILES['INTRIGO_UPLOADER'])
 * @class Upoader
 * @namespace INTRIGO
 * @requires INTRIGO
 * @param {Object|String} container: the container you want to display the uploader)
 * @param {String} formAction: the action the form performs on submit
 */
INTRIGO.Uploader = function(container, formAction){
	try {
		if (container) {
			if (typeof(container) == 'string') 
				container = document.getElementById(container);
			
			INTRIGO.addClassName(container, 'INTRIGO Uploader');
			container.appendChild(this.createAddLink());
			
			this.container = container;
			this.files = {};
			this.successStatus = 'SUCCESS';
			this.errorStatus = 'ERROR';
			this.formAction = formAction;
		}
		return this;
	} 
	catch (e) {
		alert('Document constructor error: ' + e.message);
	}
}
INTRIGO.Uploader.prototype = {
	container:null,
	files:null,
	linkElt:null,

	sizeTooBig: function() {
		alert('The file you\'re trying to upload is too big.');
	},
	
	/**
	 * creates a new INTRIGO.Uploader.File
	 * allows for adding a new file to upload
	 * @param {Object} fileInfo (optional): allows you to pass an existing file to retrieve the same behavior as when you added it
	 */
	addNewFile: function(fileInfo) {
		var uid = 'INTRIGO.Uploader' + INTRIGO.uid();
		var file = new INTRIGO.Uploader.File(uid, this);
		this.container.insertBefore(file.getContainer(), this.linkElt);
		this.files[uid] = file;
		
		//debug mode
		if (this.debug == true) {
			file.iframe.style.display = 'block';
			file.iframe.style.width = '600px';
			file.iframe.style.height = '300px';
		}
		
		/* BUGFIX: for dynamically added frames in Internet Explorer */
		if (frames[uid].name != uid) {
			frames[uid].name = uid;
		}
		
		if (fileInfo) {
			file.response(this.successStatus, fileInfo);
		}
	},
	
	/**
	 * removes a file from the "files" hash based on its 'id'
	 * @param {Integer} id: id associated with the file
	 */
	removeFile: function(id) {
		// store the data
		var data = this.files[id].getData();
		
		// delete object
		this.files[id] = null;
		delete(this.files[id]);
		
		// call remove function if any
		if (INTRIGO.isFunction(this.removeFunc))
			this.removeFunc(data);
		
		// notify the uploader that this doesn't exist anymore
		this.notify();
	},
	
	/**
	 * shows the iframe for debugging purposes
	 * @return instance of self
	 */
	debug: function() {
		this.debug = true;
		return this;
	},
	
	
	/**
	 * @return {String}: returns the form action
	 */
	getFormAction: function() {
		return this.formAction;
	},
	
	/**
	 * sets a function to be called on loading
	 * loading function will be called with an instance of the file as a parameter
	 * @param {Function} name
	 * @return instance of self for cascaded function calls
	 */
	setLoadingFunction: function(name) {
		if (INTRIGO.isFunction(name)) {
			this.loadingFunc = name;
		}
		return this;
	},
	
	/**
	 * sets a function to be called on success of ALL files
	 * @param {Function} name
	 */	
	setGlobalSuccessFunction: function(name) {
		if (INTRIGO.isFunction(name)) {
			this.globalSuccessFunc = name;
		}
		return this;
	},
	
	/**
	 * sets a function to be called on success of ALL files
	 * success function will be called with 2 params:
	 * - instance of the file
	 * - data passed from iframe
	 * @param {Function} name
	 * @param {String} status: whatever you want the success status to be named (by default: 'SUCCESS')
	 * @return instance of self for cascaded function calls
	 */	
	setSuccessFunction: function(name, status) {
		if (status)
			this.successStatus = status;
		if (INTRIGO.isFunction(name)) {
			this.successFunc = name;
		}
		return this;
	},
	
	/**
	 * sets a function to be called on error
	 * error function is called with 2 params:
	 * - instance of the file
	 * - data passed from iframe
	 * @param {Function} name
	 * @param {String} status: whatever you want the error status to be named (by default: 'ERROR')
	 * @return instance of self for cascaded function calls
	 */
	setErrorFunction: function(name, status) {
		if (status)
			this.errorStatus = status;
		if (INTRIGO.isFunction(name)) {
			this.errorFunc = name;
		}
		return this;
	},
	
	/**
	 * sets a function to be called on remove
	 * @param {Function} name
	 * @return instance of self for cascaded function calls
	 */	
	setRemoveFunction: function(name) {
		if (INTRIGO.isFunction(name)) {
			this.removeFunc = name;
		}
		return this;
	},
	
	/**
	 * goes through each file and checks the status
	 * will call one of 3 functions defined by the user depending on status (LOADING, ERROR, SUCCESS)
	 */
	notify: function() {
		var count = 0;
		var success = 0;
		for (var i in this.files) {
			count++;
			var status = this.files[i].getStatus();
			if (status == 'LOADING') {
				if (INTRIGO.isFunction(this.loadingFunc))
					this.loadingFunc(this.files[i]);
				return false;
			}
			else if (status == this.successStatus) {
				if (!this.files[i].succeeded) {
					if (INTRIGO.isFunction(this.successFunc)) {
						this.successFunc(this.files[i], this.files[i].getData());
					}
					this.files[i].succeeded = true;
				}
				success++;
			}
			else if (status == this.errorStatus) {
				if (INTRIGO.isFunction(this.errorFunc)) 
					this.errorFunc(this.files[i], this.files[i].getData());
				else alert('There was an error uploading certain files');
			}
		}
		//check if all successfull
		if (count == success) {
			if (INTRIGO.isFunction(this.successFunc)) {
				this.globalSuccessFunc();
			}
		}
	},
	
	/* DOM element creation */
	createAddLink: function() {	
		var self = this;
		var link = document.createElement('a');
		link.href = '#';
		link.className = 'document add new';
		link.innerHTML = 'Add a new document';
		link.onclick = function(){
			self.addNewFile();
			return false;
		}
		this.linkElt = link;
		return link;
	}
};
INTRIGO.Uploader.files = {};
INTRIGO.Uploader.File = function(id, uploaderRef){
	this.id = id;
	this.uploaderRef = uploaderRef;
	var form = this.createForm(id, uploaderRef);
	form.appendChild(this.createBrowseButton(id));
	
	
	var div = document.createElement('div');
	div.className = 'file';
	div.appendChild(form);
	div.appendChild(this.createFilenameSpan());
	div.appendChild(this.createLoadingDiv());
	div.appendChild(this.createRemoveButton());
	div.appendChild(this.createIFrame(id));
	
	var clearboth = document.createElement('div');
	clearboth.className = 'clearBoth';
	div.appendChild(clearboth);
	
	this.container = div;
	
	return this;
}
INTRIGO.Uploader.File.prototype = {
	uploaderRef:null,
	
	container:null,
	form:null,
	iframe:null,
	fileElt:null,
	loadingElt:null,
	
	
	id:null,
	data: null,
	status: null,
	
	/**
	 * @return DOM: div containing all file information
	 */
	getContainer: function() {
		return this.container;
	},
	
	/**
	 * @return {Object}: whatever the user returned from the iframe by calling "response(status, fileData)"
	 */
	getData: function() {
		return this.data;
	},
	
	/**
	 * #return {String}: returns the status of the file
	 */
	getStatus: function() {
		return this.status;
	},
	
	/**
	 * sets the file status
	 * @param {String} value
	 */
	setStatus: function(value) {
		this.status = value;
	},
	
	/**
	 * sets the filename that shows after a file is selected
	 */
	setFilename: function(name) {
		this.filenameElt.innerHTML = name;
	},
	
	/**
	 * removes this file from the display, notifies the uploader that the file was removed
	 */
	remove: function() {
		this.iframe.setAttribute('src', 'about:blank');
		this.container.parentNode.removeChild(this.container);
		if (this.uploaderRef)
			this.uploaderRef.removeFile(this.id);
	},
	
	/**
	 * sends the file via iframe
	 * hides controls and replaces it with the file value
	 * @param {String} value: browse button value
	 */
	upload: function(value) {
		this.form.submit();
		
		//store this object in global variable for iframe callback
		INTRIGO.Uploader.files[this.id] = this;

		//layout changes
		this.filenameElt.innerHTML = value;
		
		this.form.style.display = 'none';
		this.loading();
	},

	/**
	 * upload callback and removes the form
	 * this function is called from the iframe
	 * @param {String} status
	 * @param {Object} fileData
	 */
	response: function(status, fileData) {
		this.data = fileData;
		this.notLoading(status);
		this.form.parentNode.removeChild(this.form);
	},
	
	/**
	 * displays the 'loading' div and notifies uploader
	 */
	loading: function() {
		this.loadingElt.style.display = 'block';
		this.setStatus('LOADING');
		this.uploaderRef.notify();
	},

	/**
	 * hides the 'loading' div and notifies uploader
	 * @param {Object} status
	 */
	notLoading: function(status) {
		this.loadingElt.style.display = 'none';
		this.setStatus(status);
		this.uploaderRef.notify();
	},
	
	/* DOM element creation */

	createForm: function(id, uploaderRef) {
		var form = document.createElement('form');
		form.enctype = 'multipart/form-data';
		form.encoding = 'multipart/form-data';
		form.action = uploaderRef.getFormAction();
		form.method = 'post';
		form.target = id;
		this.form = form;
		return form;
	},

	createFilenameSpan: function() {
		var span = document.createElement('span');
		span.className = 'filename';
		this.filenameElt = span;
		return span;
	},
	
	createBrowseButton: function(){
		var self = this;
		var file = document.createElement('input');
		file.type = 'file';
		file.name = 'INTRIGO_UPLOADER';
		
		// as soon as a file is selected, start uploading
		file.onchange = function(){
			if (this.value != '') {
				self.upload(this.value);
			}
		}
		
		this.fileElt = file;
		
		return file;
	},
	
	createLoadingDiv: function(){
		var loading = document.createElement('div');
		loading.className = 'loading';
		loading.innerHTML = 'loading...';
		loading.style.display = 'none';
		
		this.loadingElt = loading;
		
		return loading;
	},
	
	createIFrame: function(id){
		var iframe = document.createElement('iframe');
		iframe.setAttribute('src', 'about:blank');
		iframe.setAttribute('id', id);
		iframe.setAttribute('NAME', id);
		iframe.style.display = 'none';
		this.iframe = iframe;
		return iframe;
	},
	
	createRemoveButton: function(){
		var self = this;
		var link = document.createElement('a');
		link.href = '#';
		link.innerHTML = 'remove';
		link.className = 'remove';
		link.onclick = function(){
			self.remove();
			return false;
		}
		return link;
	}
};