function UploadsManager() {

	$(document).ready(
		function() {
			UploadsManager._initAll();
		}
	);
}

UploadsManager = new UploadsManager();

/* ==== Private Attributes ==== */
UploadsManager._nextId = 0;
UploadsManager._uploadersReady = 0;

UploadsManager._debugMode = false;
UploadsManager._Uploaders = {};

/* ==== Private methods ==== */
UploadsManager._nextFlashId = function() {
	this._nextId++;
	return 'uploaderflash' + this._nextId;
}

UploadsManager._initAll = function() {
	this._createHooks();

	for (var i in this._Uploaders) {
		this._Uploaders[i].init();
	}
}

UploadsManager.iterate = function($method, $timeout) {
	var $me = this;
	var args = Array.prototype.slice.call(arguments); // convert to array

	if ($timeout !== undefined) {
		// 2nd parameter is given
		if ((Object.prototype.toString.call($timeout) === '[object String]') && $timeout.match(/^timeout:([\d]+)$/)) {
			// it's string in format "timeout:<number>"
			$timeout = parseInt(RegExp.$1);
		}
		else {
			// this is not the timeout, but 1st parameter of iteratable method
			$timeout = undefined;
		}
	}

	if ($timeout !== undefined) {
		// make delayed iteration (helps with direct innerHTML assignments in IE)
		args.splice(args.length - 1, 1); // remove timeout

		setTimeout(function() { $me.iterate.apply($me, args); }, $timeout);
		return ;
	}

	args.splice(0, 1); // remove method name

	for (var i in this._Uploaders) {
		this._Uploaders[i][$method].apply(this._Uploaders[i], args);
	}
}

UploadsManager._hasQueue = function() {
	var has_queue = false;

	for (var i in this._Uploaders) {
		var tmp = this._Uploaders[i].hasQueue();
		has_queue = has_queue || tmp;
	}

	return has_queue;
}

UploadsManager._getUploader = function (file) {
	var $flash_id = file.id.match(/(.*)_[\d]+/) ? RegExp.$1 : file.id;

	for (var $uploader_index in this._Uploaders) {
		if (this._Uploaders[$uploader_index].flash_id == $flash_id) {
			return this._Uploaders[$uploader_index];
		}
	}

	return null;
}

UploadsManager._createHooks = function () {
	var $me = this;

	$('#' + $form_name).submit(
		function ($e) {
			if ($me._hasQueue()) {
				submitted = false;
				alert('File upload is in progress. Please cancel the upload or wait until it\'s completed.');

				return false;
			}
		}

	);
}

/* ==== Public methods ==== */
UploadsManager.AddUploader = function(id, params) {
	this._Uploaders[id] = new Uploader(id, params);
}

UploadsManager.RemoveUploader = function(id) {
	this._Uploaders[id].remove();
	delete this._Uploaders[id];
}

UploadsManager.DeleteFile = function(uploader_id, fname, confirmed) {
	if (!confirmed && !confirm('Are you sure you want to delete "' + fname + '" file?')) {
		return false;
	}

	var $uploader = this._Uploaders[uploader_id];

	Request.makeRequest(
		$uploader.deleteURL.replace('#FILE#', encodeURIComponent(fname)).replace('#FIELD#', $uploader.params.field),
		false, '',
		function(req, fname, $uploader) {
			$uploader.removeFile({id:fname})
			$uploader.deleted.push(fname);
			$uploader.updateInfo(undefined, true);
		},

		function(req, fname, $uploader) {
			alert('Error while deleting file');
		},
		fname, $uploader
	);

	return true;
}

UploadsManager.StartUpload = function(id) {
	this._Uploaders[id].startUpload();
}

UploadsManager.CancelFile = function(id, file_id) {
	this._Uploaders[id].callFlash('CancelUpload', [file_id]);
}

UploadsManager.UploadQueueComplete = function($uploader) {

}

UploadsManager.CancelUpload = function(id) {
	this._Uploaders[id].cancelUpload();
}

UploadsManager.setDebugMode = function ($enabled) {
	/*for (var $uploader_index in this._Uploaders) {
		this._Uploaders[$uploader_index].clallFlash('SetDebugEnabled', [$enabled]);
	}*/

	this._debugMode = $enabled;
}


/* ==== Flash event handlers ==== */
UploadsManager.onHandleEverything = function () {
	if (UploadsManager._debugMode) {
		console.log('default swf handler');
	}
}

UploadsManager.onUploadStart = function(file) {
	var $uploader = UploadsManager._getUploader(file);

	$uploader.queueEvent(
		function() {
			this.UploadFileStart(file);
		}
	);
}

UploadsManager.onUploadProgress = function(file, bytesLoaded, bytesTotal) {
	var $uploader = UploadsManager._getUploader(file);

	$uploader.queueEvent(
		function() {
			this.UploadProgress(file, bytesLoaded, bytesTotal);
		}
	);
}

UploadsManager.onUploadComplete = function(file) {
	var $uploader = UploadsManager._getUploader(file);

	$uploader.queueEvent(
		function() {
			this.UploadFileComplete(file);
		}
	);
}

