"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var api_1 = require("./api");
var memory_fs_1 = require("./memory-fs");
var utils_1 = require("./utils");
function nodesToMap(tree, accumulator) {
    if (accumulator === void 0) { accumulator = {}; }
    tree.forEach(function (node) {
        if (api_1.isDir(node))
            nodesToMap(node.children, accumulator);
        accumulator[node.fullPath] = node;
    });
    return accumulator;
}
;
function getTreesDiff(cached, real) {
    var diff = {
        toAdd: [],
        toDelete: [],
        toChange: []
    };
    Object.keys(cached).forEach(function (cachedPath) {
        if (!real[cachedPath]) {
            diff.toDelete.push(cached[cachedPath]);
            diff.toChange = diff.toChange.filter(function (path) { return path !== cachedPath; });
        }
        else {
            var node = cached[cachedPath];
            if (api_1.isFile(node) && node.content)
                diff.toChange.push(cachedPath);
        }
    });
    Object.keys(real).forEach(function (realPath) {
        if (!cached[realPath])
            diff.toAdd.push(real[realPath]);
    });
    return diff;
}
var CacheFileSystem = /** @class */ (function () {
    function CacheFileSystem(fs, shouldRescanOnError) {
        if (shouldRescanOnError === void 0) { shouldRescanOnError = true; }
        var _this = this;
        this.fs = fs;
        this.shouldRescanOnError = shouldRescanOnError;
        this.events = utils_1.makeEventsEmitter();
        this.isTreeCached = false;
        this.pathsInCache = {};
        this.onFsError = function (_a) {
            var stack = _a.stack;
            _this.shouldRescanOnError ?
                _this.rescanOnError() :
                _this.emit('unexpectedError', { stack: stack });
        };
        this.baseUrl = fs.baseUrl;
        this.cache = new memory_fs_1.MemoryFileSystem();
        this.fs.events.on('unexpectedError', this.onFsError);
        this.fs.events.on('fileCreated', function (event) {
            var fullPath = event.fullPath, newContent = event.newContent;
            try {
                _this.cache.saveFileSync(fullPath, newContent);
                _this.pathsInCache[fullPath] = true;
                _this.events.emit('fileCreated', event);
            }
            catch (e) {
                _this.onFsError(e);
            }
        });
        this.fs.events.on('fileChanged', function (event) {
            var fullPath = event.fullPath, newContent = event.newContent;
            try {
                _this.cache.saveFileSync(fullPath, newContent);
                _this.pathsInCache[fullPath] = true;
                _this.events.emit('fileChanged', event);
            }
            catch (e) {
                _this.onFsError(e);
            }
        });
        this.fs.events.on('fileDeleted', function (event) {
            try {
                _this.cache.deleteFileSync(event.fullPath);
                _this.pathsInCache[event.fullPath] = true;
                _this.events.emit('fileDeleted', event);
            }
            catch (e) {
                _this.onFsError(e);
            }
        });
        this.fs.events.on('directoryCreated', function (event) {
            try {
                _this.cache.ensureDirectorySync(event.fullPath);
                _this.events.emit('directoryCreated', event);
            }
            catch (e) {
                _this.onFsError(e);
            }
        });
        this.fs.events.on('directoryDeleted', function (event) {
            try {
                _this.cache.deleteDirectorySync(event.fullPath, true);
                _this.events.emit('directoryDeleted', event);
            }
            catch (e) {
                _this.onFsError(e);
            }
        });
    }
    CacheFileSystem.prototype.saveFile = function (fullPath, newContent) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.fs.saveFile(fullPath, newContent)];
                    case 1:
                        _a.sent();
                        this.cache.saveFileSync(fullPath, newContent);
                        this.pathsInCache[fullPath] = true;
                        return [2 /*return*/];
                }
            });
        });
    };
    CacheFileSystem.prototype.deleteFile = function (fullPath) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.fs.deleteFile(fullPath)];
                    case 1:
                        _a.sent();
                        this.cache.deleteFileSync(fullPath);
                        return [2 /*return*/];
                }
            });
        });
    };
    CacheFileSystem.prototype.deleteDirectory = function (fullPath, recursive) {
        if (recursive === void 0) { recursive = false; }
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.fs.deleteDirectory(fullPath, recursive)];
                    case 1:
                        _a.sent();
                        this.cache.deleteDirectorySync(fullPath, recursive);
                        return [2 /*return*/];
                }
            });
        });
    };
    CacheFileSystem.prototype.ensureDirectory = function (fullPath) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.fs.ensureDirectory(fullPath)];
                    case 1:
                        _a.sent();
                        this.cache.ensureDirectorySync(fullPath);
                        return [2 /*return*/];
                }
            });
        });
    };
    CacheFileSystem.prototype.loadTextFile = function (fullPath) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var file;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (this.pathsInCache[fullPath]) {
                            return [2 /*return*/, this.cache.loadTextFileSync(fullPath)];
                        }
                        return [4 /*yield*/, this.fs.loadTextFile(fullPath)];
                    case 1:
                        file = _a.sent();
                        this.cache.saveFileSync(fullPath, file);
                        return [2 /*return*/, this.cache.loadTextFileSync(fullPath)];
                }
            });
        });
    };
    CacheFileSystem.prototype.loadDirectoryTree = function (fullPath) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (this.isTreeCached) {
                            return [2 /*return*/, this.cache.loadDirectoryTreeSync(fullPath)];
                        }
                        return [4 /*yield*/, this.cacheTree()];
                    case 1:
                        _a.sent();
                        this.isTreeCached = true;
                        return [2 /*return*/, this.cache.loadDirectoryTreeSync(fullPath)];
                }
            });
        });
    };
    CacheFileSystem.prototype.loadDirectoryChildren = function (fullPath) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (this.isTreeCached) {
                            return [2 /*return*/, this.cache.loadDirectoryChildrenSync(fullPath)];
                        }
                        return [4 /*yield*/, this.cacheTree()];
                    case 1:
                        _a.sent();
                        this.isTreeCached = true;
                        return [2 /*return*/, this.cache.loadDirectoryChildrenSync(fullPath)];
                }
            });
        });
    };
    CacheFileSystem.prototype.dispose = function () {
        if (api_1.isDisposable(this.fs))
            this.fs.dispose();
    };
    CacheFileSystem.prototype.rescanOnError = function () {
        var _this = this;
        var cachedTree = this.cache.loadDirectoryTreeSync();
        this.isTreeCached = false;
        this.loadDirectoryTree().then(function (realTree) {
            var _a = getTreesDiff(nodesToMap(cachedTree.children), nodesToMap(realTree.children)), toDelete = _a.toDelete, toAdd = _a.toAdd, toChange = _a.toChange;
            toDelete.forEach(function (node) {
                _this.emit((api_1.isDir(node) ? 'directory' : 'file') + "Deleted", { fullPath: node.fullPath });
            });
            toAdd.forEach(function (node) {
                if (api_1.isDir(node)) {
                    _this.emit('directoryCreated', { fullPath: node.fullPath });
                }
                else {
                    _this.loadTextFile(node.fullPath).then(function (newContent) {
                        _this.emit('fileCreated', {
                            fullPath: node.fullPath,
                            newContent: newContent
                        });
                    });
                }
            });
            toChange.forEach(function (fullPath) { return _this.loadTextFile(fullPath).then(function (newContent) {
                _this.emit('fileChanged', { fullPath: fullPath, newContent: newContent });
            }); });
        });
    };
    CacheFileSystem.prototype.emit = function (type, data) {
        this.events.emit(type, tslib_1.__assign({}, data, { type: type }));
    };
    CacheFileSystem.prototype.cacheTree = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var tree;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this.cache = new memory_fs_1.MemoryFileSystem();
                        this.pathsInCache = {};
                        return [4 /*yield*/, this.fs.loadDirectoryTree()];
                    case 1:
                        tree = _a.sent();
                        return [2 /*return*/, this.fill(tree)];
                }
            });
        });
    };
    CacheFileSystem.prototype.fill = function (tree) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var _this = this;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        if (!api_1.isDir(tree)) return [3 /*break*/, 2];
                        this.cache.ensureDirectorySync(tree.fullPath);
                        return [4 /*yield*/, Promise.all(tree.children.map(function (child) { return _this.fill(child); }))];
                    case 1:
                        _a.sent();
                        return [2 /*return*/, this.cache];
                    case 2:
                        this.cache.saveFileSync(tree.fullPath, '');
                        return [2 /*return*/, this.cache];
                }
            });
        });
    };
    return CacheFileSystem;
}());
exports.CacheFileSystem = CacheFileSystem;
//# sourceMappingURL=cache-fs.js.map