"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.collapseNode = collapseNode;
exports.expandNode = expandNode;
exports.flattenTree = flattenTree;
exports.getDummyNode = getDummyNode;
exports.getFocusableNodes = getFocusableNodes;
exports.getNavigationFn = exports.getKeyActionFnSearch = exports.getKeyActionFnMenu = exports.getKeyActionFnDefault = void 0;
exports.getNextMenuNode = getNextMenuNode;
exports.getNextNode = getNextNode;
exports.getNodesMap = getNodesMap;
exports.getPositionInSet = getPositionInSet;
exports.getPrevMenuNode = getPrevMenuNode;
exports.getPrevNode = getPrevNode;
exports.getSelectionFn = void 0;
exports.getSetSize = getSetSize;
exports.isNodeVisible = isNodeVisible;
exports.moveFocusToChild = moveFocusToChild;
exports.moveFocusToFirstMenuNode = moveFocusToFirstMenuNode;
exports.moveFocusToFirstNode = moveFocusToFirstNode;
exports.moveFocusToLastMenuNode = moveFocusToLastMenuNode;
exports.moveFocusToLastNode = moveFocusToLastNode;
exports.moveFocusToNextMenuNode = moveFocusToNextMenuNode;
exports.moveFocusToNextNode = moveFocusToNextNode;
exports.moveFocusToParent = moveFocusToParent;
exports.moveFocusToPrevMenuNode = moveFocusToPrevMenuNode;
exports.moveFocusToPrevNode = moveFocusToPrevNode;
exports.onCustomButtonPress = onCustomButtonPress;
exports.setAllNodesState = setAllNodesState;
exports.setNodeState = setNodeState;
exports.toggleSelect = toggleSelect;
var _constants = require("./constants");
function flatten(tree, map, acc, parents) {
  tree.forEach(n => {
    const {
      children,
      node
    } = map(n, parents);
    acc.push(node);
    flatten(children, map, acc, [node.id, ...parents]);
  });
}
function flattenTree(tree, map) {
  const nodes = [];
  flatten(tree, map, nodes, []);
  return nodes;
}
function getFocusableNodes(nodes) {
  return nodes.filter(({
    isDisabled
  }) => !isDisabled);
}
function getNodesMap(nodes) {
  const entries = nodes.map(n => [n.id, n]);
  return new Map(entries);
}
function setNodeState(id, patch, nodes) {
  return nodes.map(node => node.id === id ? {
    ...node,
    ...patch
  } : node);
}
function setAllNodesState(patch, nodes) {
  return nodes.map(node => ({
    ...node,
    ...patch
  }));
}
const getKeyActionFnDefault = (key, node) => {
  if (node.isExpandable && (key === _constants.KEYS.RIGHT || key === _constants.KEYS.ENTER)) {
    return node.isExpanded ? moveFocusToChild : expandNode;
  }
  if (key === _constants.KEYS.LEFT) {
    if (node.isExpanded) {
      return collapseNode;
    }
    if (node.parent) {
      return moveFocusToParent;
    }
  }
  if (key === _constants.KEYS.DOWN) {
    return moveFocusToNextNode;
  }
  if (key === _constants.KEYS.UP) {
    return moveFocusToPrevNode;
  }
  if (key === _constants.KEYS.HOME) {
    return moveFocusToFirstNode;
  }
  if (key === _constants.KEYS.END) {
    return moveFocusToLastNode;
  }
  if (key === _constants.KEYS.ENTER || key === _constants.KEYS.SPACE) {
    return toggleSelect;
  }
  return null;
};
exports.getKeyActionFnDefault = getKeyActionFnDefault;
const getNavigationFn = (key, node, context) => {
  if (node.isExpandable && (key === _constants.KEYS.RIGHT || key === _constants.KEYS.ENTER)) {
    return node.isExpanded ? moveFocusToChild : [expandNode, moveFocusToChild];
  }
  const parent = node.parent;
  if (parent) {
    if (key === _constants.KEYS.LEFT || key === _constants.KEYS.ESC) {
      const parentNode = context?.nodesMap.get(parent);
      if (parentNode) {
        return [moveFocusToParent, ({
          context: ctx,
          onToggleExpanded,
          onToggleSelected,
          isSelected,
          onCustomButtonKeyDown,
          button
        }) => collapseNode({
          node: parentNode,
          context: ctx,
          onToggleExpanded,
          onToggleSelected,
          isSelected,
          onCustomButtonKeyDown,
          button
        })];
      }
    }
  } else if (key === _constants.KEYS.ESC || key === _constants.KEYS.TAB) {
    return onCustomButtonPress;
  }
  if (key === _constants.KEYS.DOWN) {
    return moveFocusToNextMenuNode;
  }
  if (key === _constants.KEYS.UP) {
    return moveFocusToPrevMenuNode;
  }
  if (key === _constants.KEYS.HOME) {
    return moveFocusToFirstMenuNode;
  }
  if (key === _constants.KEYS.END) {
    return moveFocusToLastMenuNode;
  }
  return null;
};
exports.getNavigationFn = getNavigationFn;
const getSelectionFn = key => {
  if (key === _constants.KEYS.ENTER || key === _constants.KEYS.SPACE) {
    return toggleSelect;
  }
  return null;
};
exports.getSelectionFn = getSelectionFn;
const getKeyActionFnMenu = (key, node, context) => {
  return getNavigationFn(key, node, context) || getSelectionFn(key, node);
};
exports.getKeyActionFnMenu = getKeyActionFnMenu;
const getKeyActionFnSearch = (key, node, context) => {
  return getNavigationFn(key, node, context);
};

