var config = {

	// misc
	maxFileSize			: 4 * 1024 * 1024,		// max allowed imagesize (4MB)
	maxHeight			: 120,					// size of thumbnails in px
	maxWidth			: 120,

	// blocks
	uploadForm			: $('#uploadForm'),
	updateHint			: $('#funcbar .updateHint'),
	oldUpload			: $('#oldUpload'),
	queue				: $('#queue'),

	// buttons
	btnUpload			: $('#btnUpload'),

	// thumbnails
	thumbnailTemplate	:	'<div class="item">'+
								'<div class="imgwrapp"></div>'+
								'<strong>{filename}</strong>'+
								'<span class="remove" title="remove from list">remove from list</span>'+
							'</div>',
	thumbnailSelector	: '.item',		// must reflect thumbTemplate
	thumbnailImgWrapper	: '.imgwrapp',	// must reflect thumbTemplate

	// infobox
	infoboxTemplate		:	'<p id="infoBox">'+
								'<strong class="h">{filename}</strong><br>'+
								'<strong>size:</strong> {filesizeTxt}<br>'+
								'<strong>type:</strong> {filetype}<br>'+
								'<strong>resolution:</strong> {width} x {height}'+
							'</p>',
	infoboxSelector		: '#infoBox',		// must reflect infoBoxTemplate

	// flashmessage
	flashTemplate		:	'<div class="flashmessage {type}">{text}</div>',
	flashSelector		: '.flashmessage' // must reflect flashTemplate
};




