"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const FileSystemFile_1 = require("./FileSystemFile");
const FileSystemStorage_1 = require("./FileSystemStorage");
const StorageUtilities_1 = require("./StorageUtilities");
const Utilities_1 = require("./../core/Utilities");
const Log_1 = require("./../core/Log");
const FolderBase_1 = require("./FolderBase");
class FileSystemFolder extends FolderBase_1.default {
    get handle() {
        return this._handle;
    }
    set handle(newHandle) {
        this._handle = newHandle;
    }
    get writeHandle() {
        return this._writeHandle;
    }
    set writeHandle(newHandle) {
        this._writeHandle = newHandle;
    }
    get storage() {
        return this._storage;
    }
    get parentFolder() {
        return this._parentFolder;
    }
    get name() {
        return this._name;
    }
    get fullPath() {
        return (this._parentPath + FileSystemStorage_1.default.fileSystemFolderDelimiter + StorageUtilities_1.default.canonicalizeName(this.name));
    }
    constructor(storage, parentFolder, parentPath, folderName, handle) {
        super();
        this._storage = storage;
        this._parentFolder = parentFolder;
        this._handle = handle;
        this._writeHandle = handle;
        this._parentPath = parentPath;
        this._name = folderName;
        this._lastSavedContent = "";
        this.lastSavedFileCount = 0;
        this.folders = {};
        this.files = {};
    }
    async getFirstUnsafeError(depth, processedFolders) {
        if (depth && depth > 12) {
            return "Folder hierarchy is too deep.";
        }
        await this.load(false);
        for (const fileName in this.files) {
            if (!StorageUtilities_1.default.isUsableFile(fileName)) {
                return "Cannot work with '" + this.fullPath + fileName + "'";
            }
        }
        const folderCount = this.folderCount;
        processedFolders = processedFolders === undefined ? folderCount : processedFolders + this.folderCount;
        if (folderCount > 1000) {
            return "Folder hierarchy is too complex. (" + folderCount + " folders)";
        }
        if (processedFolders > 4000) {
            return "Too many folders to process in this folder.";
        }
        for (const folderName in this.folders) {
            const folder = this.folders[folderName];
            if (folder) {
                const result = await folder.getFirstUnsafeError(depth === undefined ? 1 : depth + 1, processedFolders);
                if (result !== undefined) {
                    return result;
                }
            }
        }
        return undefined;
    }
    ensureFile(name, file) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(name);
        let candFile = this.files[nameCanon];
        if (candFile == null) {
            candFile = new FileSystemFile_1.default(this, name);
            this.files[nameCanon] = candFile;
            this.storage.notifyFileAdded(candFile);
        }
        if (file) {
            candFile.handle = file;
            candFile.writeHandle = file;
        }
        return candFile;
    }
    async exists() {
        return true;
    }
    async ensureExists() {
        return true;
    }
    ensureFolder(name, handle) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(name);
        let candFolder = this.folders[nameCanon];
        if (!candFolder) {
            candFolder = new FileSystemFolder(this._storage, this, this.fullPath, name);
            this.folders[nameCanon] = candFolder;
        }
        if (handle) {
            candFolder.handle = handle;
        }
        return candFolder;
    }
    _removeFile(file) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(file.name);
        const candFile = this.files[nameCanon];
        Log_1.default.assert(candFile === file, "Files don't match.");
        this.files[nameCanon] = undefined;
        this.storage.notifyFileRemoved(this.storageRelativePath + file.name);
    }
    async deleteThisFolder() {
        throw new Error("Deletion of this folder " + this.fullPath + " is not supported.");
    }
    async _removeFileExistence(fileName) {
        const handle = await this.getHandle();
        await handle?.removeEntry(fileName, {
            recursive: false,
        });
    }
    _addExistingFile(file) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(file.name);
        this.files[nameCanon] = file;
    }
    _addExistingFolder(folder) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(folder.name);
        this.folders[nameCanon] = folder;
    }
    async moveTo(newStorageRelativePath) {
        const newFolderPath = StorageUtilities_1.default.getFolderPath(newStorageRelativePath);
        const newFolderName = StorageUtilities_1.default.getLeafName(newStorageRelativePath);
        if (newFolderName.length < 2) {
            throw new Error("New path is not correct.");
        }
        if (this.isSameFolder(newStorageRelativePath)) {
            return false;
        }
        if (this._parentFolder !== null) {
            const newParentFolder = await this._parentFolder.storage.ensureFolderFromStorageRelativePath(newFolderPath);
            if (newParentFolder.folders[newFolderName] !== undefined) {
                throw new Error("Folder exists at specified path.");
            }
            this._parentFolder._clearExistingFolder(this);
            this._parentFolder = newParentFolder;
            this._name = newFolderName;
            newParentFolder._addExistingFolder(this);
        }
        this._name = newFolderName;
        if (this._parentFolder !== null) {
            await this._parentFolder.save(true);
        }
        throw new Error("Not implemented.");
        //return true;
    }
    async createFile(name) {
        const file = this.ensureFile(name);
        this.storage.notifyFileAdded(file);
        return file;
    }
    async getHandle() {
        if (!this._handle) {
            if (this._writeHandle) {
                return this._writeHandle;
            }
            if (this.parentFolder) {
                const parentFolderHandle = await this.parentFolder.getHandle();
                if (parentFolderHandle) {
                    try {
                        this._handle = await parentFolderHandle.getDirectoryHandle(this.name, {
                            create: false,
                        });
                    }
                    catch (e) {
                        // Log.debugAlert("Folder r/o retrieval: " + e);
                        return undefined;
                    }
                }
            }
        }
        return this._handle;
    }
    async ensureWriteHandle() {
        if (!this._writeHandle) {
            if (this.parentFolder) {
                await this.parentFolder.ensureWriteHandle();
                if (this.parentFolder.writeHandle) {
                    try {
                        this._writeHandle = await this.parentFolder.writeHandle.getDirectoryHandle(this.name, {
                            create: true,
                        });
                    }
                    catch (e) {
                        Log_1.default.debugAlert("Folder r/w retrieval: " + e);
                    }
                }
            }
        }
        Log_1.default.assert(this._writeHandle !== undefined, "No folder handle.");
        return this._writeHandle;
    }
    async load(force) {
        if (this.lastLoadedOrSaved != null && !force) {
            return this.lastLoadedOrSaved;
        }
        const handle = await this.getHandle();
        if (handle) {
            for await (const [key, value] of handle.entries()) {
                if (value.kind === "file") {
                    this.ensureFile(key, value);
                }
                else if (value.kind === "directory") {
                    const keyLower = key.toLowerCase();
                    if (keyLower !== ".git" && keyLower !== "node_modules") {
                        this.ensureFolder(key, value);
                    }
                }
            }
        }
        this.updateLastLoadedOrSaved();
        return this.lastLoadedOrSaved;
    }
    async save(force) {
        this.updateLastLoadedOrSaved();
        const folderState = {
            updated: this.lastLoadedOrSaved,
            files: [],
            folders: [],
        };
        for (const fileName in this.files) {
            const file = this.files[fileName];
            if (file !== undefined) {
                const fileState = {
                    name: file.name,
                    size: file.size,
                    modified: file.latestModified == null ? new Date() : file.latestModified,
                };
                folderState.files.push(fileState);
            }
        }
        for (const folderName in this.folders) {
            const childFolder = this.folders[folderName];
            if (childFolder !== undefined && !childFolder.errorStatus) {
                const childFolderState = {
                    name: childFolder.name,
                    fileCount: Utilities_1.default.lengthOfDictionary(childFolder.files),
                    modified: childFolder.modifiedAtLoad == null ? new Date() : childFolder.modifiedAtLoad,
                };
                folderState.folders.push(childFolderState);
            }
        }
        const saveContent = JSON.stringify(folderState);
        if (this._lastSavedContent !== saveContent || force) {
            //  await localforage.setItem<string>(this.fullPath + FileSystemStorage.folderDelimiter, saveContent);
        }
        return this.lastLoadedOrSaved;
    }
}
exports.default = FileSystemFolder;

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