// upload stream library - handles a streaming file upload (to the server script
// upload_stream.cgi)
// Note: requires prototype.js along with prototype's ajax.js (not included by default in our version of prototype)
function UploadStream( args ) {
	Object.extend(this, args)
    if (! this.encdata) this.encdata = '';
	if (! this.element) alert('upload_stream: missing required argument: element object:' + Object.inspect($H(this)));
	if (! this.context) this.context = 'catalog';
	if (! this.encode_filenames) this.encode_filenames = 0; 
	this.name = this.element.name
	this.prefix = 'upload_progress_' + this.name + '_';
	this.error_count = 0;
	this.timeout_count = 0
	this.timeout = 300;
	this.max_errors = 15;
	this.finished = 0;
	this.cancelled = 0;
	this.counter = 0;
	this.secs_elapsed = 0;
	this.up_amounts = new Array();
}

UploadStream.prototype = {

initialize : function (name, final_callback) {
},
create_form : function() {
	this.rand = Math.round(1000000 * Math.random()); //A random number to identify this upload
	formwrapper = 	document.createElement('div');
	document.body.appendChild(formwrapper);
	var catname = this.catalog.replace("/","");
	formwrapper.innerHTML ='<form style="display:none" id="' + this.prefix + this.rand + 'upload_form"  name="upload_form" method="POST" action="/~interch/upload_stream.cgi?_z_lb_route='+catname +'" target="upload_result" enctype="multipart/form-data" > \
<input type="hidden" id="'+ this.prefix + 'rand" name="rand" value="' + this.rand + '" /> \
<input type="hidden" name="base" value="upload" /> \
<input type="hidden" name="catalog" value="' + this.catalog + '" /> \
<input type="hidden" name="encdata" value="' + this.encdata + '" /> \
<input type="hidden" name="context" value="' + this.context + '" /> \
<input type="hidden" name="encode_filenames" value="' + this.encode_filenames + '" /> \
<input type="submit" value="GO"/> \
</form> \
<iframe id="upload_result" src="/~interch/blank.html" name="upload_result" width="0" height="0" style="border:none;"></iframe> \
';

	form = $( this.prefix + this.rand + 'upload_form' );
	filename = this.element;
	this.filename = this.element.value;
	loadingObj = document.createElement('span');
	loadingObj.id = this.prefix + "loading_label";
	if (! this.no_throbber ) {
		loadingObj.innerHTML = '<img style="vertical-align:middle; margin: 5px;" src="/interchange-5/en_US/throbber.gif" class="throbber"><em>Uploading...</em>';
	} else {
		loadingObj.innerHTML = '&nbsp;';
		loadingObj.style.display = 'none';
	}
	this.loading = loadingObj;
	filename.parentNode.insertBefore(loadingObj, filename);
	this.uploader = filename;	
	this.uploader_parent = filename.parentNode;

	path_wrap = document.createElement('span');
	path_wrap.id = this.prefix + "path";
	path_wrap.innerHTML = '<input type="hidden" name="mv_data_file_path" value="' + this.path +  '" />';
	form.appendChild(filename);
	form.appendChild(path_wrap);
	this.form = form;
},

reset: function( restore ) {
	var self = this;
	window.setTimeout( function() {
		if ( restore ) self.loading.parentNode.insertBefore(self.element, self.loading);
		self.loading.parentNode.removeChild(self.loading);
		self.form.parentNode.removeChild(self.form);
		self.finished = 0;
		self.cancelled = 0;
		this.error_count = 0;
		this.timeout_count = 0;
		this.secs_elapsed = 0;
		this.up_amounts = new Array();
		if (self.reset_callback) self.reset_callback();
	}, 1100);

},
upload : function() {
	this.create_form();
	this.form.submit();
	var self = this;
	window.setTimeout(function() {
		self.poll_status();
		},1000);

	// start the timer
	window.setInterval( function() { self.secs_elapsed++ }, '1000' );
	if (this.upload_begin_callback) this.upload_begin_callback();
},
cancel : function() {
    if (window.stop) {
		window.stop();
	}
	else {  //Internet explorer has a proprietary way of stopping window.stop()
	  document.execCommand("Stop")
	 }
},

handle_error: function (req) {
	this.error_count ++;
	if ( this.error_count > this.max_errors ) {
		this.cancelled = 1;
		if (this.error_callback) this.error_callback();
	}
	var self = this;
	window.setTimeout(function() {
		self.poll_status();
		},1000);
},
//Return the filename from a path
basename: function(path) {
        fl = path.split(/[\/\\]/);
        return fl[fl.length-1];
},

poll_status : function(req) {
	var self = this;

	if (req) {
		var result = req.responseText.split("\n");
		if (result[0] == result[1]) {
			this.finished = 1;
			if (this.final_callback) this.final_callback(result[0], result[1], this.basename(this.filename));

		}
		else {
			
			if (result[0] == this.last_received) {
				this.timeout_count ++;
				if (this.timeout_count > this.timeout) {
					this.cancelled = 1;
					if (this.error_callback) this.error_callback();
				}
			}
			else {
				this.timeout_count = 0;
			}
			this.last_received = result[0];

			// calculate speed off the last 'n' retrievals
			var calc_length = 45;
			var to_data = new Array( result[0], this.secs_elapsed );
			this.up_amounts.push( to_data );
			var from_data = this.up_amounts[ this.up_amounts.length >= calc_length ? this.up_amounts.length - calc_length : 0 ];
			var secs_diff = to_data[1] - from_data[1];
			var bytes_diff = to_data[0] - from_data[0];
			var bps = bytes_diff / secs_diff;
			if ( isNaN( bps ) ) bps = 0;

			// call any callback
			if (this.update_callback) this.update_callback( result[0], result[1], bps );
		}
	}
	//Do an ajax request to get the status
	if (! this.finished && ! this.cancelled ) {	
		window.setTimeout(function() {
			var catname = self.catalog.replace("/","");
			var url = '/~interch/upload_stream_status.cgi?catalog='+ self.catalog + '&_z_lb_route=' + catname + '&rand=' + self.rand + '&nc=' + self.counter++; // the random number is to stop anything from caching
			var myAjax = new Ajax.Request(
				url, 
				{
					method: 'get', 
					onSuccess: function(req) {self.poll_status(req); },
					onFailure: function(req) {self.handle_error(req); }
			});
		}, 1000);	
	}
}

};

