/// class PD.Web.UI.Controls.Upload
/// using PD.Web.UI.Controls.Control

PD.Web.UI.Controls.Upload =
PD.Web.UI.Controls.Control.extend({

    //===========================
    // CONSTANTS
    //===========================

    FILE_STATUS: {
        QUEUED: 1,
        UPLOADING: 2,
        FAILED: 4,
        DONE: 5
    },

    //===========================
    // FIELDS
    //===========================

    options: null,
    uploader: null,

    //===========================
    // INITIALIZERS
    //===========================

    init: function (options) {
        
        this.options = new PD.Options({
            onError: this._onError,
            onFileAdded: this._onFileAdded,
            onFileUpload: this._onFileUpload,
            onFileUploaded: this._onFileUploaded,
            onUploadStart: this._onUploadStart,
            onUploadComplete: this._onUploadComplete,
            onUploadProgress: this._onUploadProgress,
            onQueueChanged: this._onQueueChanged,
            onStateChanged: this._onStateChanged,
            uploadUrl: "~/application/handlers/Uploads.ashx",
            max_files: 25
        });

        this.options.merge(options);

        this._super(options);

    },

    //===========================
    // EVENT HANDLERS
    //===========================

    _onError: function (sender, e) {

        e.error = this._err(e);

        this._invokeEventCallback("onError", e);

    },

    _onFileAdded: function (sender, e) {

        var that = this,
            uploader = this.uploader,
            maxFiles = this.options.get("max_files", 25);

        if (e.length > maxFiles) {
            e.splice(maxFiles);
            this._onError(this, { code: 2 }); // max files exceeded;
        }

        _.each(e, function (file) {
            if (file.size == null || file.size === 0) {
                that._onError(this, { code: 3, file: file });
            }
        });

        this._invokeEventCallback("onFileAdded", e);

    },

    _onFileUpload: function (sender, e) {
        this._invokeEventCallback("onFileUpload", e);
    },

    _onFileUploaded: function (sender, e) {
        this._invokeEventCallback("onFileUploaded", e);
    },

    _onUploadStart: function (sender, e) {

        var isCancelled = false,
            cancel = function () {
                isCancelled = true;
            };
        
        if (this.uploader.files.length === 0) {
            isCancelled = true;
            this._onError(this, {code: 1})
        }

        e.cancel = cancel;

        if (!isCancelled) {
            this._invokeEventCallback("onUploadStart", e);
        }

        if (!isCancelled) {
            this.uploader.start();
        }

    },

    _onUploadComplete: function (sender, e) {
        this._invokeEventCallback("onUploadComplete", e);
    },

    _onUploadProgress: function (sender, e) {

        var filesCount = this.uploader.files.length,
            filesUploadedCount = this.getCountOfUploadedFiles(),
            filePercentOf100 = (100 / filesCount) * .01,
            filePercent = e.percent * .01,
            filesPercent = 100 / filesCount,
            percentTotal = Math.round(((filePercentOf100 * filesUploadedCount) + (filePercentOf100 * filePercent)) * 100);

        e.percentTotal = percentTotal === isNaN ? 0 : percentTotal;

        this._invokeEventCallback("onUploadProgress", e);

    },

    _onQueueChanged: function (sender, e) {
        this._invokeEventCallback("onQueueChanged", e);
    },

    _onStateChanged: function (sender, e) {
        this._invokeEventCallback("onStateChanged", e);
    },

    onActivate: function () {
        this.initComponent();
    },

    remove: function () {
        if (this.uploader) {
            this.uploader.destroy();
        }
    },

    //===========================
    // PUBLIC METHODS
    //===========================

    initComponent: function () {

        var options = this.options,
            uploader = null;

        if (this.uploader != null) {
            return;
        }

        this.uploader = new plupload.Uploader({
            runtimes: "html5,silverlight,flash,html4",
            browse_button: options.get("browse_button", undefined),
            browse_button_hover: options.get("browse_button_hover", undefined),
            browse_button_active: options.get("browse_button_active", undefined),
            container: options.get("container", undefined),
            urlstream_upload: true,
            max_file_size: "2gb",
            chunk_size: "1024kb",
            unique_names: true,
            multi_selection: options.get("multi_selection", true),
            filters: options.get("filters", []),
            url: PD.Path.resolve(options.get("uploadUrl")),
            flash_swf_url: PD.Path.resolve("~/areas/shared/assets/external/misc/plupload/moxie.swf"),
            silverlight_xap_url: PD.Path.resolve("~/areas/shared/assets/external/misc/plupload/moxie.xap")
        });

        uploader = this.uploader;

        uploader.bind('Error', _.bind(this._onError, this));
        uploader.bind('FilesAdded', _.bind(this._onFileAdded, this));
        uploader.bind('BeforeUpload', _.bind(this._onFileUpload, this));
        uploader.bind('FileUploaded', _.bind(this._onFileUploaded, this));
        uploader.bind('UploadComplete', _.bind(this._onUploadComplete, this));
        uploader.bind('UploadProgress', _.bind(this._onUploadProgress, this));
        uploader.bind('QueueChanged', _.bind(this._onQueueChanged, this));
        uploader.bind('StateChanged', _.bind(this._onStateChanged, this));
        
        uploader.init();
        
    },

    getCountOfUploadedFiles: function () {
        
        var FAILED = this.FILE_STATUS.FAILED;
        var DONE = this.FILE_STATUS.DONE;

        return _.countBy(this.uploader.files, function (item) {
            return (item.status ===  FAILED || item.status === DONE) ? "complete" : "pending"
        }).complete;

    },

    start: function () {
        this._onUploadStart(this, {});
    },

    //===========================
    // PRIVATE METHODS
    //===========================
    
    _invokeEventCallback: function (eventCallbackName, eventArgs) {

        var cb = this.options.get(eventCallbackName),
            eventHandler = this["_" + eventCallbackName];

        if (_.isFunction(cb) && cb != eventHandler) {
            cb(eventArgs);
        }

    },

    _getFilteredExtensions: function () {

        var result = [],
            filters = this.options.get("filters", []);

        _.each(filters, function (filter) {
            result[result.length] = filter.extensions;
        });

        return result.join(", ");

    },

    //===========================
    // ERROR HANDLERS
    //===========================

    _err: function (e) {
        
        var result = null;

        this.uploader.stop();

        if (e.status === 400) {
            result = this._err_400(e);
        }
        else {

            switch (e.code) {
                case -601:
                    result = this._err_invalid_extension(e);
                    break;
                case 600:
                    result = this._err_file_size_limit_exceeded(e);
                    break;
                case -300:
                    result = this._err_cannot_read_file(e);
                    break;
                case 1:
                    result = this._err_no_files_selected(e);
                    break;
                case 2:
                    result = this._err_file_upload_limit_exceeded(e);
                    break;
                case 3:
                    result = this._err_zero_byte_file(e);
                    break;
                default:
                    result = this._err_default(e);
                    break;
            }

        }
        

        return result;

    },

    _err_default: function (e) {
        return {
            code: e.code,
            message: e.message,
            title: "Upload Error"
        };
    },

    _err_invalid_extension: function (e) {
        return {
            code: e.code,
            message: "Only files with <b>" + this._getFilteredExtensions() + "</b> extension(s) may be uploaded.",
            title: "Invalid File Extension"
        };
    },

    _err_cannot_read_file: function (e) {
        return {
            code: e.code,
            message: "The file \"" + e.file.name + "\" is being used by another process and cannot be uploaded.<br/><br/>This may be due to the file be open for editing in another program.",
            title: "Cannot Read File"
        };
    },

    _err_no_files_selected: function (e) {
        return {
            code: e.code,
            message: "No files have been selected for upload.",
            title: "No Files Selected"
        };
    },

    _err_file_upload_limit_exceeded: function (e) {
        return {
            code: e.code,
            message: "More files than are allowed have been selected for upload. Not all the files you selected have been added to the queue.",
            title: "File Upload Limit Exceeded"
        };
    },

    _err_zero_byte_file: function (e) {

        var that = this;

        this.uploader.removeFile(e.file);

        setTimeout(function () {
            that._invokeEventCallback("onFileAdded", that.uploader.files);
        }, 250);

        return {
            code: e.code,
            message: "The file \"" + e.file.name + "\" is zero bytes in size, which cannot be uploaded and has been automatically removed. Please attempt your upload again.",
            title: "Zero Byte File Detected"
        };

    },

    _err_file_size_limit_exceeded: function (e) {

        return {
            code: e.code,
            message: "The file \"" + e.file.name + "\" is exceeds the maximum allowed size of 2 GB and cannot be uploaded.",
            title: "File Size Exceeded"
        };

    },

    _err_400: function (e) {

        var result = null,
            response = null;

        try {

            response = JSON.parse(e.response);

            result = {
                title: "Upload Error",
                code: e.code,
                message: response.error.message
            };

        }
        catch (ex) {
            result = this._err_default(e)
        }
        
        return result;

    }

});



PD.Options = Class.extend({

    //===========================
    // PRIVATE METHODS
    //===========================

    _inner: null,

    //===========================
    // CONSTRUCTOR
    //===========================

    init: function (defaults) {
        if (_.isPlainObject(defaults)) {
            this._inner = defaults;
        }
        else {
            this._inner = {};
        }
    },

    //===========================
    // PRIVATE METHODS
    //===========================

    get: function (name, defaultValue) {
        return _.extract(this._inner, name, defaultValue);
    },

    set: function (name, value) {
        _.extrude(this._inner, name).set(value);
    },

    merge: function (deep, obj) {
    
        var deepCopy = false,
            target = deep;

        if (_.isBoolean(deep)) {
            deepCopy = deep;
            target = obj;
        } 

        if (deepCopy) {
            this._inner = _.deepExtend(this._inner, target);
        }
        else {
            this._inner = _.extend(this._inner, target);
        }

    }

});