const { Buffer } = require('buffer');
var BinaryTreeElement = require('../binarytree').BinaryTreeElement;
var Node = require('../binarytree').Node;
var Leaf = require('../binarytree').Leaf;
var EmptyLeaf = require('../binarytree').EmptyLeaf;
var BinaryTree = require('../binarytree').BinaryTree;
var ProofHashedLeaf = require('./merkleprooftree').ProofHashedLeaf;
var ProofValueLeaf = require('./merkleprooftree').ProofValueLeaf;
var MerkleProofElement = require('./merkleprooftree').MerkleProofElement;
var MerkleProofTree = require('./merkleprooftree').MerkleProofTree;
var ProofNodeArrayHead = require('./merkleprooftree').ProofNodeArrayHead;
var ProofNodeDictHead = require('./merkleprooftree').ProofNodeDictHead;
var PathLeafElement = require('../path').PathLeafElement;
var SubTreeRootNode = require('../binarytree').SubTreeRootNode;
var ArrayHeadNode = require('../binarytree').ArrayHeadNode;
var DictHeadNode = require('../binarytree').DictHeadNode;
var ProofNodeSimple = require('./merkleprooftree').ProofNodeSimple;
const EMPTY_HASH = new Buffer.alloc(32);
/**
 *
 */
function MerkleProofTreeFactory() { }
/**
 * @param {BinaryTree} originalTree
 */
MerkleProofTreeFactory.prototype.buildFromBinaryTree = function (originalTree, calculator) {
    var rootElem = this.buildFromBinaryTreeInternal(originalTree.root, calculator);
    return new MerkleProofTree(rootElem);
};
/**
 * @param {BinaryTreeElement} elem
 * @param {*} calculator
 */
MerkleProofTreeFactory.prototype.buildFromBinaryTreeInternal = function (elem, calculator) {
    if (elem instanceof EmptyLeaf) {
        return new ProofHashedLeaf(EMPTY_HASH);
    }
    else if (elem instanceof Leaf) {
        var pathElem = elem.pathElem;
        if (pathElem != null) {
            if (pathElem instanceof PathLeafElement) {
                return new ProofValueLeaf(elem.content, pathElem.previous);
            }
            else {
                throw new Error("The path and structure don't match. We are at a leaf, but path elem is not a leaf: " + pathElem);
            }
        }
        else {
            // make a hash
            var hash = calculator.calculateLeafHash(elem.content);
            return new ProofHashedLeaf(hash);
        }
    }
    else if (elem instanceof SubTreeRootNode) {
        var pathElem = elem.pathElem;
        if (pathElem != null) {
            if (pathElem instanceof PathLeafElement) {
                // Don't convert it
                return new ProofValueLeaf(elem.content, pathElem.previous);
            }
            else {
                return this.convertNode(elem, calculator);
            }
        }
        else {
            return this.convertNode(elem, calculator);
        }
    }
    else if (elem instanceof Node) {
        return this.convertNode(elem, calculator);
    }
    else {
        throw new Error("Cannot handle " + elem);
    }
};
/**
 * @param {Node} node
 */
MerkleProofTreeFactory.prototype.convertNode = function (node, calculator) {
    var left = this.buildFromBinaryTreeInternal(node.left, calculator);
    var right = this.buildFromBinaryTreeInternal(node.right, calculator);
    if (left instanceof ProofHashedLeaf && right instanceof ProofHashedLeaf) {
        var addedHash = calculator.calculateNodeHash(node.getPrefixByte(), left.merkleHash, right.merkleHash);
        return new ProofHashedLeaf(addedHash);
    }
    else {
        return this.buildNodeOfCorrectType(node, left, right);
    }
};
/**
 * @param {ArrayHeadNode} node
 */
MerkleProofTreeFactory.prototype.extractSearchablePathElement = function (node) {
    var pathElem = node.pathElem;
    if (pathElem != null) {
        return pathElem.previous;
    }
    else {
        return null;
    }
};
/**
 * @param {Node} node
 * @param {MerkleProofElement} left
 * @param {MerkleProofElement} right
 */
MerkleProofTreeFactory.prototype.buildNodeOfCorrectType = function (node, left, right) {
    if (node instanceof ArrayHeadNode) {
        return new ProofNodeArrayHead(left, right, this.extractSearchablePathElement(node));
    }
    else if (node instanceof DictHeadNode) {
        return new ProofNodeDictHead(left, right, this.extractSearchablePathElement(node));
    }
    else if (node instanceof Node) {
        return new ProofNodeSimple(left, right);
    }
    else {
        throw new Error("Should have taken care of this node type: " + node);
    }
};
module.exports = { MerkleProofTreeFactory };
//# sourceMappingURL=merkleprooftreefactory.js.map