import { Node } from "../types";
import { formatGetNodeGeneralization } from "./NodeTools"
import { createUid } from "./Number";
import { simpleDeepClone } from "./Object"



type WalkCallback = (
  node: Node,
  parent: Node | null,
  isRoot: boolean,
  layerIndex: number,
  index: number,
  ancestors: Node[]
) => boolean | void;

type BFSCallback = (node: Node, parent: Node | null) => 'stop' | void;

export const walk = (
  root: Node,
  parent: Node | null,
  beforeCallback?: WalkCallback,
  afterCallback?: WalkCallback,
  isRoot: boolean = false,
  layerIndex: number = 0,
  index: number = 0,
  ancestors: Node[] = []
): void => {
  let stop = false;
  if (beforeCallback) {
    stop = !!beforeCallback(root, parent, isRoot, layerIndex, index, ancestors);
  }
  if (!stop && root.children && root.children.length > 0) {
    const _layerIndex = layerIndex + 1;
    root.children.forEach((node, nodeIndex) => {
      walk(
        node,
        root,
        beforeCallback,
        afterCallback,
        false,
        _layerIndex,
        nodeIndex,
        [...ancestors, root]
      );
    });
  }
  afterCallback?.call(null, root, parent, isRoot, layerIndex, index, ancestors);
};

export const bfsWalk = (root: Node, callback: BFSCallback): void => {
  const stack: Node[] = [root];
  let isStop = false;

  if (callback(root, null) === 'stop') {
    isStop = true;
  }

  while (stack.length) {
    if (isStop) break;

    const cur = stack.shift()!;
    if (cur.children?.length) {
      cur.children.forEach(item => {
        if (isStop) return;
        stack.push(item);
        if (callback(item, cur) === 'stop') {
          isStop = true;
        }
      });
    }
  }
};

export const copyRenderTree = (
  tree: Node,
  root: Node,
  removeActiveState: boolean = false
): Node => {
  tree.data = simpleDeepClone(root.data);
  if (removeActiveState) {
    tree.data.isActive = false;
    const generalizationList = formatGetNodeGeneralization(tree.data);
    generalizationList.forEach(item => {
      item.isActive = false;
    });
  }

  tree.children = [];
  if (root.children?.length > 0) {
    root.children.forEach((item, index) => {
      tree.children![index] = copyRenderTree({} as Node, item, removeActiveState);
    });
  }

  Object.keys(root).forEach(key => {
    if (!['data', 'children'].includes(key) && !/^_/.test(key)) {
      tree[key] = root[key];
    }
  });

  return tree;
};

export const copyNodeTree = (
  tree: Node,
  root: Node,
  removeActiveState: boolean = false,
  removeId: boolean = true
): Node => {
  const rootData = root.nodeData ? root.nodeData : root;
  tree.data = simpleDeepClone((rootData as any).data);

  if (removeId) {
    delete tree.data.uid;
  } else if (!tree.data.uid) {
    tree.data.uid = createUid();
  }

  if (removeActiveState) {
    tree.data.isActive = false;
  }

  tree.children = [];
  if (root.children?.length > 0) {
    root.children.forEach((item, index) => {
      tree.children![index] = copyNodeTree({} as Node, item, removeActiveState, removeId);
    });
  } else if (root.nodeData?.children?.length > 0) {
    root.nodeData.children.forEach((item, index) => {
      tree.children![index] = copyNodeTree({} as Node, item, removeActiveState, removeId);
    });
  }

  Object.keys(rootData).forEach(key => {
    if (!['data', 'children'].includes(key) && !/^_/.test(key)) {
      tree[key] = (rootData as any)[key];
    }
  });

  return tree;
};