/***************************************************************
upload.js - upload files via AJAX and show progressbar

Copyright 2010 - Simon Strandgaard <simon@bee3.com>

This file depends on:
  1. jQuery
  2. misc.js
  3. htmlentities.js
  4. get_html_translation_table.js

***************************************************************/


// our constructor
function UploadController(options) {
	// console.log('UploadController', options);
	
	/*
	a value in the range 0..100
	*/
	this.m_progress = 0;
	
	/*
	array of strings
	*/
	this.m_filenames = [];
	
	/*
	array of strings
	*/
	this.m_fileitem_uids = [];
	
	/*
	array of strings
	*/
	this.m_errors = [];

	/*
	number of times the poll code have run
	*/
	this.m_poll_count = 0;

	
	/*
	index of the filename that the user is currently renaming
	a value of -1 means that no filename is being edited
	a value in the range 0..+inf means that a filename is being edited
	*/
	this.m_edit_index = -1;

	/*
	identifying name for this upload form
	*/
	this.m_name = dictionary_fetch(
		options,
		'name', 
		'unnamed'
	);

	/*
	url for ajax requests
	*/
	this.m_get_progress_url = dictionary_fetch(
		options,
		'get_progress_url', 
		'get_progress.php'
	);

	/*
	url for ajax requests
	*/
	this.m_list_files_url = dictionary_fetch(
		options,
		'list_files_url', 
		'list_files.php'
	);

	/*
	url for ajax requests
	*/
	this.m_delete_file_url = dictionary_fetch(
		options,
		'delete_file_url', 
		'delete_file.php'
	);

	/*
	url for ajax requests
	*/
	this.m_rename_file_url = dictionary_fetch(
		options,
		'rename_file_url', 
		'rename_file.php'
	);

	/*
	url for ajax requests
	*/
	this.m_download_file_url = dictionary_fetch(
		options,
		'download_file_url', 
		'download_file.php'
	);


	/*
	a md5 string, e.g: d4d53f8e3f08e0c49977c64c341bca9f
	that is used with php's APC extension
	*/
	this.m_apc_key = dictionary_fetch(
		options,
		'apc_key', 
		'LONGMD5CHECKSUMHERE'
	);
	this.m_id_progressbar_inner = dictionary_fetch(
		options,
		'id_progressbar_inner', 
		'#upload1_progressbar_inner'
	);
	this.m_id_progressbar_outer = dictionary_fetch(
		options,
		'id_progressbar_outer', 
		'#upload1_progressbar_outer'
	);
	this.m_id_list_of_files = dictionary_fetch(
		options,
		'id_list_of_files', 
		'#upload1_files'
	);
	this.m_id_upload_form = dictionary_fetch(
		options,
		'id_upload_form', 
		'#upload1_form'
	);
	this.m_id_upload_main = dictionary_fetch(
		options,
		'id_upload_main', 
		'#upload1_main'
	);
}

