"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var react_1 = tslib_1.__importDefault(require("react"));
var Item_1 = require("./Item");
var classnames_1 = tslib_1.__importDefault(require("classnames"));
var qs_1 = tslib_1.__importDefault(require("qs"));
var find = require("lodash/find");
var isPlainObject = require("lodash/isPlainObject");
var async_1 = require("async");
var Image_1 = tslib_1.__importDefault(require("./Image"));
var tpl_1 = require("../../utils/tpl");
;
;
;
var FileControl = /** @class */ (function (_super) {
    tslib_1.__extends(FileControl, _super);
    function FileControl(props) {
        var _this = _super.call(this, props) || this;
        var value = props.value;
        var multiple = props.multiple;
        var joinValues = props.joinValues;
        var delimiter = props.delimiter;
        var files = [];
        if (value) {
            files = (Array.isArray(value) ? value : joinValues ? (value.value || value).split(delimiter) : [(value.value || value)])
                .map(function (item) { return FileControl.valueToFile(item, props); })
                .filter(function (item) { return item; });
        }
        _this.state = {
            files: files,
            uploading: false
        };
        _this.sendFile = _this.sendFile.bind(_this);
        _this.removeFile = _this.removeFile.bind(_this);
        _this.clearError = _this.clearError.bind(_this);
        _this.handleDrop = _this.handleDrop.bind(_this);
        _this.startUpload = _this.startUpload.bind(_this);
        _this.stopUpload = _this.stopUpload.bind(_this);
        _this.toggleUpload = _this.toggleUpload.bind(_this);
        _this.tick = _this.tick.bind(_this);
        _this.onChange = _this.onChange.bind(_this);
        _this.uploadFile = _this.uploadFile.bind(_this);
        _this.uploadBigFile = _this.uploadBigFile.bind(_this);
        return _this;
    }
    FileControl.valueToFile = function (value, props, files) {
        var file = files && typeof value === 'string'
            ? find(files, function (item) { return item.value === value; })
            : undefined;
        return value ? tslib_1.__assign({}, (typeof value === 'string' ? {
            state: file && file.state ? file.state : 'init',
            value: value,
            name: /^data:/.test(value) ? (file && file.name || 'base64数据') : '',
            url: typeof props.downloadUrl === 'string' && value && !/^data:/.test(value) ? "" + props.downloadUrl + value : undefined
        } : value)) : undefined;
    };
    FileControl.prototype.componentWillReceiveProps = function (nextProps) {
        var _this = this;
        var props = this.props;
        if (props.value !== nextProps.value) {
            var value = nextProps.value;
            var multiple = nextProps.multiple;
            var joinValues = nextProps.joinValues;
            var delimiter = nextProps.delimiter;
            var files = [];
            if (value) {
                files = (Array.isArray(value) ? value : joinValues && typeof value === 'string' ? value.split(delimiter) : [value])
                    .map(function (item) {
                    var obj = FileControl.valueToFile(item, nextProps, _this.state.files);
                    var org;
                    if (obj && (org = find(_this.state.files, function (item) { return item.value === obj.value; }))) {
                        obj = tslib_1.__assign({}, org, obj);
                    }
                    return obj;
                })
                    .filter(function (item) { return item; });
            }
            this.setState({
                files: files,
            });
        }
    };
    FileControl.prototype.handleDrop = function (e) {
        var _this = this;
        var files = e.currentTarget.files;
        if (!files.length) {
            return;
        }
        var _a = this.props, maxSize = _a.maxSize, multiple = _a.multiple, maxLength = _a.maxLength;
        var allowed = (multiple ? maxLength ? maxLength : (files.length + this.state.files.length) : 1) - this.state.files.length;
        var inputFiles = [];
        [].slice.call(files, 0, allowed).forEach(function (file) {
            if (maxSize && file.size > maxSize) {
                alert("\u60A8\u9009\u62E9\u7684\u6587\u4EF6 " + file.name + " \u5927\u5C0F\u4E3A " + Image_1.default.formatFileSize(file.size) + " \u8D85\u51FA\u4E86\u6700\u5927\u4E3A " + Image_1.default.formatFileSize(maxSize) + " \u7684\u9650\u5236\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9");
                return;
            }
            file.state = 'pending';
            inputFiles.push(file);
        });
        if (!inputFiles.length) {
            return;
        }
        this.setState({
            error: null,
            files: this.state.files.concat(inputFiles)
        }, function () {
            var autoUpload = _this.props.autoUpload;
            if (autoUpload) {
                _this.startUpload();
            }
        });
    };
    FileControl.prototype.startUpload = function () {
        if (this.state.uploading) {
            return;
        }
        this.setState({
            uploading: true,
            files: this.state.files.map(function (file) {
                if (file.state === 'error') {
                    file.state = 'pending';
                }
                return file;
            })
        }, this.tick);
    };
    FileControl.prototype.toggleUpload = function (e) {
        e.preventDefault();
        return this.state.uploading ? this.stopUpload() : this.startUpload();
    };
    FileControl.prototype.stopUpload = function () {
        if (!this.state.uploading) {
            return;
        }
        this.setState({
            uploading: false
        });
    };
    FileControl.prototype.tick = function () {
        var _this = this;
        if (this.current || !this.state.uploading) {
            return;
        }
        var file = find(this.state.files, function (item) { return item.state === 'pending'; });
        if (file) {
            this.current = file;
            file.state = 'uploading';
            this.setState({
                files: this.state.files.concat()
            }, function () { return _this.sendFile(file, function (error, file, obj) {
                var files = _this.state.files.concat();
                var idx = files.indexOf(file);
                if (!~idx) {
                    return;
                }
                var newFile = file;
                if (error) {
                    newFile.state = 'error';
                    newFile.error = error;
                }
                else {
                    newFile = obj;
                }
                files.splice(idx, 1, newFile);
                _this.current = null;
                _this.setState({
                    files: files
                }, _this.tick);
            }); });
        }
        else {
            this.setState({
                uploading: false
            }, function () {
                _this.onChange();
                if (_this.resolve) {
                    _this.resolve(_this.state.files.some(function (file) { return file.state === 'error'; }) ? '文件上传失败请重试' : null);
                    _this.resolve = undefined;
                }
            });
        }
    };
    FileControl.prototype.sendFile = function (file, cb) {
        var _a = this.props, reciever = _a.reciever, fileField = _a.fileField, downloadUrl = _a.downloadUrl, useChunk = _a.useChunk, chunkSize = _a.chunkSize, startChunkApi = _a.startChunkApi, chunkApi = _a.chunkApi, finishChunkApi = _a.finishChunkApi, asBase64 = _a.asBase64;
        if (asBase64) {
            var reader_1 = new FileReader();
            reader_1.readAsDataURL(file);
            reader_1.onload = function () {
                cb(null, file, {
                    value: reader_1.result,
                    name: file.name,
                    url: '',
                    state: 'uploaded'
                });
            };
            reader_1.onerror = function (error) { return cb(error.message); };
            return;
        }
        var fn = useChunk === 'auto' && chunkSize && file.size > chunkSize || useChunk === true ? this.uploadBigFile : this.uploadFile;
        fn(file, reciever, {}, {
            fieldName: fileField,
            chunkSize: chunkSize,
            startChunkApi: startChunkApi,
            chunkApi: chunkApi,
            finishChunkApi: finishChunkApi
        })
            .then(function (ret) {
            if (ret.status || !ret.data) {
                throw new Error(ret.msg || '上传失败, 请重试');
            }
            var value = ret.data.value || ret.data;
            cb(null, file, tslib_1.__assign({}, isPlainObject(ret.data) ? ret.data : null, { value: value, url: typeof downloadUrl === 'string' && value ? "" + downloadUrl + value : ret.data ? ret.data.url : null, state: 'uploaded' }));
        })
            .catch(function (error) {
            cb(error.message || '上传失败, 请重试', file);
        });
    };
    FileControl.prototype.removeFile = function (file, index) {
        var files = this.state.files.concat();
        files.splice(index, 1);
        this.setState({
            files: files
        }, this.onChange);
    };
    FileControl.prototype.clearError = function () {
        this.setState({
            error: null
        });
    };
    FileControl.prototype.onChange = function () {
        var _a = this.props, multiple = _a.multiple, onChange = _a.onChange, joinValues = _a.joinValues, extractValue = _a.extractValue, valueField = _a.valueField, delimiter = _a.delimiter, resetValue = _a.resetValue;
        var files = this.state.files.filter(function (file) { return file.state == 'uploaded' || file.state == 'init'; });
        var value = multiple ? files : files[0];
        if (value) {
            if (joinValues) {
                value = Array.isArray(value) ? value.map(function (item) { return item[valueField || 'value']; }).join(delimiter || ',') : value[valueField || 'value'];
            }
            else if (extractValue) {
                value = Array.isArray(value) ? value.map(function (item) { return item[valueField || 'value']; }) : value[valueField || 'value'];
            }
        }
        else {
            value = typeof resetValue === 'undefined' ? '' : resetValue;
        }
        onChange(value);
    };
    FileControl.prototype.uploadFile = function (file, reciever, params, config) {
        if (config === void 0) { config = {}; }
        var fd = new FormData();
        reciever = tpl_1.filter(reciever, this.props.data);
        if (/^\/api\/(?:page\/)?proxy/.test(reciever)) {
            fd.append('file', file);
            fd.append('fieldName', config.fieldName);
        }
        else {
            fd.append(config.fieldName || 'file', file);
        }
        var idx = reciever.indexOf('?');
        if (~idx && params) {
            params = tslib_1.__assign({}, qs_1.default.parse(reciever.substring(idx + 1)), params);
            reciever = reciever.substring(0, idx) + '?' + qs_1.default.stringify(params);
        }
        else if (params) {
            reciever += '?' + qs_1.default.stringify(params);
        }
        return this._send(reciever, fd, {
            withCredentials: true
        });
    };
    FileControl.prototype.uploadBigFile = function (file, reciever, params, config) {
        if (config === void 0) { config = {}; }
        var chunkSize = config.chunkSize || 5 * 1024 * 1024;
        var self = this;
        ;
        ;
        return new Promise(function (resolve, reject) {
            var state;
            self._send(config.startChunkApi, { filename: file.name })
                .then(startChunk)
                .catch(reject);
            function startChunk(ret) {
                var tasks = getTasks(file);
                if (!ret.data) {
                    throw new Error('接口返回错误，请仔细检查');
                }
                state = {
                    key: ret.data.key,
                    uploadId: ret.data.uploadId,
                    loaded: 0,
                    total: tasks.length
                };
                async_1.mapLimit(tasks, 3, uploadPartFile(state, config), function (err, results) {
                    if (err) {
                        reject(err);
                    }
                    else {
                        finishChunk(results, state);
                    }
                });
            }
            function finishChunk(partList, state) {
                self._send(config.finishChunkApi, tslib_1.__assign({}, params, { uploadId: state.uploadId, key: state.key, filename: file.name, partList: partList })).then(resolve).catch(reject);
            }
            function uploadPartFile(state, conf) {
                reciever = conf.chunkApi;
                return function (task, callback) {
                    var fd = new FormData();
                    var blob = task.file.slice(task.start, task.stop + 1);
                    fd.append('key', state.key);
                    fd.append('uploadId', state.uploadId);
                    fd.append('partNumber', task.partNumber.toString());
                    fd.append('partSize', task.partSize.toString());
                    if (/^\/api\/(?:page\/)?proxy/.test(reciever)) {
                        fd.append('file', blob, file.name);
                        fd.append('fieldName', config.fieldName);
                    }
                    else {
                        fd.append(config.fieldName || 'file', blob, file.name);
                    }
                    return self._send(reciever, fd, {
                        withCredentials: true
                    })
                        .then(function (ret) {
                        state.loaded++;
                        callback(null, {
                            partNumber: task.partNumber,
                            eTag: ret.data.eTag
                        });
                    })
                        .catch(callback);
                };
            }
            function getTasks(file) {
                var leftSize = file.size;
                var offset = 0;
                var partNumber = 1;
                var tasks = [];
                while (leftSize > 0) {
                    var partSize = Math.min(leftSize, chunkSize);
                    tasks.push({
                        file: file,
                        partNumber: partNumber,
                        partSize: partSize,
                        start: offset,
                        stop: offset + partSize - 1
                    });
                    leftSize -= partSize;
                    offset += partSize;
                    partNumber += 1;
                }
                return tasks;
            }
        });
    };
    FileControl.prototype._send = function (reciever, data, options) {
        var env = this.props.env;
        if (!env || !env.fetcher) {
            throw new Error('fetcher is required');
        }
        reciever = tpl_1.filter(reciever, this.props.data);
        return env.fetcher(reciever, data, tslib_1.__assign({ method: 'post' }, options));
    };
    FileControl.prototype.validate = function () {
        var _this = this;
        if (this.state.uploading || this.state.files.some(function (item) { return item.state === 'pending'; })) {
            return new Promise(function (resolve) {
                _this.resolve = resolve;
                _this.startUpload();
            });
        }
        else if (this.state.files.some(function (item) { return item.state === 'error'; })) {
            return '文件上传失败请重试';
        }
    };
    FileControl.prototype.render = function () {
        var _this = this;
        var _a = this.props, btnLabel = _a.btnLabel, accept = _a.accept, disabled = _a.disabled, btnClassName = _a.btnClassName, btnUploadClassName = _a.btnUploadClassName, maxLength = _a.maxLength, multiple = _a.multiple, autoUpload = _a.autoUpload, stateTextMap = _a.stateTextMap, hideUploadButton = _a.hideUploadButton, className = _a.className;
        var _b = this.state, files = _b.files, uploading = _b.uploading, error = _b.error;
        var hasPending = files.some(function (file) { return file.state == 'pending'; });
        return (react_1.default.createElement("div", { className: classnames_1.default('amis-file-control', className) },
            error ? (react_1.default.createElement("div", null,
                react_1.default.createElement("p", { className: "help-block text-danger inline" }, error),
                react_1.default.createElement("a", { className: "btn btn-link", onClick: this.clearError },
                    react_1.default.createElement("i", { className: "fa fa-times" })))) : null,
            files && files.length ? (react_1.default.createElement("ul", { className: "list-group no-bg m-b-sm" }, files.map(function (file, key) { return (react_1.default.createElement("li", { key: key, className: "list-group-item clearfix" },
                react_1.default.createElement("a", { className: "text-danger pull-right", onClick: function () { return _this.removeFile(file, key); } },
                    react_1.default.createElement("i", { className: "fa fa-times" })),
                react_1.default.createElement("span", { className: "pull-right text-muted text-xs m-r-sm" }, stateTextMap && stateTextMap[file.state] || ''),
                react_1.default.createElement("i", { className: "fa fa-file fa-fw m-r-xs" }),
                file.url ? (react_1.default.createElement("a", { href: file.url, target: "_blank" }, file.name || file.filename || file.value)) : (react_1.default.createElement("span", null, file.name || file.filename)))); }))) : null,
            react_1.default.createElement("div", { className: "clear" },
                multiple && (!maxLength || files.length < maxLength) || !multiple && !files.length ? (react_1.default.createElement("label", { className: classnames_1.default("btn m-r-xs", btnClassName, { disabled: disabled }) },
                    react_1.default.createElement("input", { type: "file", accept: accept, disabled: disabled, multiple: multiple, className: "invisible", onChange: this.handleDrop }),
                    btnLabel)) : null,
                !autoUpload && !hideUploadButton && files.length ? (react_1.default.createElement("button", { type: "button", className: classnames_1.default('btn m-r-xs', btnUploadClassName), disabled: !hasPending, onClick: this.toggleUpload }, uploading ? '暂停上传' : '开始上传')) : null,
                this.state.uploading ? (react_1.default.createElement("i", { className: "fa fa-spinner fa-spin fa-2x fa-fw" })) : null)));
    };
    FileControl.defaultProps = {
        btnClassName: 'btn-sm btn-info',
        btnUploadClassName: 'btn-sm btn-success',
        maxSize: 0,
        maxLength: 0,
        placeholder: '',
        btnLabel: '请选择文件',
        reciever: '/api/upload/file',
        fileField: 'file',
        joinValues: true,
        extractValue: false,
        delimiter: ',',
        downloadUrl: '',
        useChunk: 'auto',
        chunkSize: 5 * 1024 * 1024,
        startChunkApi: '/api/upload/startChunk',
        chunkApi: '/api/upload/chunk',
        finishChunkApi: '/api/upload/finishChunk',
        accept: 'text/plain',
        multiple: false,
        autoUpload: true,
        hideUploadButton: false,
        stateTextMap: {
            'init': "",
            'pending': "等待上传",
            'uploading': "上传中",
            'error': "上传出错",
            'uploaded': "已上传"
        },
        asBase64: false
    };
    return FileControl;
}(react_1.default.Component));
exports.default = FileControl;
var FileControlRenderer = /** @class */ (function (_super) {
    tslib_1.__extends(FileControlRenderer, _super);
    function FileControlRenderer() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    FileControlRenderer = tslib_1.__decorate([
        Item_1.FormItem({
            type: 'file',
            sizeMutable: false
        })
    ], FileControlRenderer);
    return FileControlRenderer;
}(FileControl));
exports.FileControlRenderer = FileControlRenderer;
;
//# sourceMappingURL=./renderers/Form/File.js.map
