class ApiLayoutItemTreeBuilder {
  static createTree(layoutItems) {
    layoutItems.sort((a, b) => a.loutItemSerno - b.loutItemSerno);
    const rootId = layoutItems.length === 0 ? 0 : parseInt(layoutItems[0].parent);
    const root = {id: rootId, children: [], loutItemSerno: 0, loutItemName: 'Root', loutItemDesc: 'Root', loutItemType: '', loutItemDepth: 0, parent: null};

    const nodeMap = new Map();
    nodeMap.set(rootId, root);

    for (const item of layoutItems) {
      if (['Grid', 'Group'].includes(item.loutItemType)) {
        if (item.loutItemOccCnt !== null) {
          if (item.loutItemOccCnt === '*') {
            let refItem = layoutItems.find(p => p.loutItemName === item.loutItemOccRef);
            if (refItem) {
              item.childOccCnt = parseInt(refItem.value, 10);
            }
          } else {
            item.childOccCnt = parseInt(item.loutItemOccCnt, 10);
          }
        }
      }

      if (nodeMap.has(parseInt(item.parent))) {
        nodeMap.get(parseInt(item.parent)).children.push(item);
        // console.log(item.id + ' to ' + item.parent);
      } else {
        console.log('Parent not found: ' + item.parent);
      }

      nodeMap.set(item.id, item);
    }

    return root;
  }

  static moveNode(root, node, moveUp) {
    const parent = this.getParentNode(root, node);
    if (parent === null) {
      // Node is the root node, cannot be moved
      return;
    }

    const siblings = parent.children;
    const currentIndex = siblings.indexOf(node);
    if (currentIndex === -1) {
      // Node not found in parent's children list
      return;
    }

    const newIndex = moveUp ? currentIndex - 1 : currentIndex + 1;
    if (newIndex >= 0 && newIndex < siblings.length) {
      // Swap the positions of the current node and the node at the new index
      [siblings[currentIndex], siblings[newIndex]] = [siblings[newIndex], siblings[currentIndex]];
    }
  }

  static getParentNode(root, node) {
    if (node.parent === null) {
      return null; // Node is the root node
    }

    return this.getNodeById(root, node.parent);
  }

  static getNodeIndex(parent, node) {
    return parent.children.indexOf(node);
  }

  static getNodeById(node, id) {
    if (node.id == id) {
      return node;
    }

    for (const child of node.children) {
      const found = this.getNodeById(child, id);
      if (found !== null) {
        return found;
      }
    }

    return null;
  }

  static findParent(root, node) {
    if (root === null || node === null) {
      return null; // Invalid input
    }

    if (root.children.includes(node)) {
      return root; // The root is the parent of the node
    }

    for (const child of root.children) {
      const parent = this.findParent(child, node);
      if (parent !== null) {
        return parent;
      }
    }

    return null; // Node not found in the tree
  }

  static printTree(root) {
    console.log('====================================');
    this.printNode(root, 0);
    console.log('====================================');
  }

  static printNode(node, level) {
    if (node === null) {
      return;
    }

    // Print indentation based on the level
    let indent = '';
    for (let i = 0; i < level; i++) {
      indent += '    ';
    }

    // Print the node information
    console.log(`${indent}${node.id}: ${node.loutName}[${node.loutItemType}, Level: ${node.loutItemDepth}, Serno: ${node.loutItemSerno}]`);

    // Recursively print the children
    if (node.children.length > 0) {
      for (const child of node.children) {
        this.printNode(child, level + 1);
      }
    }
  }

  static moveNodeDepth(root, node, newDepth) {
    if (node === null || root === null || newDepth < 0) {
      // Invalid input
      return;
    }

    const parent = this.getParentNode(root, node);
    if (parent === null) {
      // Node is the root node, cannot be moved to a different level
      return;
    }

    const currentDepth = node.loutItemDepth;
    const nodeIndex = this.getNodeIndex(parent, node);
    const nextSibling = this.findClosestSibling(parent.children, nodeIndex);
    parent.children.splice(nodeIndex, 1);

    if (newDepth > currentDepth) {
      // Moving to a higher depth
      if (nextSibling !== null) {
        nextSibling.children.push(node);
        node.parent = nextSibling.id;
        node.loutItemDepth = newDepth;
        this.adjustChildrenDepth(node, newDepth + 1);
      } else {
        // No next sibling found, add the node back to the original parent
        parent.children.push(node);
      }
    } else {
      // Moving to a lower depth
      const newParent = this.findParent(root, parent);
      if (newParent !== null) {
        newParent.children.push(node);
        node.parent = newParent.id;
        node.loutItemDepth = newDepth;
        this.adjustChildrenDepth(node, newDepth + 1);
      } else {
        // No parent found at the desired level, add the node back to the original parent
        parent.children.push(node);
      }
    }
  }

  static adjustChildrenDepth(node, depth) {
    for (const child of node.children) {
      child.loutItemDepth = depth;
      this.adjustChildrenDepth(child, depth + 1);
    }
  }

  static findNextSibling(siblings) {
    if (siblings === null || siblings.length === 0) {
      return null;
    }
    return siblings[0];
  }

  static findClosestSibling(siblings, nodeIndex) {
    if (siblings === null || siblings.length === 0) {
      return null;
    }
    if (nodeIndex < 1 || nodeIndex >= siblings.length) {
      return this.findNextSibling(siblings);
    }
    return siblings[nodeIndex - 1];
  }
}

export default ApiLayoutItemTreeBuilder;