// UploadHelper object - methods to tie in with UploadStream & centralise a lot of 
// the surrounding interface
if ( !window.uploadQueue ) window.uploadQueue = {};
if ( !window.uploadOptions ) window.uploadOptions = {};
function UploadHelper() {
	this.init = 1;
	this.secsElapsed = {};
}
UploadHelper.prototype = {

// helper function to calculate the speed of the upload
// based off the average of last 'n' polled updates
get_display_data : function( r, t, bps ) {

	// calculate time remaining
	var kb_done = Math.round( r / 1024 );
	var kb_total = Math.round( t / 1024 );
	var done = kb_done > 1024 ? ( Math.round( ( kb_done / 1024 ) * 100 ) / 100 ) + ' Mb' : kb_done + ' Kb';
	var total = kb_total > 1024 ? ( Math.round( ( kb_total / 1024 ) * 100 ) / 100 ) + ' Mb' : kb_total + ' Kb';
	var pct = Math.round( ( r / t ) * 100 ) + '%';
	var kbps = Math.round( ( bps / 1024 ) * 100 ) / 100; // speed
	kbps += ' k/sec';

	// calculate time remaining
	var bytes_remaining = t - r;
	var secs_remaining = Math.round( bytes_remaining / bps ); // time remaining
	if ( isNaN( secs_remaining ) || secs_remaining < 0 ) secs_remaining = 0;
	var time_remaining;
	if ( secs_remaining > 60 ) {
		var mins_remaining = Math.round( secs_remaining / 60 );
		time_remaining = mins_remaining + ' minutes remaining';
	} else {
		time_remaining = secs_remaining + ' seconds remaining';
	}
	return {
		'done': done,
		'total': total,
		'pct': pct,
		'kbps': kbps,
		'time_remaining': time_remaining
	};
},

// uploads a single file. Integrates with uploadFiles and uploadNextFile to
// upload an array of files, chaining them one after the other
uploadSingleFile: function( file_el, options, index ) {
	var self = this;
	if ( !file_el.value.length ) {
		self.uploadNextFile( index ); // ignore empty uploads
		return;
	}
	var form = file_el.form;
	var cell = file_el.parentNode;
	var uploadWrapper = document.createElement( 'div' );
	cell.appendChild( uploadWrapper );
			
	// filename upload notification
	var fileNotify = document.createElement( 'div' );
	Object.extend( fileNotify.style, {
	} );
	var fileParts = file_el.value.split(/[\/\\]/);
	var fileName = fileParts[fileParts.length-1];
	fileNotify.innerHTML = 'Uploading file: ' + fileName + '...';
	uploadWrapper.appendChild( fileNotify );

	// create status bar
	var status = document.createElement( 'div' );
	status.className = 'progress_bar_waiting';
	Object.extend( status.style, {
		width: '200px'
	} );
	var statusInner = document.createElement( 'div' );
	statusInner.className = 'progress_bar_done';
	status.appendChild( statusInner );
	uploadWrapper.appendChild( status );

	// minutes remaining / speed
	var minsLeft = document.createElement( 'div' );
	minsLeft.className = 'popup_time_remaining';
	minsLeft.innerHTML = 'Calculating time remaining ...';
	uploadWrapper.appendChild( minsLeft );

	// prepare upload stream to upload file & maintain progress bar
	var uploadOptions = {
		no_throbber: 1,
		element: file_el,
		encode_filenames: 0
	};
	Object.extend( uploadOptions, options );
	var file_path = file_el.getAttribute( 'upload_path' );
	if ( !file_path && $( file_el.id + '_path' ) ) file_path = $( file_el.id + '_path' ).value; 
	if ( !file_path ) file_path = options.path || 'upload';
	Object.extend( uploadOptions, {
		path: file_path,
		upload_begin_callback: function() {
			//window.uploadCounters[countIndex] ++;
			statusInner.style.width = '0%';
			if ( options.upload_begin_callback ) options.upload_begin_callback();
		},
		update_callback: function( received, total, bps ) {

			// update status bar
			var pc = 100*received/total;
			statusInner.style.width = pc+'%';

			// update time remaining / speed
			var format_data = self.get_display_data( received, total, bps );
			minsLeft.innerHTML = format_data.time_remaining == 'Infinity minutes remaining' ? 'Calculating time remaining ...' : 'About ' + format_data.time_remaining + ' (' + format_data.done + ' / ' + format_data.total + ' at ' + format_data.kbps + ')';
			if ( options.update_callback ) options.update_callback( received, total );
		},
		final_callback : function( result1, result2, filename ) {
			form.appendChild( Object.extend( document.createElement( 'input' ), {
				type: 'hidden',
				value: filename,
				name: file_el.name
			} ) );
			cell.removeChild( uploadWrapper);
			cell.appendChild( Object.extend( document.createElement( 'div' ), {
				innerHTML: 'Uploaded: ' + filename.substr( 0, 30 ) + ( filename.length > 30 ? ' ... ' : '' )
			} ) );
			this.reset( 0 ); // reset but dont restore element
			self.uploadNextFile( index );
		},
		error_callback : function() {
			alert( 'Error uploading file' );
			if ( options.error_callback ) options.error_callback();
		}
	} );
	var stream = new UploadStream( uploadOptions );
	stream.upload();
},

// method to be used as a callback moving through a stack of files to upload
// finds the next acceptable file & uploads it
// if no more files exist for the index, then it calls the final_callback in options
uploadNextFile: function( index ) {
	var self = this;
	var options = window.uploadOptions[index];

	// find the next specified file
	var nextFile;
	while ( window.uploadQueue[index].length ) {
		nextFile = window.uploadQueue[index].shift();
		if ( nextFile && nextFile.value.length ) break;
	}

	// if there are no more files to upload call the callback & return
	if ( !nextFile ) {
		if ( options.final_callback ) options.final_callback();
		return;
	}

	// otherwise, upload the current file
	self.uploadSingleFile( nextFile, options, index );
},

// generic file to upload files.
// receives a file DOM element (or array of file elements) and uploads
// them to the specified path as either 'path' option, the 'upload_path' attribute 
// on the file input, or specified as a hidden form element of the file elements id + '_path'.
// When all files have been uploaded calls the callback specified in options.final_callback.
uploadFiles: function( files, options ) {
	var self = this;
	if ( !options.catalog ) return alert( 'upload_stream.js: Please specify a catalog to upload to' );
	if ( window.location.href.split( ':' ).shift() != 'https' ) return alert( 'upload_stream.js: Please upload files via the secure server' );
	if ( typeof( files ) == 'input' ) files = $A(files);
	if ( files.length ) {
		countIndex = 'counter_' + Math.floor( Math.random() * 1000 );
		//window.uploadCounters[countIndex] = 0;
		window.uploadQueue[countIndex] = files;
		window.uploadOptions[countIndex] = options;

		// loop through and upload each file
		self.uploadNextFile( countIndex );
	}
},

// function to upload all file elements on a form
uploadAllFiles: function( form, options ) {
	var files = $( form ).select( 'input[type=file]' );
	return this.uploadFiles( files, options );
},

// upload specified
handleUploads: function( el, catalog, upload_path, cb ) {
	var self = this;

	// check for file uploads
	var form = el.form;
	var files = $( form ).select( 'input[type=file]' );
	var upload_found = 0;
	for ( var i = 0; i < files.length; i++ ) {
		if ( files[i].value.length ) { upload_found = 1; break; }
	}

	// handle any file uploads
	if ( upload_found ) {
		if ( !el.value.length ) el.value = 'Uploading files...';
		callBack = 'buttonCb' + Math.floor( Math.random() * 1000 );
		self.uploadFiles( files, {
			catalog: catalog,
			path: upload_path,
			final_callback: function() {
				if ( cb ) cb( el );
			}
		} );
	} else {
		if ( cb ) cb( el );
	}
}

};

