if (Object.isUndefined(s3core))throw 's3core.uploader is part of the s3core libraray. s3core.js must be loaded first.';
s3core.uploader=Class.create
(
	{
		proxyForm:		false,
		proxyLink:		false,
		events:
		[
			'on:load',
			'on:browse',
			'on:select',
			'on:upload',
			'on:waiting',
			'on:complete',
			'on:error'
		],
		config:
		{
			instance:					false,
			container:					false,
			button:						false,
			proxy:						false,
			initialStatusCheckDelay:	2,
			statusCheckDelay:			1,
			listeners:					{}
		},
		cache:
		{
			method:				false,
			action:				false,
			target:				false,
			enctype:			false,
			encoding:			false,
			lastInstanceValue:	''
		},
		initialize: function(config)
		{
			Object.extend(this.config,config);
			this.generateProxyObject();
			this.config.instance=$(this.config.instance);
			this.config.container=$(this.config.container);
			this.config.button=$(this.config.button);
			this.config.instance.observe
			(
				'click',
				function()
				{
					this.fire('on:browse');
					return;
				}.bind(this)
			);
			this.config.button.observe('click',this.upload.bind(this));
			new PeriodicalExecuter
			(
				function(pe)
				{
					if (this.config.instance.getValue()!=this.cache.lastInstanceValue)
					{
						this.fire('on:select',this.config.instance.getValue());
						this.cache.lastInstanceValue=this.config.instance.getValue();
					}
					return;
				}.bind(this),
				0.1
			);
			this.events.each
			(
				function(event)
				{
					document.observe
					(
						event,
						function(firedEvent)
						{
							if (Object.isFunction(this.config.listeners[event]))
							{
								this.config.listeners[event].apply(this,firedEvent.memo.args);
							}
							return;
						}.bind(this)
					);
					return;
				}.bind(this)
			);
			this.fire('on:load');
			return;
		},
		fire: function(event,args)
		{
			if (typeof args!='Array')args=[args];
			document.fire(event,{args:args});
			return;
		},
		generateProxyObject: function()
		{
			this.proxyLink=s3core.random();
			$(this.config.instance).replace
			(
				[
					'<iframe id="'+this.proxyLink+'" name="'+this.proxyLink+'" width="0" height="0" frameborder="0" style="display:none;width:0px;height:0px;" src="javascript:{}"></iframe>',
					'<input type="hidden" id="proxyLinkId_'+this.proxyLink+'"  name="proxyLinkId" value="'+this.proxyLink+'" />',	//This is being sent as empty... WTF!?!?!?!?!?!?!?!?!
					'<input type="file" id="'+this.config.instance+'"  name="'+this.config.instance+'" />'
				].join('')
			);
			this.proxyLink=$(this.proxyLink);
			this.proxyForm=$(this.proxyLink+'_form');
			return;
		},
		upload: function()
		{
			this.fire('on:upload');
			if (this.config.instance.present())
			{
				//NB: IE needs the attribute 'encoding' to be set to send the file correctly.
				
				//Backup
				this.cache.method=		this.config.container.getAttribute('method');
				this.cache.action=		this.config.container.getAttribute('action');
				this.cache.target=		this.config.container.getAttribute('target');
				this.cache.enctype=		this.config.container.getAttribute('enctype');
				this.cache.encoding=	this.config.container.getAttribute('encoding');
				
				//Setup form for proxied upload.
				this.config.container.setAttribute('method',	'post');
				this.config.container.setAttribute('action',	this.config.proxy+'upload/');
				this.config.container.setAttribute('target',	this.proxyLink.identify());
				this.config.container.setAttribute('enctype',	'multipart/form-data');
				this.config.container.setAttribute('encoding',	'multipart/form-data');
				
				//Initiate
				this.initiateUpload();
			}
			return;
		},
		initiateUpload: function()
		{
			this.fire('on:initiate');
			new Ajax.Request
			(
				this.config.proxy+'initiate/',
				{
					parameters:	{proxyLinkId:this.proxyLink.identify()},
					onSuccess:	function(response)
					{
						this.fire('on:initiated');
						//Submit
						this.config.container.submit();
						this.checkUploadStatus.bind(this).delay(this.config.initialStatusCheckDelay);
						//Restore
						this.config.container.setAttribute('method',	this.cache.method);
						this.config.container.setAttribute('action',	this.cache.action);
						this.config.container.setAttribute('target',	this.cache.target);
						this.config.container.setAttribute('enctype',	this.cache.enctype);
						this.config.container.setAttribute('encoding',	this.cache.encoding);
						return;
					}.bind(this),
					onError: function(response)
					{
						this.fire('on:error',{message:'AJAX Error.',responseObject:response})
						return;
					}.bind(this)
				}
			);
			return;
		},
		checkUploadStatus: function()
		{
			this.fire('on:waiting');
			new Ajax.Request
			(
				this.config.proxy+'status/',
				{
					method:		'post',
					parameters:	{proxyLinkId:this.proxyLink.identify()},
					onSuccess:	function(response)
					{
						if (Object.isUndefined(response.responseJSON.status))
						{
							this.fire('on:error',{message:'Missing JSON Response!'});
						}
						else
						{
							switch (response.responseJSON.status)
							{
								//IN PROGRESS
								case 1:
								{
									this.checkUploadStatus.bind(this).delay(this.config.statusCheckDelay);
									break;
								}
								//COMPLETE
								case 2:
								{
									this.fire('on:complete',response.responseJSON);
									break;
								}
								//ERROR
								default:
								{
									this.fire('on:error',response.responseJSON);
									break;
								}
							}
						}
						return;
					}.bind(this),
					onError: function(response)
					{
						this.fire('on:error',{message:'AJAX Error.',responseObject:response})
						return;
					}.bind(this)
				}
			);
			return;
		}
	}
);