// 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',
        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 {0}.',
        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: "1048576",          // 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: "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() {
       var grid = this; 
        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: 80, 
            dataIndex: 'status', 
            renderer: this.formatStatus.createDelegate(this)
        }];
        
        this.sm = new Ext.grid.RowSelectionModel({
            singleSelect: this.single_select
        });


        this.progress_bar = new Ext.ProgressBar({
            text: this.strings.text_progressbar
//            width: this.width - 7
        }); 

        this.tbar = [{
            text: this.strings.text_add,
            iconCls: 'SwfUploadPanel_iconAdd',
            id: 'swfuploadpanel-add-button',
            xhandler: function() {
                if (this.single_select) {
                    this.suo.selectFile();
                }
                else {
                    this.suo.selectFiles();
                }
            },
            xscope: this,
            listeners: {
                render: function() {
                    grid.myInit();
                }
            }
        }, '->', {
            text: this.strings.text_cancel,
            id: 'swfuploadpanel-cancel-button',
            iconCls: 'SwfUploadPanel_iconCancel',
            handler: this.stopUpload,
            scope: this,
            hidden: true
        }, {
            text: this.strings.text_upload,
            id: 'swfuploadpanel-upload-button',
            iconCls: 'SwfUploadPanel_iconUpload',
            handler: this.startUpload,
            scope: this,
            hidden: true
        }, {
            text: this.strings.text_clear,
            id: 'swfuploadpanel-clear-button',
            iconCls: 'SwfUploadPanel_iconClear',
            handler: this.removeAllFiles,
            scope: this,
            hidden: false
        }];
        
        this.bbar = [
            this.progress_bar
        ];
        
        Ext.ux.SwfUploadPanel.superclass.initComponent.call(this);
        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();
            }
        });
    },
    onRender: function() {
        Ext.ux.SwfUploadPanel.superclass.onRender.apply(this,arguments);
		
		this.setHeight( parseInt( this.height ) );
		
        this.resizeProgressBar();
        var tb = this.getTopToolbar();
        this.addBtn = Ext.getCmp('swfuploadpanel-add-button');
        this.cancelBtn = Ext.getCmp('swfuploadpanel-cancel-button');
        this.uploadBtn = Ext.getCmp('swfuploadpanel-upload-button');
        this.clearBtn = Ext.getCmp('swfuploadpanel-clear-button');
        this.on('resize', this.resizeProgressBar, this);
		
    
    },
    myInit: 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,   
			prevent_swf_caching: this.prevent_swf_caching,
    
            // 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.debugHandler
        });
        
        Ext.get(this.suo.movieName).setStyle({
            position: 'absolute', 
            top: 0,
            left: 0
        });
    },

    // private
    resizeProgressBar: function() {
        if (this.progress_bar.el) {
            this.progress_bar.setWidth(this.getBottomToolbar().el.getWidth() - 5);
            Ext.fly(this.progress_bar.el.dom.firstChild.firstChild).applyStyles("height: 16px");
        }
    },
    
    /**
     * SWFUpload debug handler
     * @param {Object} line
     */
    debugHandler: function(line) {
        if( typeof console != "undefined" ) {
        	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;
        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( parseInt(this.file_size_limit) )));
                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 );
    },

    /**
     * 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
     */
    uploadProgress: function(file, bytes_completed, bytes_total) {
        if(this.debug) console.info('UPLOAD PROGRESS');
        
        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');

        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.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.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);
    }   
    
});
Ext.reg("swfuploadpanel", Ext.ux.SwfUploadPanel );
