	var AdvantageUpload = new Class({
			
		Extends: Swiff.Uploader,

		options: {
			filenameElement: false,
			useCurrentProgressBar: true,
			useOverallProgressBar: true,
			limitSize: false,
			limitFiles: 1,
			instantStart: false,
			allowDuplicates: false,
			validateFile: $lambda(true), // provide a function that returns true for valid and false for invalid files.
			debug: false,

			fileInvalid: null, // called for invalid files with error stack as 2nd argument
			fileCreate: null, // creates file element after select
			fileUpload: null, // called when file is opened for upload, allows to modify the upload options (2nd argument) for every upload
			fileComplete: null, // updates the file element to completed state and gets the response (2nd argument)
			fileRemove: null // removes the element
			/**
			 * Events:
			 * onBrowse, onSelect, onAllSelect, onCancel, onBeforeOpen, onOpen, onProgress, onComplete, onError, onAllComplete
			 */
		},

		initialize: function(status, options) {
			
			this.status = $(status);
			this.files = [];
			
			if (options.callBacks) {
				this.addEvents(options.callBacks);
				options.callBacks = null;
			}

			this.parent(options);
			this.log('Initialised AdvantageUpload');
			this.render();
		},

		render: function() {
			this.log('Rendering AdvantageUpload');
			var progress;
			if(this.options.useOverallProgressBar) {
				this.log('Creating overall progress bar');
				progress = this.status.getElement('.overall-progress');
				if(!progress) alert("ERROR: Invalid progress element (Overall)");
				this.overallProgress = new Fx.ProgressBar(progress, {
					text: new Element('span', {'class': 'progress-text overall'}).inject(progress, 'after')
				});
			} else {
				this.log('Hiding overall progress bar');
				if(this.status.getElement('.overall-progress')) {
					//alert(this.status.getElement('.overall-progress'));			
					this.status.getElement('.overall-progress').setStyle("display", "none");
					
				var elems = $$('.overall-progress');
					//alert("Hid it: " + this.status.getElement('.overall-progress').style.display + " L:" + elems.length);	
				}
			}
			if(this.options.useCurrentProgressBar) {	
				this.log('Creating current progress bar');
				progress = this.status.getElement('.current-progress')				
				if(!progress) alert("ERROR: Invalid progress element (Current): " + progress);
				this.currentProgress = new Fx.ProgressBar(progress, {
					text: new Element('span', {'class': 'progress-text current'}).inject(progress, 'after')
				});
			} else {
				this.log('Hiding current progress bar');
				if(this.status.getElement('.current-progress')) this.status.getElement('.current-progress').setStyle("display", "none");
			}
			
			this.currentText = this.status.getElement('.current-text')
			
		},
 
		onLoad: function() {
			this.log('Loading AdvantageUpload');
			this.currentText.set('html', 'Select file to upload.');
		},

		onBeforeOpen: function(file, options) {
			this.log('Initialize upload for "{name}".', file);
			var fn = this.options.fileUpload;
			var obj = (fn) ? fn.call(this, this.getFile(file), options) : options;

			if(this.options.useCurrentProgressBar) this.currentProgress.cancel().set(0);
			return obj;

		},

		onOpen: function(file, overall) {
			this.log('Starting upload "{name}".', file);
			// WHAT IS THIS? file = this.getFile(file);
		},

		onProgress: function(file, current, overall) {
			//var rate = (current.rate) ? this.sizeToKB(current.rate) : '- B';
			//var timeLeft = Date.fancyDuration(current.timeLeft || 0);
			
			this.log('Progressing upload "{name}".', file);
			//overall.bytesLoaded
			//overall.bytesTotal);
			if(this.options.useCurrentProgressBar) this.currentProgress.start(current.bytesLoaded, current.bytesTotal);	


			//this.currentText.set('html', 'Upload with ' + current.rate + '/s. Time left: ~{' + current.timeLeft + '}');
					
			this.currentText.set('html', 'Upload with {rate}/s. Time left: ~{timeLeft}'.substitute({
				rate: (current.rate) ? this.sizeToKB(current.rate) : '- B',
				timeLeft: Date.fancyDuration(current.timeLeft || 0)
			}));
		
			if(this.options.useOverallProgressBar) this.overallProgress.start(overall.bytesLoaded, overall.bytesTotal);
			
			
		},

		
		// onSelect is called for each selected file 
		onSelect: function(file, index, length) {
			var errors = [];
			//if (this.options.limitSize && (file.size > this.options.limitSize)) errors.push('size');
			if (this.options.limitFiles && (this.countFiles() >= this.options.limitFiles)) errors.push('length');
			//if (!this.options.allowDuplicates && this.getFile(file)) errors.push('duplicate');
			//if (!this.options.validateFile.call(this, file, errors)) errors.push('custom');
			if (errors.length) {
				this.log("Errors on {name} size {size}", file);
				//var fn = this.options.fileInvalid;
				//if (fn) fn.call(this, file, errors);
				return false;
			}
			if(this.options.filenameElement) this.options.filenameElement.value = file.name;
			this.files.push(file);
			this.log("Selected file {name} size {size}", file);

			return true;
		},

		onAllSelect: function(files, current, overall) {
			this.log('Added ' + files.length + ' files, now we have (' + current.bytesTotal + ' bytes).', arguments);
			if(this.options.instantStart) this.upload();
			else this.currentText.set('html', 'Ready to upload.');
		},

		onComplete: function(file, response) {
			this.log('Completed upload "' + file.name + '".', arguments);

			if(this.options.useCurrentProgressBar) this.currentProgress.start(100);
			
			this.log('Analyse response');			
			var json = $H(JSON.decode(response, true));
			if (json.get('result') == 'success') {
				this.uploaded = true;
				this.uploadedFilename = ('html', json.get('filename'));
				this.currentText.set('html', 'Upload complete!');
			} else {
				this.uploaded = false;
				this.currentText.set('html', 'ERROR:' + json.get('error'));
			}		
			
			this.files = [];
			
		},

		onError: function(file, error, info) {
			this.log('Upload "' + file.name + '" failed. "{1}": "{2}".', arguments);
			//(this.options.fileError || this.fileError).call(this, this.finishFile(file), error, info);
			this.currentText.set('html', 'Error: Upload "' + file.name + '" failed. ' + error + ": " + info);
			this.files = [];
		},

		onCancel: function() {
			this.log('Filebrowser cancelled.', arguments);
		},
		onBrowse: function() {
			this.log('Filebrowser opened.', arguments);
		},

		onAllComplete: function(current) {
			this.log('Completed all files, ' + current.bytesTotal + ' bytes.', arguments);
			if(this.options.useOverallProgress) this.overallProgress.start(100);
		},
		
		upload: function(options) {			
			var ret = this.parent(options);
			if (ret !== true) {
				this.log('Upload in progress or nothing to upload.');
				if (ret) alert(ret);
			} else {
				this.log('Upload started.');
				if(this.options.useOverallProgress) this.overallProgress.set(0);	    
			}
		},		
		
		log: function(text, args) {
			if (this.options.debug && window.console) console.log(text.substitute(args || {}));
		},

		countFiles: function() {
			var ret = 0;
			for (var i = 0, j = this.files.length; i < j; i++) {
				if (!this.files[i].finished) ret++;
			}
			return ret;
		},		


		getFile: function(file) {
			var ret = null;
			this.files.some(function(value) {
				if ((value.name != file.name) || (value.size != file.size)) return false;
				ret = value;			
				return true;
			});	
			return ret;
		},		
		
		sizeToKB: function(size) {
			var unit = 'B';
			if ((size / 1048576) > 1) {
				unit = 'MB';
				size /= 1048576;
			} else if ((size / 1024) > 1) {
				unit = 'kB';
				size /= 1024;
			}
			return size.round(1) + ' ' + unit;
		}
	});
	
	
	/**
	 * @todo Clean-up, into Date.js
	 */
	Date.parseDuration = function(sec) {
		var units = {}, conv = Date.durations;
		for (var unit in conv) {
			var value = Math.floor(sec / conv[unit]);
			if (value) {
				units[unit] = value;
				if (!(sec -= value * conv[unit])) break;
			}
		}
		return units;
	};

	Date.fancyDuration = function(sec) {
		var ret = [], units = Date.parseDuration(sec);
		for (var unit in units) ret.push(units[unit] + Date.durationsAbbr[unit]);
		return ret.join(', ');
	};

	Date.durations = {years: 31556926, months: 2629743.83, days: 86400, hours: 3600, minutes: 60, seconds: 1, milliseconds: 0.001};
	Date.durationsAbbr = {
		years: 'j',
		months: 'm',
		days: 'd',
		hours: 'h',
		minutes: 'min',
		seconds: 'sec',
		milliseconds: 'ms'
	};	