import { __assign, __awaiter, __generator } from "tslib";
import axios from 'axios';
import Vue from 'vue';
import uuid from '../uuid/uuid';
export var DEFAULT_STORE_NAME = 'DEFAULT';
export var MFileRejectionCause;
(function (MFileRejectionCause) {
    MFileRejectionCause["FILE_SIZE"] = "file-size";
    MFileRejectionCause["FILE_TYPE"] = "file-type";
    MFileRejectionCause["MAX_FILES"] = "max-files";
    MFileRejectionCause["CUSTOM_VALIDATION"] = "custom-validation";
})(MFileRejectionCause || (MFileRejectionCause = {}));
export var MFileStatus;
(function (MFileStatus) {
    MFileStatus["READY"] = "ready";
    MFileStatus["UPLOADING"] = "uploading";
    MFileStatus["COMPLETED"] = "completed";
    MFileStatus["FAILED"] = "failed";
    MFileStatus["REJECTED"] = "rejected";
    MFileStatus["CANCELED"] = "canceled";
})(MFileStatus || (MFileStatus = {}));
var FileService = /** @class */ (function () {
    function FileService() {
        this.stores = {};
    }
    FileService.prototype.files = function (storeName) {
        return this.getStore(storeName).files;
    };
    FileService.prototype.setValidationOptions = function (options, storeName) {
        this.getStore(storeName).validationOptions = options;
    };
    FileService.prototype.add = function (files, storeName) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.getStore(storeName).add(files)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    FileService.prototype.addSingleFile = function (file, storeName) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.getStore(storeName).addSingleFile(file)];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    FileService.prototype.remove = function (fileuid, storeName) {
        this.getStore(storeName).remove(fileuid);
    };
    FileService.prototype.clear = function (storeName) {
        this.getStore(storeName).clear();
    };
    FileService.prototype.destroy = function (storeName) {
        this.getStore(storeName).destroy();
        delete this.stores[this.getStoreName(storeName)];
    };
    FileService.prototype.upload = function (fileuid, options, storeName) {
        return this.getStore(storeName).upload(fileuid, options);
    };
    FileService.prototype.uploadTemp = function (files, storeName) {
        this.getStore(storeName).uploadTemp(files);
    };
    FileService.prototype.cancelUpload = function (fileuid, storeName) {
        this.getStore(storeName).cancelUpload(fileuid);
    };
    FileService.prototype.getStoreName = function (name) {
        return name ? name : DEFAULT_STORE_NAME;
    };
    FileService.prototype.getStore = function (name) {
        var storeName = this.getStoreName(name);
        var store = this.stores[storeName];
        if (!store) {
            store = this.stores[storeName] = new FileStore();
        }
        return store;
    };
    return FileService;
}());
export { FileService };
export function extractExtension(filename) {
    if (filename) {
        var match = filename.match(/\.([a-zA-Z0-9]{2,4})$/);
        return match ? match[1].toLowerCase() : '';
    }
    return '';
}
var FileStore = /** @class */ (function () {
    function FileStore() {
        this.filesmap = {};
        this.cancelTokens = {};
        this.rx = new Vue({
            data: {
                files: []
            }
        });
    }
    Object.defineProperty(FileStore.prototype, "validationOptions", {
        set: function (options) {
            this.options = options;
        },
        enumerable: false,
        configurable: true
    });
    Object.defineProperty(FileStore.prototype, "files", {
        get: function () {
            return this.rx.files;
        },
        enumerable: false,
        configurable: true
    });
    FileStore.prototype.getFile = function (uid) {
        return this.filesmap[uid];
    };
    FileStore.prototype.add = function (files) {
        return __awaiter(this, void 0, void 0, function () {
            var i;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        i = 0;
                        _a.label = 1;
                    case 1:
                        if (!(i < files.length)) return [3 /*break*/, 4];
                        return [4 /*yield*/, this.addSingleFile(files[i])];
                    case 2:
                        _a.sent();
                        _a.label = 3;
                    case 3:
                        ++i;
                        return [3 /*break*/, 1];
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    FileStore.prototype.addSingleFile = function (file) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.createFile(file)];
                    case 1:
                        _a.sent();
                        this.refreshRx();
                        return [2 /*return*/];
                }
            });
        });
    };
    FileStore.prototype.remove = function (uid) {
        delete this.filesmap[uid];
        this.refreshRx();
    };
    FileStore.prototype.clear = function () {
        this.filesmap = {};
        this.rx.files = [];
    };
    FileStore.prototype.destroy = function () {
        this.rx.$destroy();
    };
    FileStore.prototype.upload = function (fileuid, options) {
        var _this = this;
        var file = this.getFile(fileuid);
        var onUploadProgress = function (e) {
            file.progress = e.loaded / e.total * 100;
            if (options.onUploadProgress) {
                options.onUploadProgress(e);
            }
        };
        var httpService = Vue.prototype.$http;
        var cfg = __assign({ method: 'POST', rawUrl: options.url, data: file.file, headers: {
                'Content-Type': file.file.type
            } }, options.config);
        var cancelToken = axios.CancelToken.source();
        this.cancelTokens[fileuid] = cancelToken;
        var axiosOptions = {
            onUploadProgress: onUploadProgress,
            cancelToken: cancelToken.token
        };
        file.status = MFileStatus.UPLOADING;
        return httpService.execute(cfg, axiosOptions)
            .then(function (value) {
            file.status = MFileStatus.COMPLETED;
            file.progress = 100;
            return value;
        }, function (ex) {
            file.status = axios.isCancel(ex)
                ? MFileStatus.CANCELED
                : MFileStatus.FAILED;
            if (file.status === MFileStatus.FAILED) {
                return Promise.reject(ex);
            }
        })
            .then(function (value) {
            delete _this.cancelTokens[fileuid];
            return value;
        });
    };
    FileStore.prototype.uploadTemp = function (files) {
        var _this = this;
        files.forEach(function (file) {
            var storeFile = _this.getFile(file.uid);
            storeFile.url = URL.createObjectURL(file.file);
            storeFile.status = MFileStatus.COMPLETED;
            storeFile.progress = 100;
        });
    };
    FileStore.prototype.cancelUpload = function (fileuid) {
        this.cancelTokens[fileuid].cancel();
        delete this.cancelTokens[fileuid];
    };
    FileStore.prototype.createFile = function (file) {
        return __awaiter(this, void 0, void 0, function () {
            var mfile;
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        mfile = {
                            uid: uuid.generate(),
                            name: file.name,
                            file: file,
                            status: MFileStatus.READY,
                            progress: 0,
                            get extension() {
                                return extractExtension(file.name);
                            }
                        };
                        return [4 /*yield*/, this.validate(mfile)];
                    case 1:
                        _a.sent();
                        Object.freeze(mfile.file); // disable vuejs reactivity
                        this.filesmap[mfile.uid] = mfile;
                        return [2 /*return*/];
                }
            });
        });
    };
    FileStore.prototype.validate = function (file) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.validateExtension(file);
                        if (!this.options) {
                            return [2 /*return*/];
                        }
                        if (this.options.maxSizeKb) {
                            this.validateSize(file);
                        }
                        if (this.options.maxFiles) {
                            this.validateMaxFiles(file);
                        }
                        if (!(this.isExtensionSupported(file) && this.options.customValidationFunction)) return [3 /*break*/, 2];
                        return [4 /*yield*/, this.executeCustomValidation(file)];
                    case 1:
                        _a.sent();
                        _a.label = 2;
                    case 2: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * If the extension is not specified, it'll be rejected.
     * If the extension is a part of acceptedExtensions or if acceptedExtensions is empty or undefined, we accept all extensions.
     * If the extension is a part of rejectedExtensions, it'll be rejected.
     * If the extension is a part of the accepted and rejected extensions, it'll be rejected.
     */
    FileStore.prototype.validateExtension = function (file) {
        if (!this.isExtensionSupported(file)) {
            file.status = MFileStatus.REJECTED;
            file.rejection = MFileRejectionCause.FILE_TYPE;
        }
    };
    FileStore.prototype.isExtensionSupported = function (file) {
        var ext = extractExtension(file.file.name);
        return ext !== '' && !this.extensionInRejectedExtensions(ext) && this.extensionInAcceptedExtensions(ext);
    };
    FileStore.prototype.extensionInAcceptedExtensions = function (extension) {
        return this.options === undefined || this.options.allowedExtensions === undefined || this.options.allowedExtensions.length === 0 || this.options.allowedExtensions.indexOf(extension) !== -1;
    };
    FileStore.prototype.extensionInRejectedExtensions = function (extension) {
        return this.options !== undefined && this.options.rejectedExtensions !== undefined && this.options.rejectedExtensions.length > 0 && this.options.rejectedExtensions.indexOf(extension) !== -1;
    };
    FileStore.prototype.validateSize = function (file) {
        if (file.file.size / 1024 > this.options.maxSizeKb) {
            file.status = MFileStatus.REJECTED;
            file.rejection = MFileRejectionCause.FILE_SIZE;
        }
    };
    FileStore.prototype.validateMaxFiles = function (file) {
        var _this = this;
        var nbValidFiles = Object.keys(this.filesmap).reduce(function (t, uid) {
            var f = _this.filesmap[uid];
            return (t =
                f.status === MFileStatus.COMPLETED ||
                    f.status === MFileStatus.READY ||
                    f.status === MFileStatus.UPLOADING
                    ? t + 1
                    : t);
        }, 0);
        if (nbValidFiles >= this.options.maxFiles) {
            file.status = MFileStatus.REJECTED;
            file.rejection = MFileRejectionCause.MAX_FILES;
        }
    };
    FileStore.prototype.executeCustomValidation = function (file) {
        return __awaiter(this, void 0, void 0, function () {
            return __generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.options.customValidationFunction(file)];
                    case 1:
                        if (!(_a.sent())) {
                            file.status = MFileStatus.REJECTED;
                            file.rejection = MFileRejectionCause.CUSTOM_VALIDATION;
                        }
                        return [2 /*return*/];
                }
            });
        });
    };
    FileStore.prototype.refreshRx = function () {
        var files = [];
        for (var f in this.filesmap) {
            files.push(this.filesmap[f]);
        }
        this.rx.files = files;
    };
    return FileStore;
}());
var FilePlugin = {
    install: function (v, options) {
        v.prototype.$log.debug('$file', 'plugin.install');
        var file = new FileService();
        (v.prototype).$file = file;
    }
};
export default FilePlugin;
//# sourceMappingURL=file.js.map