var Uploader = new Class({

	Extends: Swiff.Uploader,

	options: {
		limitSize: false,
		multiple: false,
		queued: false,
		instantStart: 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
		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)
		/**
		 * 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.render();
	},

	render: function() {
		var progress = new Element('span', {'class': 'overall-progress'}).inject(this.status);
		var text = new Element('span', {'class': 'progress-text'}).inject(progress, 'after');
		this.progressBar = new Fx.ProgressBar(progress, {text: text});
	},

	onLoad: function() {
		this.log('Uploader ready!');
	},

	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;

		return obj;
	},

	onOpen: function(file, overall) {
		this.log('Starting upload "{name}".', file);
	},

	onProgress: function(file, current, overall) {
		this.progressBar.start(overall.bytesLoaded, overall.bytesTotal);
		this.log('Uploading "' + file.name + '" "' + overall.bytesLoaded + '": "' + overall.bytesTotal + '".');
	},

	onSelect: function(file) {
		var errors = [];
		if(this.options.limitSize && (file.size > this.options.limitSize)) errors.push('size');
		if(!this.options.validateFile.call(this, file, errors)) errors.push('custom');
		if(errors.length) {
			var fn = this.options.fileInvalid;
			if(fn) fn.call(this, file, errors);

			return false;
		}
		this.removeFile();
		this.files.push(file);

		return true;
	},

	onAllSelect: function(files, current, overall) {
		this.log('Added ' + files.length + ' files, now we have (' + current.bytesTotal + ' bytes).', arguments);
		this.status.removeClass('file-browsing');
		if(this.files.length && this.options.instantStart) this.upload.delay(10, this);
	},

	onComplete: function(file, response) {
		this.log('Completed upload "' + file.name + '".', arguments);
		this.progressBar.start(100);
		var fn = this.options.fileComplete;
		if(fn) fn.call(this, this.finishFile(file), response);
	},

	onAllComplete: function(current) {
		this.log('Completed all files, ' + current.bytesTotal + ' bytes.', arguments);
		this.status.removeClass('file-uploading');
		this.progressBar.start(100);
	},

	onError: function(file, error, info) {
		this.log('Upload "' + file.name + '" failed. "{1}": "{2}".', arguments);
		this.status.removeClass('file-uploading');
		this.progressBar.cancel();
		var fn = this.options.fileError;
		if(fn) fn.call(this, this.finishFile(file), error, info);
	},

	onCancel: function() {
		this.log('Filebrowser cancelled.', arguments);
	},

	browse: function(fileList) {
		var ret = this.parent(fileList);
		if(ret === true){
			this.log('Browse started.');
			this.status.addClass('file-browsing');
		} else {
			if(ret) this.log('An error occured: ' + ret)
			else this.log('Browse in progress.');
		}
	},

	upload: function(options) {
		var ret = this.parent(options);
		if(ret === true) {
			this.log('Upload started.');
			this.status.addClass('file-uploading');
			this.progressBar.set(0);
		} else {
			if(ret) this.log('An error occured: ' + ret)
			else this.log('Upload in progress or nothing to upload.');
		}
	},

	removeFile: function(file) {
		if(!file) {
			this.files.empty();
		} else {
			if(!file.element) file = this.getFile(file);
			this.files.erase(file);
		}
		this.parent(file);
	},

	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;
	},

	finishFile: function(file) {
		file = this.getFile(file);
		file.finished = true;

		return file;
	},

	log: function(text, args) {
		if(this.options.debug && window.console) console.log(text.substitute(args || {}));
	}
});

