/*
 * jQuery File Upload File Processing Plugin 1.2
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2012, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/*jslint nomen: true, unparam: true, regexp: true */
/*global define, window, document */

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define([
            'jquery',
            'load-image',
            'canvas-to-blob',
            './jquery.fileupload'
        ], factory);
    } else {
        // Browser globals:
        factory(
            window.jQuery,
            window.loadImage
        );
    }
}(function ($, loadImage) {
    'use strict';

    // The File Upload FP version extends the fileupload widget
    // with file processing functionality:
    $.widget('blueimp.fileupload', $.blueimp.fileupload, {

        options: {
            // The list of file processing actions:
            process: [
            /*
                {
                    action: 'load',
                    fileTypes: /^image\/(gif|jpeg|png)$/,
                    maxFileSize: 20000000 // 20MB
                },
                {
                    action: 'resize',
                    maxWidth: 1920,
                    maxHeight: 1200,
                    minWidth: 800,
                    minHeight: 600
                },
                {
                    action: 'save'
                }
            */
            ],

            // The add callback is invoked as soon as files are added to the
            // fileupload widget (via file input selection, drag & drop or add
            // API call). See the basic file upload widget for more information:
            add: function (e, data) {
                $(this).fileupload('process', data).done(function () {
                    data.submit();
                });
            }
        },

        processActions: {
            // Loads the image given via data.files and data.index
            // as img element if the browser supports canvas.
            // Accepts the options fileTypes (regular expression)
            // and maxFileSize (integer) to limit the files to load:
            load: function (data, options) {
                var that = this,
                    file = data.files[data.index],
                    dfd = $.Deferred();
                if (window.HTMLCanvasElement &&
                        window.HTMLCanvasElement.prototype.toBlob &&
                        ($.type(options.maxFileSize) !== 'number' ||
                            file.size < options.maxFileSize) &&
                        (!options.fileTypes ||
                            options.fileTypes.test(file.type))) {
                    loadImage(
                        file,
                        function (img) {
                            data.img = img;
                            dfd.resolveWith(that, [data]);
                        }
                    );
                } else {
                    dfd.rejectWith(that, [data]);
                }
                return dfd.promise();
            },
            // Resizes the image given as data.img and updates
            // data.canvas with the resized image as canvas element.
            // Accepts the options maxWidth, maxHeight, minWidth and
            // minHeight to scale the given image:
            resize: function (data, options) {
                var img = data.img,
                    canvas;
                options = $.extend({canvas: true}, options);
                if (img) {
                    canvas = loadImage.scale(img, options);
                    if (canvas.width !== img.width ||
                            canvas.height !== img.height) {
                        data.canvas = canvas;
                    }
                }
                return data;
            },
            // Saves the processed image given as data.canvas
            // inplace at data.index of data.files:
            save: function (data, options) {
                // Do nothing if no processing has happened:
                if (!data.canvas) {
                    return data;
                }
                var that = this,
                    file = data.files[data.index],
                    name = file.name,
                    dfd = $.Deferred(),
                    callback = function (blob) {
                        if (!blob.name) {
                            if (file.type === blob.type) {
                                blob.name = file.name;
                            } else if (file.name) {
                                blob.name = file.name.replace(
                                    /\..+$/,
                                    '.' + blob.type.substr(6)
                                );
                            }
                        }
                        // Store the created blob at the position
                        // of the original file in the files list:
                        data.files[data.index] = blob;
                        dfd.resolveWith(that, [data]);
                    };
                // Use canvas.mozGetAsFile directly, to retain the filename, as
                // Gecko doesn't support the filename option for FormData.append:
                if (data.canvas.mozGetAsFile) {
                    callback(data.canvas.mozGetAsFile(
                        (/^image\/(jpeg|png)$/.test(file.type) && name) ||
                            ((name && name.replace(/\..+$/, '')) ||
                                'blob') + '.png',
                        file.type
                    ));
                } else {
                    data.canvas.toBlob(callback, file.type);
                }
                return dfd.promise();
            }
        },

        // Resizes the file at the given index and stores the created blob at
        // the original position of the files list, returns a Promise object:
        _processFile: function (files, index, options) {
            var that = this,
                dfd = $.Deferred().resolveWith(that, [{
                    files: files,
                    index: index
                }]),
                chain = dfd.promise();
            that._processing += 1;
            $.each(options.process, function (i, settings) {
                chain = chain.pipe(function (data) {
                    return that.processActions[settings.action]
                        .call(this, data, settings);
                });
            });
            chain.always(function () {
                that._processing -= 1;
                if (that._processing === 0) {
                    that.element
                        .removeClass('fileupload-processing');
                }
            });
            if (that._processing === 1) {
                that.element.addClass('fileupload-processing');
            }
            return chain;
        },

        // Processes the files given as files property of the data parameter,
        // returns a Promise object that allows to bind a done handler, which
        // will be invoked after processing all files (inplace) is done:
        process: function (data) {
            var that = this,
                options = $.extend({}, this.options, data);
            if (options.process && options.process.length &&
                    this._isXHRUpload(options)) {
                $.each(data.files, function (index, file) {
                    that._processingQueue = that._processingQueue.pipe(
                        function () {
                            var dfd = $.Deferred();
                            that._processFile(data.files, index, options)
                                .always(function () {
                                    dfd.resolveWith(that);
                                });
                            return dfd.promise();
                        }
                    );
                });
            }
            return this._processingQueue;
        },

        _create: function () {
            this._super();
            this._processing = 0;
            this._processingQueue = $.Deferred().resolveWith(this)
                .promise();
        }

    });

}));
