"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
const BrowserFile_1 = require("./BrowserFile");
const BrowserStorage_1 = require("./BrowserStorage");
const StorageUtilities_1 = require("./StorageUtilities");
const Utilities_1 = require("./../core/Utilities");
const Log_1 = require("./../core/Log");
const localforage_1 = require("localforage");
const FolderBase_1 = require("./FolderBase");
class BrowserFolder extends FolderBase_1.default {
    _name;
    _parentPath;
    _lastSavedContent;
    _storage;
    _parentFolder;
    _lastLoadedPath;
    get storage() {
        return this._storage;
    }
    get parentFolder() {
        return this._parentFolder;
    }
    folders;
    files;
    lastSavedFileCount;
    modifiedAtLoad;
    get name() {
        return this._name;
    }
    get fullPath() {
        return this._parentPath + BrowserStorage_1.default.slashFolderDelimiter + StorageUtilities_1.default.canonicalizeName(this.name);
    }
    constructor(storage, parentFolder, parentPath, folderName) {
        super();
        this._storage = storage;
        this._parentFolder = parentFolder;
        this._parentPath = parentPath;
        this._name = folderName;
        this._lastSavedContent = "";
        this.lastSavedFileCount = 0;
        this.folders = {};
        this.files = {};
    }
    ensureFile(name) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(name);
        let candFile = this.files[nameCanon];
        if (candFile == null) {
            candFile = new BrowserFile_1.default(this, name);
            this.files[nameCanon] = candFile;
            this.storage.notifyFileAdded(candFile);
        }
        return candFile;
    }
    async exists() {
        return true;
    }
    async ensureExists() {
        return true;
    }
    async deleteThisFolder() {
        if (this.storage.readOnly) {
            throw new Error("Can't save read-only file.");
        }
        let result = await this.recursiveDeleteThisFolder();
        await localforage_1.default.removeItem(this.fullPath + BrowserStorage_1.default.slashFolderDelimiter);
        return result;
    }
    async deleteAllFolderContents() {
        throw new Error("Deletion of all folder contents at " + this.fullPath + " is not supported.");
    }
    ensureFolder(name) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(name);
        let candFolder = this.folders[nameCanon];
        if (!candFolder) {
            candFolder = new BrowserFolder(this._storage, this, this.fullPath, name);
            this.folders[nameCanon] = candFolder;
        }
        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);
    }
    _addExistingFile(file) {
        const nameCanon = StorageUtilities_1.default.canonicalizeName(file.name);
        if (Utilities_1.default.isUsableAsObjectKey(nameCanon)) {
            this.files[nameCanon] = file;
        }
    }
    async moveTo(newStorageRelativePath, ignoreParentSave) {
        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;
        }
        await this.load();
        const oldPath = this._lastLoadedPath;
        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;
        for (const fileName in this.files) {
            const file = this.files[fileName];
            if (file) {
                await file.resaveAfterMove();
            }
        }
        for (const folderName in this.folders) {
            const folder = this.folders[folderName];
            if (folder) {
                await folder.resaveAfterMove(this.fullPath);
            }
        }
        await this.save(true);
        if (oldPath) {
            await localforage_1.default.removeItem(oldPath);
        }
        if (this._parentFolder !== null && !ignoreParentSave) {
            await this._parentFolder.save(true);
        }
        if (!ignoreParentSave) {
            this.notifyFolderMoved({ previousStoragePath: oldPath, newStoragePath: this.fullPath, folder: this });
        }
        return true;
    }
    async createFile(name) {
        const file = this.ensureFile(name);
        this.storage.notifyFileAdded(file);
        return file;
    }
    async load(force) {
        if (this.lastLoadedOrSaved != null && !force) {
            return this.lastLoadedOrSaved;
        }
        this._lastLoadedPath = this.fullPath + BrowserStorage_1.default.slashFolderDelimiter;
        const listingContent = await localforage_1.default.getItem(this.fullPath + BrowserStorage_1.default.slashFolderDelimiter);
        if (listingContent != null) {
            this._lastSavedContent = listingContent;
            let folderState;
            try {
                folderState = JSON.parse(listingContent);
            }
            catch (e) {
                Log_1.default.debugAlert("Failure to parse browser folder content JSON:" + e.toString());
            }
            if (folderState) {
                for (var i = 0; i < folderState.files.length; i++) {
                    const fileMeta = folderState.files[i];
                    // JSON.parse doesn't fix up dates produced by JSON.stringify, so do that here.
                    if (fileMeta.modified != null && !(fileMeta.modified instanceof Date)) {
                        fileMeta.modified = new Date(fileMeta.modified);
                    }
                    const newFile = this.ensureFile(fileMeta.name);
                    newFile.modifiedAtLoad = fileMeta.modified == null ? null : fileMeta.modified;
                    if (newFile.sizeAtLoad === undefined) {
                        newFile.sizeAtLoad = fileMeta.size;
                    }
                }
                for (var j = 0; j < folderState.folders.length; j++) {
                    const folderMeta = folderState.folders[j];
                    // JSON.parse doesn't fix up dates produced by JSON.stringify, so do that here.
                    if (folderMeta.modified != null && !(folderMeta.modified instanceof Date)) {
                        folderMeta.modified = new Date(folderMeta.modified);
                    }
                    const newFolder = this.ensureFolder(folderMeta.name);
                    newFolder.modifiedAtLoad = folderMeta.modified;
                    newFolder.lastSavedFileCount = folderMeta.fileCount;
                }
            }
        }
        this.updateLastLoadedOrSaved();
        return this.lastLoadedOrSaved;
    }
    async resaveAfterMove(newParentPath) {
        this._parentPath = newParentPath;
        for (const fileName in this.files) {
            const file = this.files[fileName];
            if (file) {
                await file.resaveAfterMove();
            }
        }
        for (const folderName in this.folders) {
            const folder = this.folders[folderName];
            if (folder) {
                await folder.resaveAfterMove(this.fullPath);
            }
        }
        if (this._lastLoadedPath === undefined) {
            return;
        }
        if (this._lastLoadedPath !== StorageUtilities_1.default.ensureEndsWithDelimiter(this.fullPath)) {
            const oldPath = this._lastLoadedPath;
            await this.save(true);
            await localforage_1.default.removeItem(oldPath);
        }
    }
    async save(force) {
        // we need to load before we save if the only thing this folder has seen is a bunch of
        // /ensures/
        if (!this.isLoaded) {
            await this.load(false);
        }
        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) {
            this._lastSavedContent = saveContent;
            this._lastLoadedPath = this.fullPath + BrowserStorage_1.default.slashFolderDelimiter;
            await localforage_1.default.setItem(this.fullPath + BrowserStorage_1.default.slashFolderDelimiter, saveContent);
        }
        return this.lastLoadedOrSaved;
    }
    async saveAll(force) {
        // Log.verbose("Saving all at " + this.storageRelativePath);
        if (this.isDisposed) {
            Log_1.default.throwIsDisposed();
        }
        const initialDateTime = new Date().getTime();
        let needsContentSave = false;
        for (const fileName in this.files) {
            const file = this.files[fileName];
            if (file !== undefined && (file.needsSave || force)) {
                let newContentSaveDate = await file.saveContent(force, true);
                if (newContentSaveDate.getTime() > initialDateTime) {
                    needsContentSave = true;
                }
            }
        }
        for (const folderName in this.folders) {
            const folder = this.folders[folderName];
            if (folder !== undefined && !folder.errorStatus) {
                needsContentSave = true;
                await folder.saveAll(force);
            }
        }
        if (needsContentSave || this._lastSavedContent === undefined || force === true) {
            await this.save(force);
        }
        return true;
    }
}
exports.default = BrowserFolder;

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