import createDbHash from './dbHash.js';
export const destructureTreeKey = (key) => [
    (key / 4294967296) | 0, // typeId
    (key >>> 31) * 2147483648 + (key & 0x7fffffff), // start_node_id
];
export const makeTreeKey = (typeId, start) => typeId * 4294967296 + start;
export const nodeId2Start = (blockCapacity, nodeId) => ((nodeId - +!(nodeId % blockCapacity)) / blockCapacity) | 0;
const nodeId2BlockI = (nodeId, blockCapacity) => ((nodeId - 1) - ((nodeId - 1) % blockCapacity)) / blockCapacity;
export const makeTreeKeyFromNodeId = (typeId, blockCapacity, nodeId) => {
    const tmp = nodeId - +!(nodeId % blockCapacity);
    return typeId * 4294967296 + ((tmp / blockCapacity) | 0) * blockCapacity + 1;
};
const HASH_SIZE = 16;
export class VerifTree {
    #types;
    #h = createDbHash();
    constructor(schemaTypesParsed) {
        this.#types = VerifTree.#makeTypes(schemaTypesParsed);
    }
    static #makeTypes(schemaTypesParsed) {
        return Object.preventExtensions(Object.keys(schemaTypesParsed)
            .sort((a, b) => schemaTypesParsed[a].id - schemaTypesParsed[b].id)
            .reduce((obj, key) => {
            const def = schemaTypesParsed[key];
            const typeId = def.id;
            obj[typeId] = {
                typeId,
                blockCapacity: def.blockCapacity,
                blocks: [],
            };
            return obj;
        }, {}));
    }
    *types() {
        for (const k of Object.keys(this.#types)) {
            yield this.#types[k];
        }
    }
    foreachBlock(cb) {
        for (const k of Object.keys(this.#types)) {
            const { blocks } = this.#types[k];
            for (let block of blocks) {
                if (block)
                    cb(block);
            }
        }
    }
    get hash() {
        this.#h.reset();
        this.foreachBlock((block) => this.#h.update(block.hash));
        return this.#h.digest();
    }
    update(key, hash, inmem = true) {
        const [typeId, start] = destructureTreeKey(key);
        const type = this.#types[typeId];
        if (!type) {
            throw new Error(`type ${typeId} not found`);
        }
        const blockI = nodeId2BlockI(start, type.blockCapacity);
        const block = type.blocks[blockI] ?? (type.blocks[blockI] = Object.preventExtensions({ key, hash, inmem, loadPromise: null }));
        block.hash = hash;
        block.inmem = inmem;
    }
    remove(key) {
        const [typeId, start] = destructureTreeKey(key);
        const type = this.#types[typeId];
        if (!type) {
            throw new Error(`type ${typeId} not found`);
        }
        const blockI = nodeId2BlockI(start, type.blockCapacity);
        delete type.blocks[blockI];
    }
    static blockSdbFile(typeId, start, end) {
        return `${typeId}_${start}_${end}.sdb`;
    }
    getBlock(key) {
        const [typeId, start] = destructureTreeKey(key);
        const type = this.#types[typeId];
        if (!type) {
            throw new Error(`type ${typeId} not found`);
        }
        const blockI = nodeId2BlockI(start, type.blockCapacity);
        return type.blocks[blockI];
    }
    getBlockFile(block) {
        const [typeId, start] = destructureTreeKey(block.key);
        const type = this.#types[typeId];
        if (!type) {
            throw new Error(`type ${typeId} not found`);
        }
        const end = start + type.blockCapacity - 1;
        return VerifTree.blockSdbFile(typeId, start, end);
    }
    updateTypes(schemaTypesParsed) {
        const oldTypes = this.#types;
        const newTypes = VerifTree.#makeTypes(schemaTypesParsed);
        for (const k of Object.keys(oldTypes)) {
            const oldType = oldTypes[k];
            const newType = newTypes[k];
            if (newType) {
                newType.hash = oldType.hash;
                newType.blocks = oldType.blocks;
            }
        }
        this.#types = newTypes;
    }
}
//# sourceMappingURL=tree.js.map