// Create user extension namespace
Ext.namespace('Ext.ux');

/**
 * @class Ext.ux.SwfUploadPanel
 * @extends Ext.grid.GridPanel

 * Makes a Panel to provide the ability to upload multiple files using the SwfUpload flash script.
 *
 * @author Stephan Wentz
 * @author Michael Giddens (Original author)
 * @website http://www.brainbits.net
 * @created 2008-02-26
 * @version 0.5
 * 
 * known_issues 
 *	  - Progress bar used hardcoded width. Not sure how to make 100% in bbar
 *	  - Panel requires width / height to be set.  Not sure why it will not fit
 *	  - when panel is nested sometimes the column model is not always shown to fit until a file is added. Render order issue.
 *	  
 * @constructor
 * @param {Object} config The config object
 */
Ext.ux.SwfUploadPanel = Ext.extend(Ext.grid.GridPanel, {
	
	/**
	 * @cfg {Object} strings
	 * All strings used by Ext.ux.SwfUploadPanel
	 */
	strings: {
		text_add: 'Add File(s)',
		text_upload: 'Upload File(s)',
		text_cancel: 'Cancel Upload',
		text_clear: 'Clear Queue',
		text_progressbar: 'Progress Bar',
		text_remove: 'Remove File',
		text_remove_sure: 'Are you sure you wish to remove this file from queue?',
		text_error: 'Error',
		text_uploading: 'Uploading file: {0} ({1} of {2})',
		header_filename: 'Filename',
		header_size: 'Size',
		header_status: 'Status',
		header_progress: "Progress",
		status: {
			0: 'Queued',
			1: 'Uploading...',
			2: 'Completed',
			3: 'Error',
			4: 'Cancelled'
		},
		error_queue_exceeded: 'The selected file(s) exceed(s) the maximum number of {0} queued files.',
		error_queue_slots_0: 'There is no slot left',
		error_queue_slots_1: 'There is only one slot left',
		error_queue_slots_2: 'There are only {0} slots left',
		error_size_exceeded: 'The selected files size exceeds the allowed limit of 1 MB.',
		error_zero_byte_file: 'Zero byte file selected.',
		error_invalid_filetype: 'Invalid filetype selected.',
		error_file_not_found: 'File not found 404.',
		error_security_error: 'Security Error. Not allowed to post to different url.'
	},
	
	/**
	 * @cfg {Boolean} single_select
	 * True to allow multiple file selections, false for single file selection.
	 * Please note that this doesn't affect the number of allowed files in the queue. 
	 * Use the {@link #file_queue_limit} parameter to change the allowed number of files in the queue. 
	 */
	single_select: false,
	/**
	 * @cfg {Boolean} confirm_delete
	 * Show a confirmation box on deletion of queued files.
	 */ 
	confirm_delete: true,
	/**
	 * @cfg {String} file_types
	 * Allowed file types for the File Selection Dialog. Use semi-colon as a seperator for multiple file-types.
	 */ 
	file_types: "*.*",				   // Default allow all file types
	/**
	 * @cfg {String} file_types
	 * A text description that is displayed to the user in the File Browser dialog.
	 */ 
	file_types_description: "All Files", // 
	/**
	 * @cfg {String} file_size_limit
	 * The file_size_limit setting defines the maximum allowed size of a file to be uploaded. 
	 * This setting accepts a value and unit. Valid units are B, KB, MB and GB. If the unit is omitted default is KB. 
	 * A value of 0 (zero) is interpretted as unlimited.
	 */ 
	file_size_limit: "1MB",		  // Default size limit 100MB
	/**
	 * @cfg {String} file_upload_limit
	 * Defines the number of files allowed to be uploaded by SWFUpload. 
	 * This setting also sets the upper bound of the {@link #file_queue_limit} setting. 
	 * The value of 0 (zero) is interpretted as unlimited.
	 */ 
	file_upload_limit: "0",			  // Default no upload limit
	/**
	 * @cfg {String} file_queue_limit
	 * Defines the number of unprocessed files allowed to be simultaneously queued.
	 * The value of 0 (zero) is interpretted as unlimited.
	 */ 
	file_queue_limit: "0",			   // Default no queue limit
	/**
	 * @cfg {String} file_post_name
	 * The file_post_name allows you to set the value name used to post the file.
	 */ 
	file_post_name: "Filedata",		  // Default name
	/**
	 * @cfg {String} flash_url
	 * The full, absolute, or relative URL to the Flash Control swf file.
	 */ 
	flash_url: BaseHref+'javascript/SWFUpload/swfupload.swf',	   // Default url, relative to the page url
	/**
	 * @cfg {Boolean} debug
	 * A boolean value that defines whether the debug event handler should be fired.
	 */ 
	debug: false,
	
	// standard grid parameters
	autoExpandColumn: 'name',
	enableColumnResize: false,
	enableColumnMove: false,

	// private
	upload_cancelled: false,
		
	// private
	initComponent: function() {

		Ext.apply(this.strings, GO.lang.swfUploadPanelStrings);

		this.addEvents(
			/**
			 * @event swfUploadLoaded
			 * Fires after the Flash object has been loaded
			 * @param {Ext.grid.GridPanel} grid This grid
			 */
			'swfUploadLoaded',
			/**
			 * @event swfUploadLoaded
			 * Fires after a file has been qeueud
			 * @param {Ext.grid.GridPanel} grid This grid
			 * @param {Object} file The file object that produced the error
			 */
			'fileQueued',
			/**
			 * @event startUpload
			 * Fires before the upload starts
			 * @param {Ext.grid.GridPanel} grid This grid
			 */
			'startUpload',
			/**
			 * @event fileUploadError
			 * Fires after an upload has been stopped or cancelled
			 * @param {Ext.grid.GridPanel} grid This grid
			 * @param {Object} file The file object that produced the error
			 * @param {String} code The error code
			 * @param {String} message Supplemental error message
			 */
			'fileUploadError',
			/**
			 * @event fileUploadSuccess
			 * Fires after an upload has been successfully uploaded
			 * @param {Ext.grid.GridPanel} grid This grid
			 * @param {Object} file The file object that has been uploaded
			 * @param {Object} data The response data of the upload request
			 */
			'fileUploadSuccess',
			/**
			 * @event fileUploadComplete
			 * Fires after the upload cycle for one file finished
			 * @param {Ext.grid.GridPanel} grid This grid
			 * @param {Object} file The file object that has been uploaded
			 */
			'fileUploadComplete',
			/**
			 * @event fileUploadComplete
			 * Fires after the upload cycle for all files in the queue finished
			 * @param {Ext.grid.GridPanel} grid This grid
			 */
			'allUploadsComplete',
			/**
			 * @event fileUploadComplete
			 * Fires after one or more files have been removed from the queue
			 * @param {Ext.grid.GridPanel} grid This grid
			 */
			'removeFiles',
			/**
			 * @event fileUploadComplete
			 * Fires after all files have been removed from the queue
			 * @param {Ext.grid.GridPanel} grid This grid
			 */
			'removeAllFiles'
		);
		
		this.rec = Ext.data.Record.create([
			 {name: 'name'},
			 {name: 'size'},
			 {name: 'id'},
			 {name: 'type'},
			 {name: 'creationdate', type: 'date', dateFormat: 'm/d/Y'},
			 {name: 'status'}
		]);
		
		this.store = new Ext.data.Store({
			reader: new Ext.data.JsonReader({
				  id: 'id'
			 }, this.rec)
		});
		
		this.columns = [{
			id:'name', 
			header: this.strings.header_filename, 
			dataIndex: 'name'
		},{
			id:'size', 
			header: this.strings.header_size, 
			width: 80, 
			dataIndex: 'size', 
			renderer: this.formatBytes
		},{
			id:'status', 
			header: this.strings.header_status, 
			width: 100, 
			dataIndex: 'status', 
			renderer: this.formatStatus.createDelegate(this)
		},{
			id:'progress', 
			header: this.strings.header_progress, 
			width: 150,  
			renderer: this.formatProgressBar
		}
		
		];
		
		this.sm = new Ext.grid.RowSelectionModel({
			singleSelect: this.single_select
		});


		this.progress_bar = new Ext.ProgressBar({
			text: this.strings.text_progressbar,
			// added by mh
			listeners: {
				render: function(){
					this.resizeProgressBar();
				},
				scope: this
			}
			// end added
//			width: this.width - 7
		}); 

		this.tbar = [{
			text: this.strings.text_add,
			iconCls: 'SwfUploadPanel_iconAdd',
			xhandler: function() {
				if (this.single_select) {
					this.suo.selectFile();
				}
				else {
					this.suo.selectFiles();
				}
			},
			xscope: this,
			// added by mh
			listeners: {
				render: function(){
					this.initButton();
				},
				scope: this
			}
			// end added
		}, '->', {
			text: this.strings.text_cancel,
			iconCls: 'SwfUploadPanel_iconCancel',
			handler: this.stopUpload,
			scope: this,
			hidden: true
		}, {
			text: this.strings.text_upload,
			iconCls: 'SwfUploadPanel_iconUpload',
			handler: this.startUpload,
			scope: this,
			hidden: true
		}, {
			text: this.strings.text_clear,
			iconCls: 'SwfUploadPanel_iconDelete',
			handler: this.removeAllFiles,
			scope: this,
			hidden: false
		}];
		
		//this.bbar = [this.progress_bar]; 
		
		this.addListener({
			keypress: {
				fn: function(e) {
					if (this.confirm_delete) {
						if(e.getKey() == e.DELETE) {
							Ext.MessageBox.confirm(this.strings.text_remove,this.strings.text_remove_sure, function(e) {
								if (e == 'yes') {
									this.removeFiles();
								}
							}, this);
						}   
					} else {
						this.removeFiles(this);
					}
				},
				scope: this
			},
			
			// Prevent the default right click to show up in the grid.
			contextmenu: function(e) {
				e.stopEvent();
			},
			
			// modified by mh
			render: function(){
				this.addBtn = this.getTopToolbar().items.items[0];
				this.cancelBtn = this.getTopToolbar().items.items[2];
				this.uploadBtn = this.getTopToolbar().items.items[3];
				this.clearBtn = this.getTopToolbar().items.items[4];
	
				// defer to avoid resize on render
				(function(){
					this.on('resize', this.resizeProgressBar, this);
				}).defer(100, this);
			}
			// end modified
		});

		Ext.ux.SwfUploadPanel.superclass.initComponent.call(this);
	},
	
	// modified by mh - refactor to separate method
	initButton: function() {
		var suoID = Ext.id();
		
		var em = this.addBtn.el.child('em');
		em.setStyle({
			position: 'relative',
			display: 'block'
		});
		em.createChild({
			tag: 'div',
			id: suoID
		});
		this.suo = new SWFUpload({
			button_placeholder_id: suoID,
			button_width: em.getWidth(),
			button_height: em.getHeight(),
			button_cursor: SWFUpload.CURSOR.HAND,
			button_window_mode: SWFUpload.WINDOW_MODE.TRANSPARENT,
			
			upload_url: this.upload_url,
			post_params: this.post_params,
			file_post_name: this.file_post_name,  
			file_size_limit: this.file_size_limit,
			file_queue_limit: this.file_queue_limit,
			file_types: this.file_types,
			file_types_description: this.file_types_description,
			file_upload_limit: this.file_upload_limit,
			flash_url: this.flash_url,   
	
			// Event Handler Settings
			swfupload_loaded_handler: this.swfUploadLoaded.createDelegate(this),
	
			file_dialog_start_handler: this.fileDialogStart.createDelegate(this),
			file_queued_handler: this.fileQueue.createDelegate(this),
			file_queue_error_handler: this.fileQueueError.createDelegate(this),
			file_dialog_complete_handler: this.fileDialogComplete.createDelegate(this),
			
			upload_start_handler: this.uploadStart.createDelegate(this),
			upload_progress_handler: this.uploadProgress.createDelegate(this),
			upload_error_handler: this.uploadError.createDelegate(this), 
			upload_success_handler: this.uploadSuccess.createDelegate(this),
			upload_complete_handler: this.uploadComplete.createDelegate(this),
	
			debug: this.debug,
			debug_handler: this.debug ? this.debugHandler : Ext.emptyFn
		});
		
		Ext.get(this.suo.movieName).setStyle({
			position: 'absolute', 
			top: 0,
			left: 0
		});
	},

	formatProgressBar : function(_v, cellmeta, record){

		var returnValue;

		switch(record.data.fileState){
			case SWFUpload.FILE_STATUS.COMPLETE:
				if(Ext.isIE){
					returnValue = 
						'<div class="x-progress-wrap" style="height: 18px">' +
							'<div class="x-progress-inner">' +
								'<div style="width: 100%;" class="x-progress-bar x-progress-text">' + '100 %'
								'</div>' +
							'</div>' +
						'</div>';
				}else{
					returnValue = 
						'<div class="x-progress-wrap" style="height: 18px">' +
							'<div class="x-progress-inner">' +
								'<div id="progressBar_' + record.data.id + '" style="width: 100%;" class="x-progress-bar">' +
								'</div>' +
								'<div id="progressText_' + record.data.id + '" style="width: 100%;" class="x-progress-text x-progress-text-back" />100 %</div>'
							'</div>' +
						'</div>';
				}
				break;
			default : 
				returnValue = 
					'<div class="x-progress-wrap" style="height: 18px">' +
						'<div class="x-progress-inner">' +
							'<div id="progressBar_' + record.data.id + '" style="width: 0%;" class="x-progress-bar">' +
							'</div>' +
							'<div id="progressText_' + record.data.id + '" style="width: 100%;" class="x-progress-text x-progress-text-back" />0 %</div>'
						'</div>' +
					'</div>';
				break;
		}
		return returnValue;
	},

	// private
	resizeProgressBar: function() {
		this.progress_bar.setWidth(this.getBottomToolbar().el.getWidth() - 5);
		Ext.fly(this.progress_bar.el.dom.firstChild.firstChild).applyStyles("height: 18px");
	},
	
	/**
	 * SWFUpload debug handler
	 * @param {Object} line
	 */
	debugHandler: function(line) {
		console.log(line);
	},
	
	/**
	 * Formats file status
	 * @param {Integer} status
	 * @return {String}
	 */
	formatStatus: function(status) {
		return this.strings.status[status];
	},
	
	/**
	 * Formats raw bytes into kB/mB/GB/TB
	 * @param {Integer} bytes
	 * @return {String}
	 */
	formatBytes: function(size) {
		if (!size) {
			size = 0;
		}
		var suffix = ["B", "KB", "MB", "GB"];
		var result = size;
		size = parseInt(size, 10);
		result = size + " " + suffix[0];
		var loop = 0;
		while (size / 1024 > 1) {
			size = size / 1024;
			loop++;
		}
		result = Math.round(size) + " " + suffix[loop];

		return result;
	},

	/**
	 * SWFUpload swfUploadLoaded event
	 */
	swfUploadLoaded: function() {
		if(this.debug) console.info('SWFUPLOAD LOADED');
		
		this.fireEvent('swfUploadLoaded', this);
	},
		
	/**
	 * SWFUpload fileDialogStart event
	 */
	fileDialogStart: function() {
		if(this.debug) console.info('FILE DIALOG START');
		
		this.fireEvent('fileDialogStart', this);
	},
	
	/**
	 * Add file to store / grid
	 * SWFUpload fileQueue event
	 * @param {Object} file
	 */
	fileQueue: function(file) {
		if(this.debug) console.info('FILE QUEUE');		
		file.status = 0;
		var r = new this.rec(file);
		r.id = file.id;
		this.store.add(r);
		
		this.fireEvent('fileQueued', this, file);
	},

	/**
	 * Error when file queue error occurs
	 * SWFUpload fileQueueError event
	 * @param {Object}  file
	 * @param {Integer} code
	 * @param {string}  message
	 */
	fileQueueError: function(file, code, message) {
		if(this.debug) console.info('FILE QUEUE ERROR');

		switch (code) {
			case -100: 
				var slots;
				switch(message) {
					case '0':
						slots = this.strings.error_queue_slots_0;
						break;
					case '1':
						slots = this.strings.error_queue_slots_1;
						break;
					default:
						slots = String.format(this.strings.error_queue_slots_2, message);
				}
				Ext.MessageBox.alert(this.strings.text_error, String.format(this.strings.error_queue_exceeded + ' ' + slots, this.file_queue_limit));
				break;
				
			case -110:
				Ext.MessageBox.alert(this.strings.text_error, String.format(this.strings.error_size_exceeded, this.formatBytes(this.file_size_limit * 1024)));
				break;

			case -120:
				Ext.MessageBox.alert(this.strings.text_error, this.strings.error_zero_byte_file);
				break;

			case -130:
				Ext.MessageBox.alert(this.strings.text_error, this.strings.error_invalid_filetype);
				break;
		}
		
		this.fireEvent('fileQueueError', this, file, code/*, error*/);
	},

	/**
	 * SWFUpload fileDialogComplete event
	 * @param {Integer} file_count
	 */
	fileDialogComplete: function(file_count) {
		if(this.debug) console.info('FILE DIALOG COMPLETE');


		
		if (file_count > 0) {
			//this.uploadBtn.show();
		}
		
		this.addBtn.show();
		this.clearBtn.show();
		
		this.fireEvent('fileDialogComplete', this, file_count);
	},

	/**
	 * SWFUpload uploadStart event
	 * @param {Object} file
	 */
	uploadStart: function(file) {
		if(this.debug) console.info('UPLOAD START');		
		this.fireEvent('uploadStart', this, file);
		
		return true;
	},
	
	/**
	 * SWFUpload uploadProgress event
	 * @param {Object}  file
	 * @param {Integer} bytes_completed
	 * @param {Integer} bytes_total
	 */
	progressCount : 0,
	uploadProgress: function(file, bytes_completed, bytes_total) {
		//if(this.debug) 
		var percent = Math.ceil((bytes_completed / bytes_total) * 100);
		if (percent != this.progressCount)
		{
			Ext.getDom('progressBar_' + file.id).style.width = percent + "%";
			Ext.getDom('progressText_' + file.id).innerHTML = percent + " %";
			this.progressCount = percent;
		}
		
		//console.info(percent);
		
		this.store.getById(file.id).set('status', 1);	
		
		//this.store.getById(file.id).commit();
		//this.progress_bar.updateProgress(bytes_completed/bytes_total, String.format(this.strings.text_uploading, file.name, this.formatBytes(bytes_completed), this.formatBytes(bytes_total)));
		
		this.fireEvent('uploadProgress', this, file, bytes_completed, bytes_total);
	},

	/**
	 * SWFUpload uploadError event
	 * Show notice when error occurs
	 * @param {Object} file
	 * @param {Integer} error
	 * @param {Integer} code
	 * @return {}
	 */
	uploadError: function(file, error, code) {
		if(this.debug) console.info('UPLOAD ERROR');
		if (code==500)
		{
			Ext.MessageBox.alert(this.strings.text_error, "File too big");
			this.fireEvent('fileUploadError', this, file, error, code);
			return;
		}
		switch (error) {
			case -200:  
				Ext.MessageBox.alert(this.strings.text_error, this.strings.error_file_not_found);
				break;
				
			case -230:  
				Ext.MessageBox.alert(this.strings.text_error, this.strings.error_security_error);
				break;
				
			case -290:
				this.store.getById(file.id).set('status', 4);
				this.store.getById(file.id).commit();
				break;
		}
		
		this.fireEvent('fileUploadError', this, file, error, code);
	},

	/**
	 * SWFUpload uploadSuccess event
	 * @param {Object} file
	 * @param {Object} response
	 */ 
	uploadSuccess: function(file, response) {
		if(this.debug) console.info('UPLOAD SUCCESS');
		
		var data = Ext.decode(response); 
		if (data.success) {
			this.store.getById(file.id).data.fileState = SWFUpload.FILE_STATUS.COMPLETE;
			this.store.getById(file.id).set('status', 2);
			//this.store.remove(this.store.getById(file.id));
		} else {
			this.store.getById(file.id).set('status', 3);
			this.store.getById(file.id).commit();
			if (data.msg) {
				Ext.MessageBox.alert(this.strings.text_error, data.msg);
			}
		}
		
		
		this.fireEvent('fileUploadSuccess', this, file, data);
	},

	/**
	 * SWFUpload uploadComplete event
	 * @param {Object} file
	 */
	uploadComplete: function(file) {
		if(this.debug) console.info('UPLOAD COMPLETE');
		
		this.progress_bar.reset();
		this.progress_bar.updateText(this.strings.text_progressbar);
		
		if(this.suo.getStats().files_queued && !this.upload_cancelled) {
			this.suo.startUpload();
		} else {
			this.fireEvent('fileUploadComplete', this, file);
			
			this.allUploadsComplete();
		}
		
	},
	
	/**
	 * SWFUpload allUploadsComplete method
	 */
	allUploadsComplete: function() {
		this.cancelBtn.hide();
		this.addBtn.show();
		this.clearBtn.show();
		
		this.fireEvent('allUploadsComplete', this);
	},
	
	/**
	 * SWFUpload setPostParams method
	 * @param {String} name
	 * @param {String} value
	 */
	addPostParam: function(name, value) {
		if (this.suo) {
			this.suo.settings.post_params[name] = value;
			this.suo.setPostParams(this.suo.settings.post_params);
		} else {
			this.post_params[name] = value;
		}
	},
		
	/**
	 * Start file upload
	 * SWFUpload startUpload method
	 */
	startUpload: function() {
		if(this.debug) console.info('START UPLOAD');
		
		this.cancelBtn.show();
		this.uploadBtn.hide();
		this.clearBtn.hide();
//		this.addBtn.hide();
		
		this.upload_cancelled = false;
		
		this.fireEvent('startUpload', this);
		
		this.suo.startUpload();
	},
	
	/**
	 * SWFUpload stopUpload method
	 * @param {Object} file
	 */
	stopUpload: function(file) {
		if(this.debug) console.info('STOP UPLOAD');
		
		this.suo.stopUpload();
		
		this.upload_cancelled = true;
		
		this.getStore().each(function() {
			if (this.data.status == 1) {
				this.set('status', 0);
				this.commit();
			}
		});

		this.cancelBtn.hide();
		if (this.suo.getStats().files_queued > 0) {
			this.uploadBtn.show();
		}
		this.addBtn.show();
		this.clearBtn.show();

		this.progress_bar.reset();
		this.progress_bar.updateText(this.strings.text_progressbar);

	},
	
	/**
	 * Delete one or multiple rows
	 * SWFUpload cancelUpload method
	 */
	removeFiles: function() {
		if(this.debug) console.info('REMOVE FILES');

		var selRecords = this.getSelectionModel().getSelections();
		for (var i=0; i < selRecords.length; i++) {
			if (selRecords[i].data.status != 1) {
				this.suo.cancelUpload(selRecords[i].id);
				this.store.remove(selRecords[i]);
			}
		}
		
		if (this.suo.getStats().files_queued === 0) {
			//this.uploadBtn.hide();
//			this.clearBtn.hide();
		}
		
		this.fireEvent('removeFiles', this);
	},
	
	/**
	 * Clear the Queue
	 * SWFUpload cancelUpload method
	 */
	removeAllFiles: function() {
		if(this.debug) console.info('REMOVE ALL');
		
		// mark all internal files as cancelled
		var files_left = this.suo.getStats().files_queued;

		while (files_left > 0) {
			this.suo.cancelUpload();
			files_left = this.suo.getStats().files_queued;
		}
		
		this.store.removeAll();
		
		this.cancelBtn.hide();
		this.uploadBtn.hide();
//		this.clearBtn.hide();
		
		this.fireEvent('removeAllFiles', this);
	}   
	
});