// prototype assignment
UploadController.prototype = (function() {
	return {
		constructor: UploadController,
		
		setup: function() {
			// console.log('setup');
			// console.log('setup', this.m_download_file_url);
			
			this.refreshFilenames();
			// return this;
			var thisObj = this;
			
			var selector_file = this.m_id_upload_form + ' .file';
			var selector_submit = this.m_id_upload_form + ' .submit_button';
			$(selector_submit).click(function(event) {     
				thisObj.startPolling();
				return true;        
			});
			$(selector_file).change(function() {
				$(selector_submit).click();
			});
			
			
			return this;
		},
		
		toString: function() {
			return '<UploadController, apc_key: ' + this.m_apc_key + ' >';
		},

		displayProgress: function() {
			var progress = this.m_progress;
			$(this.m_id_progressbar_inner).css('width', progress.toString() + '%');
			$(this.m_id_progressbar_inner).html(progress.toString() + '%');
		},

		displayErrors: function() {
			var s = '';
			var ary = this.m_errors;
			for(var i=0;i<ary.length;i++) { s += '<li>' + ary[i] + '</li>'; }
			if(ary.length == 0) {
				s = '<li>error list is empty</li>';
			}
			s = '<h2>Errors</h2><ul>' + s + '</ul>';
		  	$(this.m_id_list_of_files).html(s);
		},
		
		formatRowEdit: function(filename_escaped) {
			var s = '<td class="col1"><input class="filename_value" type="text" value="' + filename_escaped + '" /></td>';
		 	s += '<td class="col2"><input class="cancel_rename_button" type="button" value="Cancel" /></td>';
			s += '<td class="col3"><input class="ok_rename_button" type="submit" value="Ok" /></td>';
			return '<tr>' + s + '</tr>';
		},
		
		formatRowItem: function(filename, tag, fileitem_uid, href) {
			var tooltip0 = 'Download denne fil';            
			var tooltip1 = '&AElig;ndre på navn';
			var tooltip2 = 'Fjern denne fil';
			if(0) {
				tooltip0 += htmlentities(' (File ID: ' + fileitem_uid.toString() + ')', 'ENT_QUOTES');
			}
			title_attr0 = ' title="' + tooltip0 + '"';
			title_attr1 = ' title="' + tooltip1 + '"';
			title_attr2 = ' title="' + tooltip2 + '"';
			
			var s = '<td class="col1">' + filename + '</td>';
			if(1) {
				s = '<td class="col1"><a' + title_attr0 + ' class="download_file ' + tag + '" href="' + href + '">' + filename + '</a></td>';
			}
			s += '<td class="col2"><a' + title_attr1 + ' class="rename_button ' + tag + '">Rediger</a></td>';
			s += '<td class="col3"><a' + title_attr2 + ' class="delete_button ' + tag + '">Slet</a></td>'; 
			return '<tr>' + s + '</tr>';
		},
		
		formatRowEmpty: function() {
			var s = '<td class="col1">Der er ikke vedh&aelig;ftet nogen filer</td>';
			s += '<td class="col2"></td>';
			s += '<td class="col3"></td>'; 
			return '<tr>' + s + '</tr>';
		},
		
		displayFilenames: function() {
			var edit_i = this.m_edit_index;
			var ary = this.m_filenames;
			var ary2 = this.m_fileitem_uids;
			var s = '';
			for(var i=0;i<ary.length;i++) {
				var filename = ary[i];
				var fileitem_uid = (i < ary2.length) ? ary2[i] : 'NO_UID';
				var tag = 'tag_' + i.toString();
				var href = this.m_download_file_url + '&uid=' + fileitem_uid;
				var filename_escaped = '';
				if(filename != null) {
					filename_escaped = htmlentities(filename,'ENT_QUOTES'); 
				}
				if(edit_i == i) {
					s += this.formatRowEdit(filename_escaped);
				} else {
					s += this.formatRowItem(filename, tag, fileitem_uid, href);
				}
			}
			if(ary.length == 0) {
				s += this.formatRowEmpty();
			}
			var msg = '<table class="list_of_files">' + s + '</table>';
		  	$(this.m_id_list_of_files).html(msg);
		
			var thisObj = this;
			
/*			var selector_download_file = this.m_id_list_of_files + ' .download_file';
			$(selector_download_file).click( function(event) { 
				var ary = event.target.className.split(' ');
				var tag = thisObj.findTagInStringArray(ary);
				thisObj.downloadClickAction(tag); 
			});*/
			
			var selector_delete_button = this.m_id_list_of_files + ' .delete_button';
			$(selector_delete_button).click( function(event) { 
				var ary = event.target.className.split(' ');
				var tag = thisObj.findTagInStringArray(ary);
				thisObj.deleteClickAction(tag); 
			});
			
			var selector_rename_button = this.m_id_list_of_files + ' .rename_button';
			$(selector_rename_button).click( function(event) { 
				var ary = event.target.className.split(' ');
				var tag = thisObj.findTagInStringArray(ary);
				thisObj.renameClickAction(tag); 
			});
			
			var selector_cancelrename_button = this.m_id_list_of_files + ' .cancel_rename_button';
			$(selector_cancelrename_button).click( function(event) {
				event.preventDefault();
				thisObj.m_edit_index = -1;
				thisObj.displayFilenames();
				return false;
			});

			var selector_okrename_button = this.m_id_list_of_files + ' .ok_rename_button';
			var selector_filename_value = this.m_id_list_of_files + ' .filename_value';
			$(selector_okrename_button).click( function(event) {
				event.preventDefault();

				// obtain the new name from the form
				var value = $(selector_filename_value).val();
				if(value != undefined) {
					var index = thisObj.m_edit_index;
					thisObj.m_filenames[index] = value;
					
					var fileitem_uid = 0;
					if(index < thisObj.m_fileitem_uids.length) {
						fileitem_uid = thisObj.m_fileitem_uids[index];
					}

					if(1) {
						/*
						actually rename the file
						*/
						thisObj.renameTheFile(fileitem_uid, value.toString());
					}

				}

				thisObj.m_edit_index = -1;
				thisObj.displayFilenames();
				return false;
			});
		},

		renameFileCallback: function(text) {
			// the file has now been renamed on the server, re-display filenames again
			// console.log("renamed file:", text);
			this.refreshFilenames();
		},


		/*
		tell server to rename the file
		*/
		renameTheFile: function(fileitem_uid, new_filename) {
			// console.log("actual rename", fileitem_uid, new_filename);
			$.ajax({
				url: this.m_rename_file_url,
				cache: false,
				context: this,
				data: { uid: fileitem_uid, name: new_filename },
				success: function(text) { this.renameFileCallback(text); }
			});
		},
		
		modeProgress: function() {
			$(this.m_id_upload_form).hide();
			$(this.m_id_progressbar_outer).show();
		},

		modeNormal: function() {
			$(this.m_id_progressbar_outer).hide();
			$(this.m_id_upload_form).show();
		},

		modeNormal2: function() {
			var thisObj = this;
			$(this.m_id_progressbar_outer).delay(500).fadeOut('slow', function() { thisObj.modeNormal(); });
		},
		
		refreshFilenames: function() {
			// console.log('refreshFilenames', this.m_list_files_url);
			var thisObj = this;
			$.getJSON(
				this.m_list_files_url, 
				
				{ /*thename: this.m_name*/ },
				
				function(data) {
					// console.log('refreshFilenames_callback', thisObj.m_list_files_url);
					if(data.uids) {
						thisObj.m_fileitem_uids = data.uids;
					} else {
						thisObj.m_fileitem_uids = [];
					}
					// console.log("fileitem_uids:", thisObj.m_fileitem_uids);
					if(data.errors) {
						thisObj.m_errors = data.errors;
						thisObj.displayErrors();
					} else
					if(data.files) {
						thisObj.m_filenames = data.files;
						thisObj.displayFilenames();
					}
				}
			);
		},
		
		clearFileInputField: function() {
			$(this.m_id_upload_form)[0].reset();
		},
		
		pollProgressCallback: function(text) {

			if((text == 'DONE') || (text == 'FAIL')) {
				this.m_progress = 100;
				this.displayProgress();
				this.refreshFilenames();
				this.clearFileInputField();
				this.modeNormal2();
				
				/*
				FILESYSTEM-HACK: sometimes the uploaded file isn't fully
				written to the filesystem even though status is 'DONE',
				so really we should continue trying until it's there,
				however I don't have time to code an entire IPC protocol
				for this (and ajax isn't the solution). 
				
				SOLUTION: a few retries, assuming that the filesystem is
				faster than 16 seconds to complete the operation.
				*/
		   		setTimeout($.proxy(this.refreshFilenames, this), 1000);
		   		setTimeout($.proxy(this.refreshFilenames, this), 2000);
		   		setTimeout($.proxy(this.refreshFilenames, this), 4000);
		   		setTimeout($.proxy(this.refreshFilenames, this), 8000);
		   		setTimeout($.proxy(this.refreshFilenames, this), 16000);
				return;
			}

			this.m_progress = parseInt(text);
			this.displayProgress();

			if(this.m_poll_count > 2000) {
				return;
			}
			this.m_poll_count++;
	   		setTimeout($.proxy(this.pollProgress, this), 150);
		},
			
		pollProgress: function() {
			$.ajax({
				url: this.m_get_progress_url,
				cache: false,
				context: this,
				data: { apc_key: this.m_apc_key },
				success: function(text) { this.pollProgressCallback(text); }
			});
		},

		startPolling: function() {
			this.modeProgress();

			this.m_progress = 0;
			this.displayProgress();
			
			this.m_poll_count = 0;

			this.pollProgress();
		},

		/*
		user clicked on the "rename" button causing the file to be renamed
		*/
		renameClickAction: function(tag) {
			this.m_edit_index = tag;
			this.displayFilenames();

			/*
			INTENTION: select the text in the textfield
			BROWSER-HACK: first focus() then select(), to make the Opera browser happy.
			*/
			var selector_filename_value = this.m_id_list_of_files + ' .filename_value';
			$(selector_filename_value).focus();
			$(selector_filename_value).select();
		},

		deleteFileCallback: function(text) {
			// the file has now been deleted on the server, re-display filenames again
			// console.log("deleted file:", text);
			this.refreshFilenames();
		},


		/*
		user clicked on the "delete" button causing the file to be deleted
		*/
		deleteClickAction: function(tag) {
			if(tag == null) {
				return;
			}
			
			var filename = this.m_filenames[tag];
			var fileitem_uid = this.m_fileitem_uids[tag];
			// console.log('delete UID', fileitem_uid);
			
			var question = 'Slet "' + filename + '" ?';
			if(!confirm(question)) {
				return;
			}

			// console.log("yes delete it: ", filename);

			if(1) {
				/*
				tell server to delete the file
				*/
				$.ajax({
					url: this.m_delete_file_url,
					cache: false,
					context: this,
					data: { uid: fileitem_uid },
					success: function(text) { this.deleteFileCallback(text); }
				});
			}
 
   			if(0) {
				/*
				fake that the file has been deleted
				*/
				this.m_filenames = array_remove(this.m_filenames, tag);
				this.displayFilenames();
			}
		},

		/*
		user clicked on the "download" button causing the file to be downloaded
		*/
/*		downloadClickAction: function(tag) {
			if(tag == null) {
				return;
			}
			var filename = this.m_filenames[tag];
			var fileitem_uid = this.m_fileitem_uids[tag];
			console.log('download UID', fileitem_uid, filename);
		}, */


		/*
		finds the first "tag_1234" in an array
		
		findTagInStringArray(['abcd', 'xyz', 'tag_12', 'hello', 'tag_55', 'test']) -> 12
		findTagInStringArray(['abcd', 'xyz', 'hello', 'tag_55', 'test'])           -> 55
		findTagInStringArray(['abcd', 'xyz', 'hello', 'test'])                     -> null
		*/
		findTagInStringArray: function(strings) {
			if(!isArray(strings)) {
				// alert("strings is not an array.");
				return null;
			}
			for(var i=0;i<strings.length;i++) {
				var matches = /tag_(\d+)/.exec(strings[i]);
				if(matches != null) return parseInt(matches[1]);
			}
			return null;
		}

	};
})();

// factory method
UploadController.setup = function(options) { return new UploadController(options).setup(); };