// action functions start
exports.getKeyActionFnSearch = getKeyActionFnSearch;
function moveFocusToChild({
  node,
  context
}) {
  const child = context.nodes.find(n => n.parent === node.id);
  if (child) {
    context.setFocused(child.id);
  }
}
function expandNode({
  node,
  context,
  onToggleExpanded
}) {
  context.setNodes(nodes => {
    const withCurrentExpanded = setNodeState(node.id, {
      isExpanded: true
    }, nodes);
    if (context.isNodeExpandedByDefault) {
      return withCurrentExpanded.map(n => {
        return n.parents.includes(node.id) ? {
          ...n,
          isExpanded: true
        } : n;
      });
    }
    return withCurrentExpanded;
  });
  onToggleExpanded(true);
}
function collapseNode({
  node,
  context,
  onToggleExpanded
}) {
  context.setNodes(nodes => {
    return setNodeState(node.id, {
      isExpanded: false
    }, nodes);
  });
  onToggleExpanded(false);
}
function moveFocusToParent({
  node,
  context
}) {
  const parent = node.parent;
  if (parent) {
    context.setFocused(parent);
  }
}
function moveFocusToNextNode({
  node,
  context
}) {
  const targetNode = getNextNode(node, context.nodes, context.nodesMap);
  if (!targetNode) {
    return;
  }
  context.setFocused(targetNode.id);
}
function moveFocusToNextMenuNode({
  node,
  context
}) {
  const targetNode = getNextMenuNode(node, context.nodes);
  if (!targetNode) {
    return;
  }
  context.setFocused(targetNode.id);
}
function moveFocusToPrevNode({
  node,
  context
}) {
  const targetNode = getPrevNode(node, context.nodes, context.nodesMap);
  if (!targetNode) {
    return;
  }
  context.setFocused(targetNode.id);
}
function moveFocusToPrevMenuNode({
  node,
  context
}) {
  const targetNode = getPrevMenuNode(node, context.nodes);
  if (!targetNode) {
    return;
  }
  context.setFocused(targetNode.id);
}
function moveFocusToFirstNode({
  context
}) {
  const targetNode = context.nodes[0];
  context.setFocused(targetNode.id);
}
function moveFocusToFirstMenuNode({
  context,
  node
}) {
  const targetNode = context.nodes.filter(n => n.parent === node.parent)[0];
  if (targetNode) {
    context.setFocused(targetNode.id);
  }
}
function moveFocusToLastNode({
  context
}) {
  const targetNode = context.nodes[context.nodes.length - 1];
  context.setFocused(targetNode.id);
}
function moveFocusToLastMenuNode({
  context,
  node
}) {
  const targetNode = context.nodes.filter(n => n.parent === node.parent).slice(-1)[0];
  if (targetNode) {
    context.setFocused(targetNode.id);
  }
}
function toggleSelect({
  onToggleSelected,
  isSelected
}) {
  onToggleSelected(!isSelected);
}
function onCustomButtonPress({
  onCustomButtonKeyDown,
  button
}) {
  return onCustomButtonKeyDown?.(button);
}

// action functions end

function isNodeVisible(node, nodesMap) {
  if (!node.parent) {
    return true;
  }
  return node.parents.every(p => !!nodesMap.get(p)?.isExpanded);
}
function getNextNode(node, nodes, nodesMap) {
  const index = nodes.indexOf(node);
  return nodes.find((n, i) => i > index && isNodeVisible(n, nodesMap)) ?? null;
}
function getNextMenuNode(node, nodes) {
  const sNodes = nodes.filter(n => n.parent === node.parent);
  const index = sNodes.indexOf(node);
  return sNodes.find((_, i) => i > index) ?? sNodes[0];
}
function getPrevNode(node, nodes, nodesMap) {
  const index = nodes.indexOf(node);

  // TODO compilerOptions > lib/target update
  // TS2339: Property findLast does not exist on type TreeNode[]
  for (let i = index - 1; i >= 0; i--) {
    if (isNodeVisible(nodes[i], nodesMap)) {
      return nodes[i];
    }
  }
  return null;
  // return (
  //   nodes.findLast((n, i) => i < index && isNodeVisible(n, nodesMap)) ?? null
  // );
}
function getPrevMenuNode(node, nodes) {
  const sNodes = nodes.filter(n => n.parent === node.parent);
  const index = sNodes.indexOf(node);
  return index > 0 ? sNodes[index - 1] : sNodes.slice(-1)[0];
}
function getSetSize(node, nodes, nodesMap) {
  const parentNode = nodesMap.get(node.parent);
  if (parentNode) {
    return parentNode.children.length;
  }
  return nodes.filter(({
    parent
  }) => !parent).length;
}
function getPositionInSet(node, nodes, nodesMap) {
  const parentNode = nodesMap.get(node.parent);
  if (parentNode) {
    return parentNode.children.indexOf(node.id) + 1;
  }
  return nodes.filter(({
    parent
  }) => !parent).findIndex(({
    id
  }) => node.id === id) + 1;
}
function getDummyNode(id) {
  return {
    id,
    children: [],
    parent: null,
    parents: [],
    label: '',
    isDisabled: false,
    isExpanded: false,
    isExpandable: false,
    isFocused: false,
    isLeaf: false
  };
}