Ext.ns('Ext.ux');

/**
 * Ext.ux.FileUpload
 * 
 * Simple rewrite of swfupload api for easier 
 * integration on ext advanced upload components
 * 
 * ExtJS : 2.0.2
 * Swfupload : 2.1.0
 * 
 * @author OXYS.net
 * @version 0.0.1
 * @extends Ext.util.Observable
 */

Ext.ux.FileUploadFile = Ext.extend(Ext.util.Observable, {
    
    id: '',
    name: '',
    size: '',
    type: '',
    start: null,
    end: null,    
    creationDate: '',
    modificationDate: '',
    remainingTime: null,
    bytesComplete: 0,
    status: '',
    queueError:null,
    uploadError:null, 
    fileUploadInstance: null,   
    params:{},
    
    constructor: function(fileData, params, fileUploadInstance) {
        this.id = fileData.id;
        this.name = fileData.name;
        this.type = fileData.type;
        this.size = fileData.size;
        this.bytesComplete = 0;
        this.creationDate = fileData.creationdate;
        this.modificationDate = fileData.modificationdate;
        this.status = Ext.ux.FileUpload.FILE_STATUS.QUEUED;
        
        for (param in params) {
            fileUploadInstance.addFileParam(this.id, param, params[param]);
        }
            
        this.fileUploadInstance = fileUploadInstance;
        
        this.addEvents(
            'uploadprogress',
            'uploaderror',
            'uploadsuccess',
            'uploadcomplete'
        );
    },
    
    fileQueueError: function(errorCode, message){
        this.status = Ext.ux.FileUpload.FILE_STATUS.ERROR;
        this.queueError = errorCode;
    },    
    uploadStart: function(){
        this.start = new Date();
    },
    uploadProgress: function(bytesComplete, bytesTotal){
        this.status = Ext.ux.FileUpload.FILE_STATUS.IN_PROGRESS;
        this.bytesComplete = bytesComplete;
        this.fireEvent("uploadprogress", this);
    },
    uploadSuccess: function(serverData){
        this.status = Ext.ux.FileUpload.COMPLETE;
        this.fireEvent("uploadsuccess", serverData);
    },
    uploadError: function(serverData, errorCode, message){
        this.status = Ext.ux.FileUpload.FILE_STATUS.ERROR;
        this.uploadError = errorCode;
        this.fireEvent("uploaderror", this);
    },
    uploadComplete: function(){
        this.end = new Date();
        this.fireEvent("uploadcomplete", this);
    },


    progress: function() {
        if(this.size == 0 ) return 0;
        return (this.bytesComplete/this.size);
    },
    
    startUpload: function(){
        this.fileUploadInstance.startUpload(this.id);
    },

    cancelUpload: function(){
        this.status = Ext.ux.FileUpload.FILE_STATUS.CANCELLED;
        this.fileUploadInstance.cancelUpload(this.id);
    }
});

/**
 * Ext.ux.FileUpload
 * 
 * ExtJS : 2.0.2
 * Swfupload : 2.1.0
 *  
 * @author OXYS.net
 * @version 0.0.1
 * @extends Ext.Component
 */

