/**
 * FancyUpload - Flash meets Ajax for powerful and elegant uploads.
 *
 * @version		2.1
 *
 * @license		MIT License
 *
 * @author		Harald Kirschner <mail [at] digitarald [dot] de>
 * @copyright	Authors
 */

var FancyUpload2 = new Class({

	Extends: Swiff.Uploader,

	options: {
		limitSize: false,
		limitFiles: 99,
		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, list, options) {
		this.status = $(status);
		this.list = $(list);

		this.files = [];

		if (options.callBacks) {
			this.addEvents(options.callBacks);
			options.callBacks = null;
		}

		this.parent(options);
		this.render();
	},

	render: function() {
		this.overallTitle = this.status.getElement('.overall-title');
		this.currentTitle = this.status.getElement('.current-title');
		this.currentText = this.status.getElement('.current-text');

		var progress = this.status.getElement('.overall-progress');
		this.overallProgress = new Fx.ProgressBar(progress, {
			text: new Element('span', {'class': 'progress-text'}).inject(progress, 'after')
		});
		progress = this.status.getElement('.current-progress')
		this.currentProgress = new Fx.ProgressBar(progress, {
			text: new Element('span', {'class': 'progress-text'}).inject(progress, 'after')
		});
	},

	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);
		file = this.getFile(file);
		file.element.addClass('file-uploading');
		this.currentProgress.cancel().set(0);
		this.currentTitle.set('html', 'File Progress "{name}"'.substitute(file) );
	},

	onProgress: function(file, current, overall) {
		this.overallProgress.start(overall.bytesLoaded, overall.bytesTotal);
		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)
		}));
		this.currentProgress.start(current.bytesLoaded, current.bytesTotal);
	},

	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) {
			var fn = this.options.fileInvalid;
			if (fn) fn.call(this, file, errors);
			return false;
		}
		(this.options.fileCreate || this.fileCreate).call(this, file);
		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.updateOverall(current.bytesTotal);
		this.status.removeClass('status-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.currentText.set('html', 'Upload complete!');
		this.currentProgress.start(100);
		(this.options.fileComplete || this.fileComplete).call(this, this.finishFile(file), response);
	},

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

	onCancel: function() {
		this.log('Filebrowser cancelled.', arguments);
		this.status.removeClass('file-browsing');
	},

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

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

	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.');
			this.status.addClass('file-uploading');
			this.overallProgress.set(0);
		}
	},

	removeFile: function(file) {
		if (!$('upload-list').hasChild('li')) $('no-image').removeClass('hide');
		var remove = this.options.fileRemove || this.fileRemove;
		if (!file) {
			this.files.each(remove, this);
			this.files.empty();
			this.updateOverall(0);
		} else {
			if (!file.element) file = this.getFile(file);
			this.files.erase(file);
			remove.call(this, file);
			this.updateOverall(this.bytesTotal - file.size);
		}
		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;
	},

	countFiles: function() {
		var ret = 0;
		for (var i = 0, j = this.files.length; i < j; i++) {
			if (!this.files[i].finished) ret++;
		}
		return ret;
	},

	updateOverall: function(bytesTotal) {
		this.bytesTotal = bytesTotal;
		this.overallTitle.set('html', 'Overall Progress (' + this.sizeToKB(bytesTotal) + ')');
	},

	finishFile: function(file) {
		file = this.getFile(file);
		file.element.removeClass('file-uploading');
		file.element.setStyle('background-image', 'none');
		var thumb = new Element('img', {
			'src': 'http://www.ezyprints.co.nz/image.php?w=100&u=temp_images/ajax/'+file.name,
			'width': '35',
			'style': 'position: absolute'
		});
		thumb.inject(file.photoname, 'after');
		file.finished = true;
		return file;
	},

	fileCreate: function(file) {
		$('no-image').addClass('hide');
		file.photoname = new Element('input', {
			'name':'file_photoname['+file.name+']',
			'type':'hidden',
			'value': file.name
		});

		var firstsizes = $('size_select_all').clone();
		firstsizes.set('name', 'file_photosizes['+file.name+'][]');
		firstsizes.set('value', $('size_select_all').value);
		firstsizes.set('id', 'file-size-hidden-' + currentCountValue);
		file.photohiddensizes = new Array(firstsizes);

		var firstquantity = $('qty_select_all').clone();
		firstquantity.set('name', 'file_photoquantity['+file.name+'][]');
		file.photoquantity = new Array(firstquantity);
		
		var firstfinish = $('finish_select').clone();
		firstfinish.set('name', 'file_photofinish['+file.name+'][]');
		firstfinish.set('value', $('finish_select').value);
		firstfinish.set('id', 'file-size-finish-' + currentCountValue);
		file.photofinishs = new Array(firstfinish);
		
		var firstsizelink = new Element('a',{
			'class': 'file-size-display',
			'id' : 'file-size-display-' + currentCountValue,
			'rel' : currentCountValue,
			'html': " " + $('setSizeMain').innerHTML + " ",
			'href': '#',
			'events': {
				'click': function() {
					setPhotoSize('file-size-display-' + this.rel,
						 'file-size-hidden-' + this.rel, 
						 'file-size-finish-' + this.rel
					);
					return false;
				}
			}
		});
		file.photosizes = new Array(firstsizelink);
		currentCountValue++;

		file.addsize = new Element('a', {
			'class': 'file-size-add',
			'href': '#',
			'html': '[ + ] Extra Size',
			'events': {
				'click': function() {
					var photosize = new Element('a',{
						'class': 'file-size-display',
						'id' : 'file-size-display-' + currentCountValue,
						'rel' : currentCountValue,
						'html': " " + $('setSizeMain').innerHTML + " ",
						'href': '#',
						'events': {
							'click': function() {
								setPhotoSize('file-size-display-' + this.rel,
									 'file-size-hidden-' + this.rel, 
									 'file-size-finish-' + this.rel
								);
								return false;
							}
						}
			
					});//var photosize = firstsizes.clone();
					// hidden photo size element
					var photohiddensize = new Element('input',{
						'type': 'hidden',
						'id' : 'file-size-hidden-' + currentCountValue,
						'name': 'file_photosizes['+file.name+'][]',
						'value': $('size_select_all').get('value')			});
					// hidden photo finish element
					var photohiddenfinish = new Element('input',{
						'type': 'hidden',
						'id' : 'file-size-finish-' + currentCountValue,
						'name': 'file_photofinish['+file.name+'][]',
						'value': $('finish_select').get('value')			});
					var photoqty = firstquantity.clone();
					currentCountValue++;
					var removesize = new Element('a', {
						'class': 'file-size-remove',
						'html': '[ - ]',
						'href': '#',
						'events': {
							'click': function() {
								//photosizes.destroy();
								//photoqty.destroy();
								//this.destroy();
								this.getParent().destroy();
								return false;
							}
						}
					});

					//photosizes.inject(file.addsize, 'before');
					//photoqty.inject(file.addsize, 'before');
					//removesize.inject(file.addsize, 'before');
					var a_size = new Element('div', {'class': 'a-size'}).adopt(
						photoqty,
						photosize,
						photohiddensize,
						photohiddenfinish,
						removesize
					);
					a_size.inject(file.addsize, 'before');
					file.photoquantity.push(photoqty);
					file.photosizes.push(photosize);
					file.photohiddensizes.push(photohiddensize);
					file.photofinishs.push(photohiddenfinish);
					
					return false;
				}
			}
		});

		file.a_size = new Element('div', {'class': 'a-size'}).adopt(
			file.photoquantity,
			file.photosizes,
			file.photohiddensizes,
			file.photofinishs
		);

		file.info = new Element('span', {'class': 'file-info'});
		file.element = new Element('li', {'class': 'file'}).adopt(
			new Element('span', {'class': 'file-size', 'html': this.sizeToKB(file.size)}),
			new Element('a', {
				'class': 'file-remove',
				'href': '#',
				'html': 'Remove',
				'events': {
					'click': function() {
						this.removeFile(file);
						return false;
					}.bind(this)
				}
			}),
			file.origname,
			file.photoname,
			new Element('span', {'class': 'file-name', 'html': file.name}),
			file.info,
			file.a_size,
			file.addsize
		).inject(this.list);
	},

	fileComplete: function(file, response) {
		this.options.processResponse || this
		var json = $H(JSON.decode(response, true));
		if (json.get('result') == 'success') {
			file.element.addClass('file-success');
			file.info.set('html', json.get('size'));
			file.photoname.set('name', 'file_photoname['+json.get('name')+']');
			file.photohiddensizes.each(function (el) {el.set('name', 'file_photosizes['+json.get('name')+'][]');});
			// file.photosizes.set('name', 'file_photosizes['+json.get('name')+'][]');
			file.photoquantity.each(function (el) {el.set('name', 'file_photoquantity['+json.get('name')+'][]');});
			file.photofinishs.each(function (el) {el.set('name', 'file_photofinish['+json.get('name')+'][]');});
			//file.photoquantity.set('name', 'file_photoquantity['+json.get('name')+'][]');
		} else {
			file.element.addClass('file-failed');
			file.info.set('html', json.get('error') || response);
		}
	},

	fileError: function(file, error, info) {
		file.element.addClass('file-failed');
		file.info.set('html', '<strong>' + error + '</strong><br />' + info);
	},

	fileRemove: function(file) {
		file.element.fade('out').retrieve('tween').chain(Element.destroy.bind(Element, file.element));
	},

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

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

});

/**
 * @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'
};
