import { jsx as _jsx } from "react/jsx-runtime";
import { FixedSizeList, VariableSizeList } from "react-window";
import { useDataUpdates, useTreeApi } from "../context";
import { focusNextElement, focusPrevElement } from "../utils";
import { ListOuterElement } from "./list-outer-element";
import { ListInnerElement } from "./list-inner-element";
import { RowContainer } from "./row-container";
let focusSearchTerm = "";
let timeoutId = null;
/**
 * All these keyboard shortcuts seem like they should be configurable.
 * Each operation should be a given a name and separated from
 * the event handler. Future clean up welcome.
 */
export function DefaultContainer() {
    useDataUpdates();
    const tree = useTreeApi();
    return (_jsx("div", { role: "tree", "aria-label": tree.props["aria-label"], "aria-labelledby": tree.props["aria-labelledby"], "aria-multiselectable": !tree.props.disableMultiSelection || undefined, style: {
            height: tree.height,
            width: tree.width,
            minHeight: 0,
            minWidth: 0,
        }, onContextMenu: tree.props.onContextMenu, onClick: tree.props.onClick, tabIndex: 0, onFocus: (e) => {
            if (!e.currentTarget.contains(e.relatedTarget)) {
                tree.onFocus();
            }
        }, onBlur: (e) => {
            if (!e.currentTarget.contains(e.relatedTarget)) {
                tree.onBlur();
            }
        }, onKeyDown: (e) => {
            var _a;
            if (tree.isEditing) {
                return;
            }
            if (e.key === "Backspace") {
                if (!tree.props.onDelete)
                    return;
                const ids = Array.from(tree.selectedIds);
                if (ids.length > 1) {
                    let nextFocus = tree.mostRecentNode;
                    while (nextFocus && nextFocus.isSelected) {
                        nextFocus = nextFocus.nextSibling;
                    }
                    if (!nextFocus)
                        nextFocus = tree.lastNode;
                    tree.focus(nextFocus, { scroll: false });
                    tree.delete(Array.from(ids));
                }
                else {
                    const node = tree.focusedNode;
                    if (node) {
                        const sib = node.nextSibling;
                        const parent = node.parent;
                        tree.focus(sib || parent, { scroll: false });
                        tree.delete(node);
                    }
                }
                return;
            }
            if (e.key === "Tab" && !e.shiftKey) {
                e.preventDefault();
                focusNextElement(e.currentTarget);
                return;
            }
            if (e.key === "Tab" && e.shiftKey) {
                e.preventDefault();
                focusPrevElement(e.currentTarget);
                return;
            }
            if (e.key === "ArrowDown") {
                e.preventDefault();
                const next = tree.nextNode;
                if (e.metaKey) {
                    tree.select(tree.focusedNode);
                    tree.activate(tree.focusedNode);
                    return;
                }
                else if (!e.shiftKey || tree.props.disableMultiSelection) {
                    tree.focus(next);
                    return;
                }
                else {
                    if (!next)
                        return;
                    const current = tree.focusedNode;
                    if (!current) {
                        tree.focus(tree.firstNode);
                    }
                    else if (current.isSelected) {
                        tree.selectContiguous(next);
                    }
                    else {
                        tree.selectMulti(next);
                    }
                    return;
                }
            }
            if (e.key === "ArrowUp") {
                e.preventDefault();
                const prev = tree.prevNode;
                if (!e.shiftKey || tree.props.disableMultiSelection) {
                    tree.focus(prev);
                    return;
                }
                else {
                    if (!prev)
                        return;
                    const current = tree.focusedNode;
                    if (!current) {
                        tree.focus(tree.lastNode); // ?
                    }
                    else if (current.isSelected) {
                        tree.selectContiguous(prev);
                    }
                    else {
                        tree.selectMulti(prev);
                    }
                    return;
                }
            }
            if (e.key === "ArrowRight") {
                const node = tree.focusedNode;
                if (!node)
                    return;
                if (node.isInternal && node.isOpen) {
                    tree.focus(tree.nextNode);
                }
                else if (node.isInternal)
                    tree.open(node.id);
                return;
            }
            if (e.key === "ArrowLeft") {
                const node = tree.focusedNode;
                if (!node || node.isRoot)
                    return;
                if (node.isInternal && node.isOpen)
                    tree.close(node.id);
                else if (!((_a = node.parent) === null || _a === void 0 ? void 0 : _a.isRoot)) {
                    tree.focus(node.parent);
                }
                return;
            }
            if (e.key === "a" && (e.metaKey || e.ctrlKey) && !tree.props.disableMultiSelection) {
                e.preventDefault();
                tree.selectAll();
                return;
            }
            if (e.key === "a" && !e.metaKey && !e.ctrlKey && tree.props.onCreate) {
                tree.createLeaf();
                return;
            }
            if (e.key === "A" && !e.metaKey && !e.ctrlKey) {
                if (!tree.props.onCreate)
                    return;
                tree.createInternal();
                return;
            }
            if (e.key === "Home") {
                // add shift keys
                e.preventDefault();
                tree.focus(tree.firstNode);
                return;
            }
            if (e.key === "End") {
                // add shift keys
                e.preventDefault();
                tree.focus(tree.lastNode);
                return;
            }
            if (e.key === "Enter") {
                const node = tree.focusedNode;
                if (!node)
                    return;
                if (!node.isEditable || !tree.props.onRename)
                    return;
                setTimeout(() => {
                    if (node)
                        tree.edit(node);
                });
                return;
            }
            if (e.key === " ") {
                e.preventDefault();
                const node = tree.focusedNode;
                if (!node)
                    return;
                if (node.isLeaf) {
                    node.select();
                    node.activate();
                }
                else {
                    node.toggle();
                }
                return;
            }
            if (e.key === "*") {
                const node = tree.focusedNode;
                if (!node)
                    return;
                tree.openSiblings(node);
                return;
            }
            if (e.key === "PageUp") {
                e.preventDefault();
                tree.pageUp();
                return;
            }
            if (e.key === "PageDown") {
                e.preventDefault();
                tree.pageDown();
            }
            // If they type a sequence of characters
            // collect them. Reset them after a timeout.
            // Use it to search the tree for a node, then focus it.
            // Clean this up a bit later
            clearTimeout(timeoutId);
            focusSearchTerm += e.key;
            timeoutId = setTimeout(() => {
                focusSearchTerm = "";
            }, 600);
            const node = tree.visibleNodes.find((n) => {
                // @ts-ignore
                const name = n.data.name;
                if (typeof name === "string") {
                    return name.toLowerCase().startsWith(focusSearchTerm);
                }
                else
                    return false;
            });
            if (node)
                tree.focus(node.id);
        }, children: _jsx(List, {}) }));
}
/**
 * Fixed-height trees (numeric rowHeight) render a FixedSizeList, preserving the
 * original O(1) layout and avoiding VariableSizeList's measurement cache. Only
 * the function form, which needs per-row heights, uses VariableSizeList.
 */
function List() {
    var _a, _b;
    const tree = useTreeApi();
    const commonProps = {
        className: tree.props.className,
        outerRef: tree.listEl,
        itemCount: tree.visibleNodes.length,
        height: tree.height,
        width: tree.width,
        overscanCount: tree.overscanCount,
        itemKey: (index) => { var _a; return ((_a = tree.visibleNodes[index]) === null || _a === void 0 ? void 0 : _a.id) || index; },
        outerElementType: (_a = tree.props.outerElementType) !== null && _a !== void 0 ? _a : ListOuterElement,
        innerElementType: (_b = tree.props.innerElementType) !== null && _b !== void 0 ? _b : ListInnerElement,
        onScroll: tree.props.onScroll,
        onItemsRendered: tree.onItemsRendered.bind(tree),
    };
    if (typeof tree.props.rowHeight === "function") {
        return (
        // @ts-ignore
        _jsx(VariableSizeList, Object.assign({}, commonProps, { itemSize: tree.rowHeightAt, ref: tree.list, children: RowContainer })));
    }
    return (
    // @ts-ignore
    _jsx(FixedSizeList, Object.assign({}, commonProps, { itemSize: tree.rowHeight, ref: tree.list, children: RowContainer })));
}