Ext.ux.FileUpload = Ext.extend(Ext.Component, {
    
    /**
    * @cfg Upload file after select without calling method startUpload manualy
    */
    autoUpload: true
    /**
    * @cfg
    */
    ,uploadUrl: ''
    /**
    * @cfg
    */
    ,filePostName: 'Filedata'
    /**
    * @cfg
    */
    ,postParams: {}
    /**
    * @cfg
    */
    ,useQueryString: false
    /**
    * @cfg
    */
    ,fileTypes: '*.*'
    /**
    * @cfg
    */
    ,fileTypesDescription: 'All Files'
    /**
    * @cfg Default zero means "unlimited"
    */
    ,fileSizeLimit: 0     
    /**
    * @cfg
    */
    ,fileUploadLimit: 0
    /**
    * @cfg
    */
    ,fileQueueLimit: 0
    /**
    * @cfg
    */
    ,flashUrl: 'libs/js/swfupload/Flash9/swfupload_f9.swf'
    /**
    * @cfg
    */
    ,flashColor: '#FFFFFF'
    /**
    * @cfg {boolean}
    */
    ,debugEnabled:false
    /**
    * @cfg
    */
    ,customSettings: {}

    /**
     * private
     */
    ,initComponent:function() {
        
        this.addEvents(
            /**
             * @event initialize
             * Fires when the file upload component is fully initialized (including the flash)
             * @param {Ext.ux.FileUpload} this
             */
            'initialize',
            /**
             * @event beforefiledialogshow
             * Fires before the file dialog is displayed
             * @param {Ext.ux.FileUpload} this
             */
            'beforefiledialogshow',
            /**
             * @event filequeued
             * @param {Ext.ux.FileUpload, file} this
             */
            'filequeued',
            /**
             * @event filequeueerror
             * @param {Ext.ux.FileUpload, file, Ext.ux.FileUpload.QUEUE_ERROR, message} this
             */
            'filequeueerror',
            /**
             * @event afterdialogcomplete
             * @param {Ext.ux.FileUpload, numFilesSelected, numFilesQueued} this
             */
            'afterfiledialogcompleted',            
            /**
             * @event uploadstart
             * @param {Ext.ux.FileUpload, file} this
             */
            'uploadstart',
            /**
             * @event uploadprogress
             * @param {Ext.ux.FileUpload, file, bytesComplete, bytesTotal} this
             */
            'uploadprogress',
            /**
             * @event uploaderror
             * @param {Ext.ux.FileUpload, file, errorCode, message} this
             */
            'uploaderror',
            /**
             * @event uploadsuccess
             * @param {Ext.ux.FileUpload, file, serverData} this
             */
            'uploadsuccess',
            /**
             * @event uploadcompleted
             * @param {Ext.ux.FileUpload, file} this
             */
            'uploadcompleted'
            
            
        );
        this.eventQueue = [];
        this.files = [];
        this.fileParams = {};
        this.movieName = "SWFUpload_" + Ext.ux.FileUpload.movieCount++;
        Ext.ux.FileUpload.instances[this.movieName] = this;
        var s = document.createElement("div");
        Ext.DomHelper.applyStyles(s, {width: '1px',height : '1px'});
        s.innerHTML=this.getFlashHTML();
        document.body.appendChild(s);
        this.el = document.getElementById(this.movieName);
        //this.tmp = "ddcd";
    }    
    
    /**
     * private
     * getStats gets the file statistics object.  It looks like this (where n is a number):
     */
    ,getStats : function () {
        return this.callFlash("GetStats");
    }
    
    /**
     * private
     * setStats changes the SWFUpload statistics.  You shouldn't need to 
     * change the statistics but you can.  Changing the statistics does not
     * affect SWFUpload accept for the successful_uploads count which is used
     * by the upload_limit setting to determine how many files the user may upload.
     */
    ,setStats : function (statsObject) {
        this.callFlash("SetStats", statsObject);
    }
    
    /**
     * private
     * setCredentials that will be used to authenticate to the uploadUrl.
     * Note: This feature does not work.  It has been added in anticipation of
     * the Flex 3 SDK which has not been released yet.
     */
    ,setCredentials : function (name, password) {
        this.callFlash("SetCrednetials", name, password);
    }
    
    /**
     * private
     * getFile retrieves a File object by ID or Index.  If the file is
     * not found then 'null' is returned.
     */
    ,getFileObject : function (file) {
        for(i=0;i<this.files.length;i++) {
            if(this.files[i].id == file.id) {
                return this.files[i];
            }    
        }
        return null;
    }
    
    /**
     * private
     * addFileParam sets a name/value pair that will be posted with the
     * file specified by the Files ID.  If the name already exists then the
     * exiting value will be overwritten.
     */
    ,addFileParam : function (fileId, name, value) {
        return this.callFlash("AddFileParam", fileId, name, value);
    }
    
    /**
     * private
     * removeFileParam removes a previously set (by addFileParam) name/value
     * pair from the specified file.
     */
    ,removeFileParam : function (fileId, name) {
        this.callFlash("RemoveFileParam", fileId, name);
    }
    
    /**
     * private
     * setUploadUrl changes the uploadUrl setting.
     */
    ,setUploadURL : function (url) {
        this.uploadUrl = url.toString();
        this.callFlash("SetUploadURL", url);
    }
    
    /**
     * private
     * setPostParams changes the postParams setting
     */
    ,setPostParams : function (paramsObject) {
        this.postParams = paramsObject;
        this.callFlash("SetPostParams", paramsObject);
    }
    
    /**
     * private
     * setFileTypes changes the fileTypes setting and the fileTypesDescription setting
     */
    ,setFileTypes : function (types, description) {
        this.fileTypes = types;
        this.fileTypesDescription = description;
        this.callFlash("SetFileTypes", types, description);
    }
    
    /**
     * private
     * setFileSizeLimit changes the fileSizeLimit setting
     */
    ,setFileSizeLimit : function (fileSizeLimit) {
        this.fileSizeLimit = fileSizeLimit;
        this.callFlash("SetFileSizeLimit", fileSizeLimit);
    }
    
    /**
     * private
     * setFileUploadLimit changes the fileUploadLimit setting
     */
    ,setFileUploadLimit : function (fileUploadLimit) {
        this.fileUploadLimit = fileUploadLimit;
        this.callFlash("SetFileUploadLimit", fileUploadLimit);
    }
    
    /**
     * private
     * setFileQueueLimit changes the fileQueueLimit setting
     */
    ,setFileQueueLimit : function (fileQueueLimit) {
        this.fileQueueLimit = fileQueueLimit;
        this.callFlash("SetFileQueueLimit", fileQueueLimit);
    }
    
    /**
     * private
     * setFilePostName changes the filePostName setting
     */
    ,setFilePostName : function (filePostName) {
        this.filePostName = filePostName;
        this.callFlash("SetFilePostName", filePostName);
    }
    
    /**
     * private
     * setUseQueryString changes the useQueryString setting
     */
    ,setUseQueryString : function (useQueryString) {
        this.useQueryString = useQueryString;
        this.callFlash("SetUseQueryString", useQueryString);
    }
    
    /**
     * private
     * setDebugEnabled changes the debugEnabled setting
     */
    ,setDebugEnabled : function (debugEnabled) {
        this.debugEnabled = debugEnabled;
        this.callFlash("SetDebugEnabled", debugEnabled);
    }
    
    ,flashReady: function () {
        // Check that the movie element is loaded correctly with its ExternalInterface methods defined
        if (typeof(this.el.StartUpload) !== "function") {
            throw "ExternalInterface methods failed to initialize.";
        }
        this.fireEvent("initialize", this);
    }
    
    ,fileDialogStart: function () {
        this.fireEvent("beforefiledialogshow", this);
    }
    
    ,fileQueued: function (fileFlash) {
        file = new Ext.ux.FileUploadFile(fileFlash, this.fileParams, this);
        this.files.push(file);
        this.fireEvent("filequeued", this, file); 
    }    
    
    ,fileQueueError: function (fileFlash, errorCode, message) {
        file = new Ext.ux.FileUploadFile(fileFlash, this.fileParams, this);
        this.files.push(file);
        file.fileQueueError(errorCode, message);
        this.fireEvent("filequeueerror", this, file, errorCode, message); 
    }
    
    ,fileDialogComplete: function(numFilesSelected, numFilesQueued) {
        if (this.autoUpload == true) {
            this.startUpload();
        }
        this.fireEvent("afterfiledialogcompleted", this, numFilesSelected, numFilesQueued);        
    }
    
    ,uploadStart: function (fileFlash) {
        file = this.getFileObject(fileFlash);
        file.uploadStart();
        this.fireEvent("uploadstart", this, file);    
        this.returnUploadStart(fileFlash);
    }
    
    ,uploadProgress: function (fileFlash, bytesComplete, bytesTotal) {
        file = this.getFileObject(fileFlash);
        file.uploadProgress(bytesComplete, bytesTotal);
        this.fireEvent("uploadprogress", this, file, bytesComplete, bytesTotal);    
    }
    
    ,uploadError: function (fileFlash, errorCode, message) {
        file = this.getFileObject(fileFlash);
        file.uploadError(errorCode, message);
        this.fireEvent("uploaderror", this, file, errorCode, message);    
    }
    
    ,uploadSuccess: function (fileFlash, serverData) {
        file = this.getFileObject(fileFlash);
        file.uploadSuccess(serverData);
        this.fireEvent("uploadsuccess", this, file, serverData);    
    }
    
    ,uploadComplete: function (fileFlash) {
        file = this.getFileObject(fileFlash);
        file.uploadComplete();
        if (this.autoUpload == true) {
            this.startUpload();
        }
        this.fireEvent("uploadcompleted", this, file);    
    }
    
    ,returnUploadStart: function (fileFlash) {
        file = this.getFileObject(fileFlash);
        this.callFlash("ReturnUploadStart", true);
    }
    
    ,debug: function (message) {
        console.debug(message);    
    }
    
    /**
     * private
     * callFlash handles function calls made to the Flash element.:
     */
    ,callFlash: function () {
        functionName = arguments[0];
        if (typeof(this.el[functionName]) === "function") {
            if (arguments.length === 1) {
                return this.el[functionName]();
            } else if (arguments.length === 2) {
                return this.el[functionName](arguments[1]);
            } else if (arguments.length === 3) {
                return this.el[functionName](arguments[1], arguments[2]);
            } else if (arguments.length === 4) {
                return this.el[functionName](arguments[1], arguments[2], arguments[3]);
            } else {
                throw "Too many arguments";
            }
        } else {
            throw "Invalid function name";
        }
    }

    /**
     * private
     * selectFile causes a File Selection Dialog window to appear.  This
     * dialog only allows 1 file to be selected.
     */
    ,selectFile: function (fileParams) {
        this.fileParams = fileParams;
        this.callFlash("SelectFile");
    }
    
    /**
     * private
     * selectFiles causes a File Selection Dialog window to appear/ This
     * dialog allows the user to select any number of files
     * Flash Bug Warning: Flash limits the number of selectable files based on the combined length of the file names.
     * If the selection name length is too long the dialog will fail in an unpredictable manner.  There is no work-around
     * for this bug.
     */
    ,selectFiles: function (fileParams) {
        this.fileParams = fileParams;
        this.callFlash("SelectFiles");
    }
    
    /**
     * private
     * startUpload starts uploading the first file in the queue unless
     * the optional parameter 'fileId' specifies the ID 
     */
    ,startUpload: function (fileId) {
        // NOTE: Testing this without using a setTimeout. Since StartUpload was reworked to use ReturnUploadStart
        // it might not be necessary anymore
        this.callFlash("StartUpload", fileId);
    }
    
    /**
     * private
     * cancelUpload cancels any queued file.  The fileId parameter
     * Cancels a the file upload.  You must specify a file_id 
     * must be specified.
     */
    ,cancelUpload: function (fileId) {
        this.callFlash("CancelUpload", fileId);
    }
    
    /**
     * private
     * stopUpload stops the current upload and requeues the file at the beginning of the queue.
     * If nothing is currently uploading then nothing happens.
     */
    ,stopUpload: function () {
        this.callFlash("StopUpload");
    }


    /**
     * private
     * getFlashHTML generates the object tag needed to embed the flash in to the document
     */
    ,getFlashHTML: function () {
        // Flash Satay object syntax: http://www.alistapart.com/articles/flashsatay
        return ['<object id="', this.movieName, '" type="application/x-shockwave-flash" data="', this.flashUrl, '" width="1" height="1" style="-moz-user-focus: ignore;">',
                    '<param name="movie" value="', this.flashUrl, '" />',
                    '<param name="bgcolor" value="', this.flashColor, '" />',
                    '<param name="quality" value="high" />',
                    '<param name="menu" value="false" />',
                    '<param name="allowScriptAccess" value="always" />',
                    '<param name="flashvars" value="' + this.getFlashVars() + '" />',
                    '</object>'].join("");
    }
    
    /**
     * private
     * getFlashVars builds the parameter string that will be passed
     * to flash in the flashvars param.
     */
    ,getFlashVars: function () {
        // Build a string from the post param object
        var paramString = this.buildParamString();
    
        // Build the parameter string
        return ["movieName=", encodeURIComponent(this.movieName),
                "&amp;uploadURL=", encodeURIComponent(this.uploadUrl),
                "&amp;useQueryString=", encodeURIComponent(this.useQueryString),
                "&amp;params=", encodeURIComponent(paramString),
                "&amp;filePostName=", encodeURIComponent(this.filePostName),
                "&amp;fileTypes=", encodeURIComponent(this.fileTypes),
                "&amp;fileTypesDescription=", encodeURIComponent(this.fileTypesDescription),
                "&amp;fileSizeLimit=", encodeURIComponent(this.fileSizeLimit),
                "&amp;fileUploadLimit=", encodeURIComponent(this.fileUploadLimit),
                "&amp;fileQueueLimit=", encodeURIComponent(this.fileQueueLimit),
                "&amp;debugEnabled=", encodeURIComponent(this.debugEnabled)].join("");
    }
    
    /**
     * private
     * buildParamString takes the name/value pairs in the post_params setting object
     * and joins them up in to a string formatted "name=value&amp;name=value"
     */
    ,buildParamString: function () {
        var postParams = this.postParams;
        var paramStringPairs = [];
    
        if (typeof(postParams) === "object") {
            for (var name in postParams) {
                if (postParams.hasOwnProperty(name)) {
                    paramStringPairs.push(encodeURIComponent(name.toString()) + "=" + encodeURIComponent(postParams[name].toString()));
                }
            }
        }
    
        return paramStringPairs.join("&amp;");
    }
});
SWFUpload = Ext.ux.FileUpload;

Ext.ux.FileUpload.instances = []
Ext.ux.FileUpload.movieCount = 0;

Ext.ux.FileUpload.QUEUE_ERROR = {
    QUEUE_LIMIT_EXCEEDED              : -100,
    FILE_EXCEEDS_SIZE_LIMIT          : -110,
    ZERO_BYTE_FILE                      : -120,
    INVALID_FILETYPE                  : -130
};
Ext.ux.FileUpload.UPLOAD_ERROR = {
    HTTP_ERROR                          : -200,
    MISSING_UPLOAD_URL                  : -210,
    IO_ERROR                          : -220,
    SECURITY_ERROR                      : -230,
    UPLOAD_LIMIT_EXCEEDED              : -240,
    UPLOAD_FAILED                      : -250,
    SPECIFIED_FILE_ID_NOT_FOUND        : -260,
    FILE_VALIDATION_FAILED              : -270,
    FILE_CANCELLED                      : -280,
    UPLOAD_STOPPED                    : -290
};
Ext.ux.FileUpload.FILE_STATUS = {
    QUEUED         : -1,
    IN_PROGRESS     : -2,
    ERROR         : -3,
    COMPLETE     : -4,
    CANCELLED     : -5
};  