UploadsManager.onFileQueued = function(file) {
	var $uploader = UploadsManager._getUploader(file);
//	file = this.unescapeFilePostParams(file);

	$uploader.queueEvent(
		function() {
			if (this.files_count >= this.params.multiple) {
				// new file can exceed allowed file number
				if (this.params.multiple > 1) {
					// it definetly exceed it
					UploadsManager.onFileQueueError(file, -100, this.params.multiple);
					this.callFlash('CancelUpload', [file.id]);
				}
				else {
					// delete file added
					this.files_count++;
					this.files.push(file);

					if (this.files[0].uploaded) {
						UploadsManager.DeleteFile(UploadsManager._getUploader(file).id, this.files[0].name, true);
					}
					else {
						this.callFlash('CancelUpload', [this.files[0].id]);
					}

					this.startUpload();
				}
			}
			else {
				// new file will not exceed allowed file number
				this.files_count++;
				this.files.push(file);

				this.startUpload();
			}

			this.updateInfo(this.files.length - 1);
		}
	)
}

UploadsManager.onUploadSuccess = function(file, serverData, receivedResponse) {
	var $uploader = UploadsManager._getUploader(file);

	$uploader.queueEvent(
		function() {
			this.UploadSuccess(file, serverData, receivedResponse);
		}
	);
}

UploadsManager.onUploadError = function(file, errorCode, message) {
	var $uploader = UploadsManager._getUploader(file);

	$uploader.queueEvent(
		function() {
			this.removeFile(file);

			switch (errorCode) {
				case -200:
					// HTTP Error
					message = parseInt(message); // HTTP Error Code
					switch (message) {
						case 403:
							message = "You don't have permission to upload";
							break;

						case 413:
							message = 'File size exceeds allowed limit';
							break;

						case 500:
							message = 'Write permissions not set on the server, please contact server administrator';
							break;
					}

					if (isNaN(message)) {
						// message is processed
						alert('Error: ' + message + "\n" + 'Occured on file ' + file.name);
						return ;
					}
					break;

				case -280:
					// File Cancelled
					return ;
					break;

				case -290:
					// Upload Stopped
					UploadsManager.UploadQueueComplete(this);
					return ;
					break;
			}

			// all not processed error messages go here
			alert('Error [' + errorCode + ']: ' + message + "\n" + 'Occured on file ' + file.name);
		}
	);
}

UploadsManager.onFileQueueError = function(file, errorCode, message) {
	switch (errorCode) {
		case -100:
			// maximal allowed file count reached
			alert('Error: Files count exceeds allowed limit' + "\n" + 'Occured on file ' + file.name);
			return ;
			break;

		case -110:
			// maximal allowed filesize reached
			alert('Error: File size exceeds allowed limit' + "\n" + 'Occured on file ' + file.name);
			return ;
			break;

		case -130:
			// maximal allowed filesize reached
			alert('Error: File is not an allowed file type.' + "\n" + 'Occured on file ' + file.name);
			return ;
			break;
	}

	// all not processed error messages go here
	alert('Error [' + errorCode + ']: ' + message + "\n" + 'Occured on file ' + file.name);
}

UploadsManager.onFlashReady = function ($uploader_id) {
	this._Uploaders[$uploader_id].onFlashReady();
	this._uploadersReady++;

	if (this._uploadersReady == this._nextId) {
		// all uploaders are ready
		Application.processHooks('m:OnUploadersReady');
	}
}

UploadsManager.onDebug = function (message) {
	if (!UploadsManager._debugMode) {
		return ;
	}

	var exceptionMessage, exceptionValues = [];

	// Check for an exception object and print it nicely
	if (typeof(message) === 'object' && typeof(message.name) === 'string' && typeof(message.message) === 'string') {
		for (var key in message) {
			if (message.hasOwnProperty(key)) {
				exceptionValues.push(key + ': ' + message[key]);
			}
		}
		exceptionMessage = exceptionValues.join("\n") || '';
		exceptionValues = exceptionMessage.split("\n");
		exceptionMessage = 'EXCEPTION: ' + exceptionValues.join("\nEXCEPTION: ");

		console.log(exceptionMessage);
	} else {
		console.log(message);
	}
};

if (!window.console || !console.firebug) {
	// emulate FireBug Console in other browsers to see flash debug messages
	window.console = {};
	window.console.log = function (message) {
		var console, documentForm;

		try {
			console = document.getElementById('SWFUpload_Console');

			if (!console) {
				documentForm = document.createElement('form');
				document.getElementsByTagName('body')[0].appendChild(documentForm);

				console = document.createElement('textarea');
				console.id = 'SWFUpload_Console';
				console.style.fontFamily = 'monospace';
				console.setAttribute('wrap', 'off');
				console.wrap = 'off';
				console.style.overflow = 'auto';
				console.style.width = '700px';
				console.style.height = '350px';
				console.style.margin = '5px';
				documentForm.appendChild(console);
			}

			console.value += message + "\n";

			console.scrollTop = console.scrollHeight - console.clientHeight;
		} catch (ex) {
			alert('Exception: ' + ex.name + ' Message: ' + ex.message);
		}
	};
}