/**
 * @fileOverview 各种验证，包括文件总大小是否超出、单文件是否超出和文件是否重复。
 */

define([
    '../base',
    '../uploader',
    '../file',
    './widget'
], function( Base, Uploader, WUFile ) {

    var $ = Base.$,
        validators = {},
        api;

    /**
     * @event error
     * @param {String} type 错误类型。
     * @description 当validate不通过时，会以派送错误事件的形式通知调用者。通过`upload.on('error', handler)`可以捕获到此类错误，目前有以下错误会在特定的情况下派送错来。
     *
     * * `Q_EXCEED_NUM_LIMIT` 在设置了`fileNumLimit`且尝试给`uploader`添加的文件数量超出这个值时派送。
     * * `Q_EXCEED_SIZE_LIMIT` 在设置了`Q_EXCEED_SIZE_LIMIT`且尝试给`uploader`添加的文件总大小超出这个值时派送。
     * * `Q_TYPE_DENIED` 当文件类型不满足时触发。。
     * @for  Uploader
     */

    // 暴露给外面的api
    api = {

        // 添加验证器
        addValidator: function( type, cb ) {
            validators[ type ] = cb;
        },

        // 移除验证器
        removeValidator: function( type ) {
            delete validators[ type ];
        }
    };

    // 在Uploader初始化的时候启动Validators的初始化
    Uploader.register({
        name: 'validator',

        init: function() {
            var me = this;
            Base.nextTick(function() {
                $.each( validators, function() {
                    this.call( me.owner );
                });
            });
        }
    });

    /**
     * @property {int} [fileNumLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证文件总数量, 超出则不允许加入队列。
     */
    api.addValidator( 'fileNumLimit', function() {
        var uploader = this,
            opts = uploader.options,
            count = 0,
            max = parseInt( opts.fileNumLimit, 10 ),
            flag = true;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {

            if ( count >= max && flag ) {
                flag = false;
                this.trigger( 'error', 'Q_EXCEED_NUM_LIMIT', max, file );
                setTimeout(function() {
                    flag = true;
                }, 1 );
            }

            return count >= max ? false : true;
        });

        uploader.on( 'fileQueued', function() {
            count++;
        });

        uploader.on( 'fileDequeued', function() {
            count--;
        });

        uploader.on( 'reset', function() {
            count = 0;
        });
    });


    /**
     * @property {int} [fileSizeLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证文件总大小是否超出限制, 超出则不允许加入队列。
     */
    api.addValidator( 'fileSizeLimit', function() {
        var uploader = this,
            opts = uploader.options,
            count = 0,
            max = parseInt( opts.fileSizeLimit, 10 ),
            flag = true;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {
            var invalid = count + file.size > max;

            if ( invalid && flag ) {
                flag = false;
                this.trigger( 'error', 'Q_EXCEED_SIZE_LIMIT', max, file );
                setTimeout(function() {
                    flag = true;
                }, 1 );
            }

            return invalid ? false : true;
        });

        uploader.on( 'fileQueued', function( file ) {
            count += file.size;
        });

        uploader.on( 'fileDequeued', function( file ) {
            count -= file.size;
        });

        uploader.on( 'reset', function() {
            count = 0;
        });
    });

    /**
     * @property {int} [fileSingleSizeLimit=undefined]
     * @namespace options
     * @for Uploader
     * @description 验证单个文件大小是否超出限制, 超出则不允许加入队列。
     */
    api.addValidator( 'fileSingleSizeLimit', function() {
        var uploader = this,
            opts = uploader.options,
            max = opts.fileSingleSizeLimit;

        if ( !max ) {
            return;
        }

        uploader.on( 'beforeFileQueued', function( file ) {

            if ( file.size > max ) {
                file.setStatus( WUFile.Status.INVALID, 'exceed_size' );
                this.trigger( 'error', 'F_EXCEED_SIZE', max, file );
                return false;
            }

        });

    });

    /**
     * @property {Boolean} [duplicate=undefined]
     * @namespace options
     * @for Uploader
     * @description 去重， 根据文件名字、文件大小和最后修改时间来生成hash Key.
     */
    api.addValidator( 'duplicate', function() {
        var uploader = this,
            opts = uploader.options,
            mapping = {};

        if ( opts.duplicate ) {
            return;
        }

        function hashString( str ) {
            var hash = 0,
                i = 0,
                len = str.length,
                _char;

            for ( ; i < len; i++ ) {
                _char = str.charCodeAt( i );
                hash = _char + (hash << 6) + (hash << 16) - hash;
            }

            return hash;
        }

        uploader.on( 'beforeFileQueued', function( file ) {
            var hash = file.__hash || (file.__hash = hashString( file.name +
                    file.size + file.lastModifiedDate ));

            // 已经重复了
            if ( mapping[ hash ] ) {
                this.trigger( 'error', 'F_DUPLICATE', file );
                return false;
            }
        });

        uploader.on( 'fileQueued', function( file ) {
            var hash = file.__hash;

            hash && (mapping[ hash ] = true);
        });

        uploader.on( 'fileDequeued', function( file ) {
            var hash = file.__hash;

            hash && (delete mapping[ hash ]);
        });

        uploader.on( 'reset', function() {
            mapping = {};
        });
    });

    return api;
});
