import * as check from "../types/check.js";
import * as protocol from "../protocol/index.js";
import * as ipfs from "../../ipfs/index.js";
import * as link from "../link.js";
import { decodeCID } from "../../common/index.js";
import { isObject, hasProp } from "../../common/type-checks.js";
import BareFile from "../bare/file.js";
import BaseTree from "../base/tree.js";
class BareTree extends BaseTree {
    constructor(links) {
        super();
        this.type = "BareTree";
        this.links = links;
        this.children = {};
    }
    static async empty() {
        return new BareTree({});
    }
    static async fromCID(cid) {
        const links = link.arrToMap((await ipfs.ls(cid)).map(link.fromFSFile));
        return new BareTree(links);
    }
    static fromLinks(links) {
        return new BareTree(links);
    }
    static instanceOf(obj) {
        return isObject(obj)
            && hasProp(obj, "links")
            && hasProp(obj, "children")
            && hasProp(obj, "type")
            && obj.type === "BareTree";
    }
    async createChildTree(name, onUpdate) {
        const child = await BareTree.empty();
        const existing = this.children[name];
        if (existing) {
            if (check.isFile(existing)) {
                throw new Error(`There is a file at the given path: ${name}`);
            }
            return existing;
        }
        await this.updateDirectChild(child, name, onUpdate);
        return child;
    }
    async createOrUpdateChildFile(content, name, onUpdate) {
        const existing = await this.getDirectChild(name);
        let file;
        if (existing === null) {
            file = await BareFile.create(content);
        }
        else if (BareFile.instanceOf(existing)) {
            file = await existing.updateContent(content);
        }
        else {
            throw new Error(`There is already a directory with that name: ${name}`);
        }
        await this.updateDirectChild(file, name, onUpdate);
        return file;
    }
    async putDetailed() {
        return protocol.basic.putLinks(this.links);
    }
    async putAndUpdateLink(child, name, onUpdate) {
        const details = await child.putDetailed();
        this.updateLink(name, details);
        onUpdate && await onUpdate();
        return this;
    }
    async updateDirectChild(child, name, onUpdate) {
        this.children[name] = child;
        return this.putAndUpdateLink(child, name, onUpdate);
    }
    removeDirectChild(name) {
        delete this.links[name];
        if (this.children[name]) {
            delete this.children[name];
        }
        return this;
    }
    async getDirectChild(name) {
        if (this.children[name]) {
            return this.children[name];
        }
        const link = this.links[name] || null;
        if (link === null)
            return null;
        const cid = decodeCID(link.cid);
        const child = link.isFile
            ? await BareFile.fromCID(cid)
            : await BareTree.fromCID(cid);
        // check that the child wasn't added while retrieving the content from the network
        if (this.children[name]) {
            return this.children[name];
        }
        this.children[name] = child;
        return child;
    }
    // TODO
    async get(path) {
        const [head, ...nextPath] = path;
        if (!head)
            return this;
        const nextTree = await this.getDirectChild(head);
        if (!nextPath.length) {
            return nextTree;
        }
        else if (nextTree === null || check.isFile(nextTree)) {
            return null;
        }
        return nextTree.get(nextPath);
    }
    updateLink(name, result) {
        const { cid, size, isFile } = result;
        this.links[name] = link.make(name, cid, isFile, size);
        return this;
    }
    getLinks() {
        return this.links;
    }
}
export default BareTree;
//# sourceMappingURL=tree.js.map