var html5Uploader = function() {

	var	configuration	= null,
		workaround		= false,
		timer			= null,
		xhr				= new XMLHttpRequest();


/* ********************* *
 *        UTILITY        *
 * ********************* */

	function checkBrowser()
	{
		if (!('FileReader' in window)) {
			return false;
		}

		if (!('FormData' in window)) {
			workaround = true;
		}

		return true;
	}


	function init(conf)
	{
		configuration = conf;

		// HTML stuff
		configuration.updateHint.after('<span id="btnClrqueue" class="button" style="display:none">Clear queue</span>').remove();
		configuration.btnClrqueue = $('#btnClrqueue');
		configuration.btnClrqueue.after(configuration.btnUpload);
		configuration.btnUpload.addClass('button').hide();
		configuration.oldUpload.remove();
		configuration.btnUpload.after('<div id="progressWrapper" style="display:none"><div class="progress single"><div class="bar"></div></div><div class="progress total"><div class="bar"></div></div></div>');
		configuration.progressSingle = $('#progressWrapper .single .bar');
		configuration.progressTotal = $('#progressWrapper .total .bar');
		configuration.queue.addClass('init');

		// events stuff
		configuration.queue[0].ondragenter = function(e) {return false};
		configuration.queue[0].ondragover  = function(e) {return false};
		configuration.queue[0].ondragover  = function(e) {return false};
		configuration.queue[0].ondragleave = function(e) {return false};
		configuration.queue[0].ondrop      = function(e) {
			e.stopPropagation();
			e.preventDefault();
			listFiles(e.dataTransfer.files);
		};

		configuration.btnClrqueue.click(clearQueue);
		configuration.btnUpload.click(upload);
		configuration.queue.find('img').live('hover', infobox);
		configuration.queue.find('img').live('mousemove', infoboxMove);
		configuration.queue.find(configuration.thumbnailSelector + ' .remove').live('click', remove);
		$(configuration.flashSelector + '.sticky').live('click', removeStickyFlashmsg);
		configuration.queue.find('img').live('click', colorbox);
	}


	function generateBoundary()
	{
		return "-----------------------------9" + (new Date).getTime();
	}


	function getMultipartFd(file, boundary)
	{
		var	rn		= '\r\n',
			body	= '';

		body  = '--' + boundary + rn;
		body += 'Content-Disposition: form-data; name="Upload[file]"; filename="'+file.name+'"'+ rn;
		body += 'Content-Type: ' + file.type + rn + rn;
		body += file.getAsBinary() + rn;
		body += '--' + boundary + rn;
		body += '--' + boundary + '--' + rn;

		return body;
	}


	function resize(img, maxHeight, maxWidth)
	{
		var	height		= img.naturalHeight,
			width		= img.naturalWidth,
			newWidth	= null,
			newHeight	= null;

		if ((height / maxHeight) >= (width / maxWidth)) {
			newHeight = (height > maxHeight) ? maxHeight : height;
			newWidth = parseInt((newHeight * width) / height);
		}
		else {
			newWidth = (width > maxWidth) ? maxWidth : width;
			newHeight = parseInt((newWidth * height) / width);
		}

		img.height = newHeight;
		img.width = newWidth;
	}


	function readFile(file, callback)
	{
		var	reader = new FileReader();

		reader.onload = function(e){
			callback(file, e.target.result);
		}

		reader.readAsDataURL(file);
	}


/* ********************* *
 *    EVENT HANDLERS     *
 * ********************* */

	function listFiles(files)
	{
		var	file		= null,
			stack		= [], // counter for async code
			$queue		= configuration.queue,
			skip		= false,
			$inQueue	= $queue.find('img');

		var	len = files.length,
			numOfImages = 0;

		for (var i = 0; i < len; i++) {
			file = files[i];
			if (file.type.toLowerCase().match(/image.*/)) {
				numOfImages++;
			}
		}

		for (var i = 0; i < len; i++) {

			file = files[i];
			skip = false;

			if (file.type.toLowerCase().match(/image.*/)) { // only images allowed

				$inQueue.each(function(i, imgInQueue){
					if (
						file.name === imgInQueue.file.name &&
						file.size === imgInQueue.file.size &&
						file.type === imgInQueue.file.type
					) {
						skip = true;
					}
				});

				if (skip) {
					$.flashmessage('One or more images skipped! Already in queue.', 'info', 2500);
					continue;
				}

				if (file.size > configuration.maxFileSize) {
					$.flashmessage('One or more images exceeded maximum allowed file size ('+ (configuration.maxFileSize/1024/1024).toFixed(0) +'MB)', 'info', 3000);
					continue;
				}



				// read Image content
				readFile(file, function(file, content) {
					var img = document.createElement('img');
					img.file = file;
					img.src = content;
					img.onload = function() {
						resize(this, configuration.maxHeight, configuration.maxWidth);

						// create HTML markup of thumbnail
						$queue.append(configuration.thumbnailTemplate.supplant({
							filename : this.file.name
						}));

						// insert img element into newly created thumbnail
						$queue.find(configuration.thumbnailSelector)
							.last()
							.children(configuration.thumbnailImgWrapper)
							.append(this);

						// show controls
						stack.push('');
						if (stack.length == numOfImages) { // run only after last image
							configuration.btnUpload.fadeIn();
							configuration.btnClrqueue.fadeIn();
							configuration.progressTotal.parent().parent().fadeIn();
						}
					};
				});
			}
		}
	}


	function clearQueue()
	{
		$imgs = configuration.queue.children(configuration.thumbnailSelector);
		if ($imgs.length) {
			$imgs.each(function(){$(this).hide(500, function(){$(this).remove();});});
		}

		configuration.btnClrqueue.fadeOut();
		configuration.btnUpload.fadeOut();
		configuration.progressTotal.parent().parent().fadeOut();
	}


	function upload()
	{
		var	url				= configuration.uploadForm.attr('action'),
			totalSize		= 0,
			totalUploaded	= 0,
			currentFilesize = 0,
			currentUploaded	= 0,
			uploadQueue		= [],
			$imgs			= configuration.queue.find('img'),
			boundary		= generateBoundary();

		if ($imgs.length) { // any images to upload?

			$imgs.each(function(){
				// prepare upload data
				var file       = this.file;
					totalSize += file.size;

				if (!workaround) {
					var fd = new FormData;
					fd.append('Upload[file]', file);
					uploadQueue.push(fd);
				} else {
					uploadQueue.push(getMultipartFd(file, boundary));
				}
			});

			xhr.upload.onerror = function(e){};
			xhr.upload.onabort = function(e){};
			xhr.upload.onloadstart = function(e){
				currentUploaded = 0;
				configuration.progressSingle.css('width', '0%');
				$cuttentItem = configuration.queue.children(configuration.thumbnailSelector).first();
				$cuttentItem.append('<div class="processCover"></div>');
			};
			xhr.upload.onprogress  = function(e){
				if (e.lengthComputable) {

					if (currentFilesize == 0) {
						currentFilesize = e.total;
					}

					totalUploaded += e.loaded - currentUploaded;
					currentUploaded = e.loaded;
					configuration.progressSingle.css('width', (e.loaded/e.total)*100 + '%');
					configuration.progressTotal.css('width', (totalUploaded/totalSize)*100 + '%');
				}
			};

			(function upload() {
				xhr.upload.onload = function(e){ // upload complete
					totalUploaded += currentFilesize - currentUploaded;
					currentUploaded = currentFilesize;
					currentFilesize = 0;
					configuration.progressSingle.css('width', '100%');
					configuration.progressTotal.css('width', (totalUploaded/totalSize)*100 + '%');

					configuration.queue.find('.processCover').parent().hide(500, function() {
						$(this).remove()

						if (uploadQueue.length) { // any queued uploads?
							upload();
						} else {
							// all uploads finished
							configuration.progressTotal.css('width', '100%');
							configuration.btnClrqueue.fadeOut(1000);
							configuration.btnUpload.fadeOut(1000);
							configuration.progressTotal.parent().parent().fadeOut(1000, function() {
								configuration.progressSingle.css('width', 0);
								configuration.progressTotal.css('width', 0);
							});

							$.flashmessage('All files uploaded successfully', 'info sticky', 3500);
							return false;
						}
					});
				};

				xhr.open('POST', url, true);

				if (!workaround) {
					xhr.send(uploadQueue.shift());
				} else {
					xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
					xhr.sendAsBinary(uploadQueue.shift());
				}
			})();

			return false;

		} else { // if ($thumbs.length)
			$.flashmessage('Nothing to upload!', 'error', 2500);
			return false;
		}
	}


	function infobox(e)
	{
		if (e.type == 'mouseover') {
			var img = $(this)[0];

			$('body').append(configuration.infoboxTemplate.supplant({
				filename    : img.file.name,
				filesize    : img.file.size,
				filesizeTxt : (img.file.size/1024).toFixed(2) + 'kB',
				filetype    : img.file.type,
				width       : img.naturalWidth,
				height      : img.naturalHeight
			}));

			$(configuration.infoboxSelector).css({position:'absolute', top:e.pageY-15, left:e.pageX+15}).fadeIn();
		}

		if (e.type == 'mouseout') {
			$(configuration.infoboxSelector).remove();
		}
	}


	function infoboxMove(e)
	{
		$(configuration.infoboxSelector).css({top:e.pageY-15, left:e.pageX+15});
	}


	function remove()
	{
		$(this).parent(configuration.thumbnailSelector).hide(400, function() {
			$(this).remove();
		});

		// hide controls after removing last image
		if (configuration.queue.find(configuration.thumbnailSelector).length == 1) {
			configuration.btnClrqueue.fadeOut();
			configuration.btnUpload.fadeOut();
			configuration.progressTotal.parent().parent().fadeOut();
		}
	}


	function removeStickyFlashmsg()
	{
		$(this).fadeOut(400, function(){ $(this).remove(); });
		$('#overlay').fadeOut(400, function(){ $(this).remove(); });
	}


	function colorbox()
	{
		var	$this			= $(this),
			resizedWidth	= $this.attr('width'),
			resizedHeight	= $this.attr('height');

		$(configuration.infoboxSelector).remove();

		$this.removeAttr('height');
		$this.removeAttr('width');

		$.fn.colorbox({
			inline      : false,
			href        : $this.attr('src'),
			maxWidth    : '95%',
			maxHeight   : '95%',
			photo       : true,
			onOpen      : function(){
				$this.addClass('hidn');
				$this.hide();
			},
			onClosed    : function(){
				$img = configuration.queue.find('img.hidn');
				$img.removeClass('hidn');
				$img.attr('width', resizedWidth);
				$img.attr('height', resizedHeight);
				$img.show();
			}
		});

	}


/* ********************* *
 *          PUB          *
 * ********************* */

	return {
		checkBrowser : checkBrowser,
		init : init
	}

}();



