import { isString } from "../stringUtils";
import { TreeNode } from "./TreeNode";
import { getFocusableId } from "./utils";

export class Tree {
  private tree: TreeNode[];
  constructor(focusManagerTree: TreeNode[] = []) {
    this.tree = focusManagerTree;
  }

  add(component, parentFocusable, isFocusableCell) {
    const focusableId = getFocusableId(component);
    const parentId = getFocusableId(parentFocusable);
    const focusableComponentInTree = this.find(focusableId);

    // update node if it already exists
    if (focusableComponentInTree) {
      focusableComponentInTree.updateNode(component);
    }

    if (parentFocusable?.current) {
      if (!this.find(parentId)) {
        this.tree.push(new TreeNode(null, parentId, null, isFocusableCell));
      }

      const parentNode = this.find(parentId);

      if (parentNode) {
        if (focusableComponentInTree) {
          focusableComponentInTree.isFocusableCell = isFocusableCell;
          focusableComponentInTree.parentId = parentNode.id;

          parentNode.addChild(focusableComponentInTree);

          // remove root object from the list
          this.tree = this.tree.filter(
            (node) => node !== focusableComponentInTree
          );
        } else {
          parentNode.addChild(component, focusableId, isFocusableCell);
        }
      }
    }
  }

  remove(focusableId: string) {
    this.tree = this.tree.reduce((acc: TreeNode[], node: TreeNode) => {
      if (node.id === focusableId) {
        return acc;
      } else {
        node.children = node.removeNode(focusableId);
        acc.push(node);

        return acc;
      }
    }, []);
  }

  find(id: string) {
    for (const node of this.tree) {
      const foundNode = node.findNode(id);

      if (foundNode) {
        return foundNode;
      }
    }

    return null;
  }

  findNode(component) {
    return this.find(
      isString(component) ? component : getFocusableId(component)
    );
  }

  findParent(id: string) {
    const node = this.find(id);

    if (node && node.parentId) {
      return this.find(node.parentId);
    }

    return null;
  }
}
