"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const StorageUtilities_1 = require("./StorageUtilities");
const Utilities_1 = require("./../core/Utilities");
const AllFolderFileIterator_1 = require("./AllFolderFileIterator");
const Log_1 = require("../core/Log");
const ste_events_1 = require("ste-events");
class FolderBase {
    get ensuredName() {
        return this.name;
    }
    isDisposed = false;
    #lastLoadedOrSaved;
    manager;
    #onFolderMoved = new ste_events_1.EventDispatcher();
    #onChildFolderMoved = new ste_events_1.EventDispatcher();
    errorStatus;
    get onFolderMoved() {
        return this.#onFolderMoved.asEvent();
    }
    get onChildFolderMoved() {
        return this.#onChildFolderMoved.asEvent();
    }
    get isLoaded() {
        return this.#lastLoadedOrSaved !== null;
    }
    get canIgnore() {
        return StorageUtilities_1.default.isIgnorableFolder(this.name);
    }
    get allFiles() {
        let parentFolder = this;
        return {
            [Symbol.asyncIterator]() {
                return new AllFolderFileIterator_1.default(parentFolder);
            },
        };
    }
    get folderCount() {
        let i = 0;
        for (const folderName in this.folders) {
            if (this.folders[folderName]) {
                i++;
            }
        }
        return i;
    }
    get fileCount() {
        let i = 0;
        // eslint-disable-next-line
        for (const fileName in this.files) {
            if (this.files[fileName]) {
                i++;
            }
        }
        return i;
    }
    get storageRelativePath() {
        if (this.parentFolder === null) {
            return "/";
        }
        else {
            return this.parentFolder.storageRelativePath + this.name + "/";
        }
    }
    get lastLoadedOrSaved() {
        return this.#lastLoadedOrSaved;
    }
    get fullPath() {
        return this.storageRelativePath;
    }
    get extendedPath() {
        let start = "";
        if (this.storage.storagePath) {
            start = this.storage.storagePath;
        }
        let result = start + this.fullPath;
        if (!result.endsWith(this.storage.folderDelimiter)) {
            result += this.storage.folderDelimiter;
        }
        return result;
    }
    constructor() {
        this.#lastLoadedOrSaved = null;
    }
    updateLastLoadedOrSaved() {
        this.#lastLoadedOrSaved = new Date();
    }
    getFolderRelativePath(toFolder) {
        if (toFolder.storage !== this.storage && this.storage.storagePath) {
            const parentPath = toFolder.storageRelativePath;
            if (parentPath &&
                this.extendedPath &&
                this.extendedPath.startsWith(parentPath) &&
                StorageUtilities_1.default.ensureEndsWithDelimiter(parentPath)) {
                const subPath = this.extendedPath.substring(parentPath.length - 1);
                return subPath;
            }
            return this.extendedPath;
        }
        else if (this === toFolder) {
            return "/";
        }
        else if (this.parentFolder === null) {
            return undefined;
        }
        else {
            const result = this.parentFolder.getFolderRelativePath(toFolder);
            if (result === undefined) {
                return undefined;
            }
            return result + this.name + "/";
        }
    }
    dispose() {
        this.manager = undefined;
        if (this.folders) {
            for (const folderName in this.folders) {
                const folder = this.folders[folderName];
                if (folder) {
                    folder.dispose();
                    this.folders[folderName] = undefined;
                }
            }
        }
        if (this.files) {
            for (const fileName in this.files) {
                const file = this.files[fileName];
                if (file) {
                    file.dispose();
                    this.files[fileName] = undefined;
                }
            }
        }
        this.isDisposed = true;
    }
    async setStructureFromFileList(fileList) {
        this.updateLastLoadedOrSaved();
        for (const file of fileList) {
            let folderPath = StorageUtilities_1.default.getFolderPath(file);
            if (folderPath) {
                folderPath = StorageUtilities_1.default.ensureEndsDelimited(folderPath);
                if (folderPath.length > 2) {
                    const folder = (await this.ensureFolderFromRelativePath(folderPath, true));
                    folder.updateLastLoadedOrSaved();
                    let parentFolder = folder.parentFolder;
                    while (parentFolder && parentFolder !== this) {
                        parentFolder.updateLastLoadedOrSaved();
                        parentFolder = parentFolder.parentFolder;
                    }
                }
                await this.ensureFileFromRelativePath(file, true);
            }
        }
    }
    isSameFolder(newFolderStorageRelativePath) {
        if (StorageUtilities_1.default.canonicalizePath(newFolderStorageRelativePath) ===
            StorageUtilities_1.default.canonicalizePath(this.storageRelativePath)) {
            return true;
        }
        return false;
    }
    _addExistingFolderToParent(folder) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(folder.name);
        if (Utilities_1.default.isUsableAsObjectKey(nameCanon)) {
            this.folders[nameCanon] = folder;
        }
    }
    _removeExistingFolderFromParent(folder) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(folder.name);
        if (Utilities_1.default.isUsableAsObjectKey(nameCanon)) {
            this.folders[nameCanon] = undefined;
        }
    }
    folderExists(name) {
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        const nameCanon = StorageUtilities_1.default.canonicalizeName(name);
        return !Utilities_1.default.isNullOrUndefined(this.folders[nameCanon]);
    }
    fileExists(name) {
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        const nameCanon = StorageUtilities_1.default.canonicalizeName(name);
        return !Utilities_1.default.isNullOrUndefined(this.files[nameCanon]);
    }
    async deleteFileFromRelativePath(path) {
        path = this.canonicalizePath(path);
        const file = await this.getFileFromRelativePath(path);
        if (file !== undefined) {
            return await file.deleteThisFile();
        }
        return false;
    }
    canonicalizePath(path) {
        return path.replace(/\\/g, "/");
    }
    async getFileFromRelativePath(path) {
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        if (path.length < 2) {
            throw Error("Path is too short.");
        }
        path = this.canonicalizePath(path);
        if (path[0] !== "/") {
            throw Error("Storage relative path '" + path + "' is not in the right format.");
        }
        await this.ensureExists();
        await this.load(false);
        const nextSlash = path.indexOf("/", 1);
        if (nextSlash < 0) {
            const fileName = path.substring(1, path.length);
            return this.files[StorageUtilities_1.default.canonicalizeName(fileName)];
        }
        else {
            const folderName = path.substring(1, nextSlash);
            const folder = this.folders[StorageUtilities_1.default.canonicalizeName(folderName)];
            if (folder === undefined || folder === null) {
                return undefined;
            }
            return folder.getFileFromRelativePath(path.substring(nextSlash, path.length));
        }
    }
    clearAllManagers() {
        if (this.manager) {
            this.manager = undefined;
        }
        for (const fileName in this.files) {
            const file = this.files[fileName];
            if (file) {
                file.manager = undefined;
            }
        }
        for (const folderName in this.folders) {
            const folder = this.folders[folderName];
            if (folder) {
                folder.clearAllManagers();
            }
        }
    }
    async getFolderFromRelativePath(path) {
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        if (path === "/" || path === "\\") {
            return this;
        }
        if (path.length < 2) {
            throw Error("Path is too short.");
        }
        path = this.canonicalizePath(path);
        if (path[0] !== "/") {
            throw Error("Storage relative path '" + path + "' is not in the right format.");
        }
        await this.ensureExists();
        await this.load(false);
        const nextSlash = path.indexOf("/", 1);
        if (nextSlash < 0) {
            const folderName = path.substring(1, path.length);
            return this.folders[StorageUtilities_1.default.canonicalizeName(folderName)];
        }
        else {
            let folderName = path.substring(1, nextSlash);
            folderName = StorageUtilities_1.default.canonicalizeName(folderName);
            const folder = this.folders[folderName];
            if (folder === undefined || folder === null) {
                return undefined;
            }
            const nextPath = path.substring(nextSlash, path.length);
            if (nextPath === "/" || nextPath === "\\") {
                return folder;
            }
            return await folder.getFolderFromRelativePath(nextPath);
        }
    }
    getFolderByIndex(index) {
        let curIndex = 0;
        for (const folderName in this.folders) {
            const folder = this.folders[folderName];
            if (folder && curIndex === index) {
                return folder;
            }
            if (folder) {
                curIndex++;
            }
        }
        return undefined;
    }
    getSortedFolderKeys() {
        return Object.keys(this.folders).sort();
    }
    getSortedFileKeys() {
        return Object.keys(this.files).sort();
    }
    getSummary() {
        let str = "P: " + this.fullPath + " FO:";
        for (const folderName in this.folders) {
            str += " " + folderName;
        }
        str += " FI:";
        for (const fileName in this.files) {
            str += " " + fileName;
        }
        return str;
    }
    getFolderFromRelativePathLocal(path) {
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        if (path === "/" || path === "\\") {
            return this;
        }
        if (path.length < 2) {
            throw Error("Path is too short.");
        }
        path = this.canonicalizePath(path);
        if (path[0] !== "/") {
            throw Error("Storage relative path '" + path + "' is not in the right format.");
        }
        const nextSlash = path.indexOf("/", 1);
        if (nextSlash < 0) {
            const folderName = path.substring(1, path.length);
            return this.folders[StorageUtilities_1.default.canonicalizeName(folderName)];
        }
        else {
            const folderName = path.substring(1, nextSlash);
            let folder = this.folders[StorageUtilities_1.default.canonicalizeName(folderName)];
            if (folder === undefined || folder === null) {
                if (folderName.endsWith("#")) {
                    const storageFile = this.files[StorageUtilities_1.default.canonicalizeName(folderName.substring(0, folderName.length - 1))];
                    if (storageFile) {
                        if (storageFile.fileContainerStorage) {
                            folder = storageFile.fileContainerStorage.rootFolder;
                        }
                    }
                }
                if (folder === undefined || folder === null) {
                    return undefined;
                }
            }
            const nextPath = path.substring(nextSlash, path.length);
            if (nextPath === "/" || nextPath === "\\") {
                return folder;
            }
            return folder.getFolderFromRelativePathLocal(nextPath);
        }
    }
    async saveAll(force) {
        // Log.verbose("Saving all at " + this.storageRelativePath);
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        for (const fileName in this.files) {
            const file = this.files[fileName];
            if (file !== undefined && (file.needsSave || force)) {
                await file.saveContent(force);
            }
        }
        for (const folderName in this.folders) {
            const folder = this.folders[folderName];
            if (folder !== undefined && !folder.errorStatus) {
                await folder.saveAll(force);
            }
        }
        return true;
    }
    async ensureFolderFromRelativePath(path, ignoreLoad) {
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        if (path === "/" || path === "\\") {
            return this;
        }
        if (path.startsWith("./")) {
            path = path.substring(1);
        }
        path = this.canonicalizePath(path);
        if (path[path.length - 1] !== "/") {
            path = path + "/";
        }
        if (path[0] !== "/") {
            throw Error("Storage relative path '" + path + "' is not in the right format.");
        }
        if (!ignoreLoad) {
            await this.ensureExists();
            await this.load(false);
        }
        const nextSlash = path.indexOf("/", 1);
        if (nextSlash < 0) {
            throw new Error("Unexpected path format error");
        }
        else {
            const folderName = path.substring(1, nextSlash);
            const folder = this.ensureFolder(folderName);
            if (nextSlash === path.length - 1) {
                return folder;
            }
            else {
                return folder.ensureFolderFromRelativePath(path.substring(nextSlash, path.length), ignoreLoad);
            }
        }
    }
    async ensureFileFromRelativePath(path, ignoreLoad) {
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        if (path.length < 2) {
            throw Error("Path is too short.");
        }
        path = this.canonicalizePath(path);
        if (path[0] !== "/") {
            throw Error("Storage relative path '" + path + "' is not in the right format.");
        }
        await this.ensureExists();
        if (!ignoreLoad) {
            await this.load(false);
        }
        const nextSlash = path.indexOf("/", 1);
        if (nextSlash < 0) {
            const file = this.ensureFile(path.substring(1, path.length));
            return file;
        }
        else {
            const folderName = path.substring(1, nextSlash);
            const folder = this.ensureFolder(folderName);
            const file = await folder.ensureFileFromRelativePath(path.substring(nextSlash, path.length), ignoreLoad);
            return file;
        }
    }
    async deleteFile(name) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(name);
        const file = this.files[nameCanon];
        if (file !== undefined) {
            return await file.deleteThisFile();
        }
        this.files[nameCanon] = undefined;
        return false;
    }
    removeFolder(name) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(name);
        if (!Utilities_1.default.isUsableAsObjectKey(nameCanon)) {
            throw new Error();
        }
        const exists = this.folders[nameCanon] !== undefined;
        this.folders[nameCanon] = undefined;
        return exists;
    }
    async rename(name) {
        let targetPath = name;
        if (this.parentFolder !== null) {
            targetPath = this.storage.joinPath(this.parentFolder.storageRelativePath, targetPath);
        }
        return await this.moveTo(targetPath);
    }
    async recursiveDeleteThisFolder() {
        let isAllDeletes = await this.recursiveDeleteContentsOfThisFolder();
        this.removeMeFromParent();
        return isAllDeletes;
    }
    async recursiveDeleteContentsOfThisFolder() {
        let isAllDeletes = true;
        await this.load(true);
        for (const folderName in this.folders) {
            const folder = this.folders[folderName];
            if (folder) {
                if (!(await folder.deleteThisFolder())) {
                    isAllDeletes = false;
                }
            }
        }
        for (const fileName in this.files) {
            const file = this.files[fileName];
            if (file) {
                if (!(await file.deleteThisFile(true))) {
                    isAllDeletes = false;
                }
            }
        }
        return isAllDeletes;
    }
    notifyFolderMoved(folderMove) {
        this.#onFolderMoved.dispatch(this, folderMove);
        if (this.parentFolder) {
            this.parentFolder.notifyChildFolderMoved(folderMove);
        }
        this.storage.notifyFolderMoved(folderMove);
    }
    notifyChildFolderMoved(folderMove) {
        this.#onChildFolderMoved.dispatch(this, folderMove);
        if (this.parentFolder) {
            this.parentFolder.notifyChildFolderMoved(folderMove);
        }
    }
    removeMeFromParent() {
        if (this.parentFolder) {
            this.parentFolder.removeFolder(this.name);
        }
    }
}
exports.default = FolderBase;

//# sourceMappingURL=../maps/storage/FolderBase.js.map