String.prototype.supplant = function(o) {
	return this.replace( /{([^{}]*)}/g, function(a, b) {
			var r = o[b];
			if (typeof r === 'string' || typeof r === 'number')
				return r;
			else
				return a;
		}
	);
}

$.flashmessage = function(text, type, delay){
	if (!$(config.flashSelector).length) { // is there any flashmessage in the DOM?
		delay = (typeof(delay) === 'number') ? delay : 2000;

		$flashbox = $(config.flashTemplate.supplant({text:text, type:type}));
		$flashbox
			.appendTo($('body'))
			.css({
				position : 'absolute',
				left     : ($(window).width()  - $flashbox.width())  / 2 + $(window).scrollLeft() + "px",
				top      : ($(window).height() - $flashbox.height()) / 2 + $(window).scrollTop()  + "px"
			});

		if (-1 === type.indexOf('sticky')) {
			$flashbox.fadeIn().delay(delay).fadeOut(400, function(){ $(this).remove(); });
		} else {
			$('body').append('<div id="overlay"></div>');
			$('#overlay').fadeTo(200, 0.66, function(){
				$(this).after($flashbox);
				$flashbox.fadeIn();
			});
		}
	}
}



// RUN
$(function(){

	if (html5Uploader.checkBrowser()) {
		html5Uploader.init(config);
	}

});

