import { Item, MerkleTreeLeafNode, MerkleTreeLeafNodeList, MerkleTreeLayerNodeList, getItemSignal, Transaction, ID, MerkleTreeLayerNode, MerkleTreeHashNode } from "../internals"

/**
 * 添加Merkle Tree Leaf Node
 * @param {ByzantineStore} store
 * @param {number} client
 *
 * @function
 */
export const initialByzantineClientStore = (store, client) => {
    const merkleTreeLeafNodeList = new MerkleTreeLeafNodeList()
    const merkleTreeLayerNodeList = new MerkleTreeLayerNodeList()
    const byzantineClientStore = {
        leafNodeList: merkleTreeLeafNodeList,
        layerNodeList: merkleTreeLayerNodeList,
        merkleTreeRoot: null
    }
    store.clients.set(client, byzantineClientStore)
    return byzantineClientStore
}

/**
 * @param {Map<number, Array<MerkleTreeLeafNode>>} leafNodesMap
 * @param {ByzantineStore} store 
 */
export const addLeafNodesToMerkleTree = (leafNodesMap, store) => {
    const clients = leafNodesMap.keys()
    for(let client of clients){
        // 如果没有该client的拜占庭store，则初始化一个
        let byzantineClientStore = store.clients.get(client)
        if (byzantineClientStore === undefined) {
            byzantineClientStore = initialByzantineClientStore(store, client)
        }

        // 获取其层节点列表
        const layerNodeList = byzantineClientStore.layerNodeList
        // 获取其需要加入树的叶子结点列表
        const clientLeafNodes = leafNodesMap.get(client)
        // 如果没有列表，则直接跳过
        if(!clientLeafNodes || clientLeafNodes.length === 0) continue
        /**
         * 当前需要加入树的节点列表
         * @type {Array<MerkleTreeHashNode | MerkleTreeLeafNode>}
        */
        let hashNodeQueue = clientLeafNodes
        /**
         * 下一层需要加入树的节点列表
        * @type {Array<MerkleTreeHashNode | MerkleTreeLeafNode>}
        */
        let nextHashNodeQueue = []
        // 如果没有任何层，则初始化一层
        if(layerNodeList.length === 0 && hashNodeQueue.length){
            const newLayerNode = new MerkleTreeLayerNode()
            layerNodeList.arr.push(newLayerNode)
        }
        // 设置该层的最后一个hash节点
        let lastHashNode

        for(let layer = 0; layer < layerNodeList.length ; layer ++){
            // 如果节点列表为空，则认为这层已经没有需要加入的新节点了，结束
            if(!hashNodeQueue || hashNodeQueue.length === 0) break
            /**
             * 获取该层的hash节点列表
             * @type {MerkleTreeLayerNode}
             */
            let layerNode = layerNodeList.arr[layer]
            // 如果该层节点不存在
            if(!layerNode){
                // 初始化该层
                layerNode = new MerkleTreeLayerNode()
                // 设置该层节点
                layerNodeList.arr[layer] = layerNode
            }
            
            // 如果该层节点为空，则初始化
            if(layerNode.length === 0){
                lastHashNode = new MerkleTreeHashNode()
                // 层节点加入该节点
                layerNode.arr.push(lastHashNode)
                // 下一层需要处理该节点
                nextHashNodeQueue.push(lastHashNode)
            } else {
                // 直接获取最后一个节点
                lastHashNode = layerNode.arr[layerNode.length - 1]
            }
            
            /**
             * 从待加入树的节点中获取指定位置的节点
             * @type {MerkleTreeLeafNode | MerkleTreeHashNode | null | undefined}
             */
            let leafNode = hashNodeQueue.shift()
            while(leafNode){
                // ash节点已完全, 创建新的hash节点
                if(lastHashNode.left && lastHashNode.right){
                    lastHashNode = new MerkleTreeHashNode()
                    layerNode.arr.push(lastHashNode)
                    nextHashNodeQueue.push(lastHashNode)
                }

                if(!lastHashNode.left){
                    // 左孩子没有，就加左孩子
                    lastHashNode.left = leafNode
                } else if(!lastHashNode.right){
                    // 否则右孩子没有，就加右孩子
                    lastHashNode.right = leafNode
                }

                // 如果还有剩下的没有加入的节点，就继续循环
                if(hashNodeQueue.length){
                    leafNode = hashNodeQueue.shift()
                } else {
                    // 节点已经全部遍历完毕，该层遍历结束
                    leafNode = null
                }
            }
            
            // 切换待加入的节点列表
            hashNodeQueue = nextHashNodeQueue
            nextHashNodeQueue = []

            // 如果已经到达顶层，但是还有没有加入的节点，那么就创建新层，继续遍历
            if(layer === layerNodeList.length - 1 && hashNodeQueue.length){
                // 如果层已经到头，但是还有新的hashNode需要合并
                const newLayerNode = new MerkleTreeLayerNode()
                layerNodeList.arr.push(newLayerNode)
            }
        }

        // 设置merkletree root节点，每一次数据同步时需要传递该节点确定历史是否一致
        byzantineClientStore.merkleTreeRoot = lastHashNode
    }
}

/**
 * 在TX结束时进行Merkle Tree增量构建
 * @param {Transaction} transaction
 * @param {ID} structId
 * @param {Array<MerkleTreeLeafNode>} leafNodes 
 */
export const addPendingMerkleTreeLeafNodesToTransaction = (transaction, structId ,leafNodes) => {
    let pendingMerkleTreeLeafNodes = transaction.pendingMerkleTreeLeafNodes.get(structId.client)
    if(!pendingMerkleTreeLeafNodes){
        pendingMerkleTreeLeafNodes = []
        transaction.pendingMerkleTreeLeafNodes.set(structId.client, pendingMerkleTreeLeafNodes)
    }
    pendingMerkleTreeLeafNodes.push(...leafNodes)
}

/**
 * 添加Merkle Tree Leaf Node
 * @param {ByzantineStore} store
 * @param {Item} struct
 * @param {Transaction} transaction
 *
 * @function
 */
export const addMerkleTreeLeafNodes = (store, struct, transaction) => {
    let byzantineClientStore = store.clients.get(struct.id.client)
    if (byzantineClientStore === undefined) {
        byzantineClientStore = initialByzantineClientStore(store, struct.id.client)
    }
    const len = struct.length
    const signalArray = getItemSignal(struct, 'array')
    const leafNodeList = []
    for(let i = 0 ; i < len ; i ++){
        leafNodeList.push(new MerkleTreeLeafNode(struct, i, signalArray[i]))
    }
    byzantineClientStore.leafNodeList.add(...leafNodeList)
    addPendingMerkleTreeLeafNodesToTransaction(transaction, struct.id, leafNodeList)
    return leafNodeList
}

export class ByzantineStore {
    constructor () {
        /**
         * @type {Map<number,{
         *   leafNodeList: MerkleTreeLeafNodeList,
         *   layerNodeList: MerkleTreeLayerNodeList,
         *   merkleTreeRoot: MerkleTreeHashNode | null | undefined,
         * }>}
         */
        this.clients = new Map()
      }
}