/*!
 * Wunderbaum - types
 * Copyright (c) 2021-2023, Martin Wendt. Released under the MIT license.
 * @VERSION, @DATE (https://github.com/mar10/wunderbaum)
 */

import { WunderbaumNode } from "./wb_node";
import { Wunderbaum } from "./wunderbaum";
import { WunderbaumOptions } from "./wb_options";

/** A value that can either be true, false, or undefined. */
export type TristateType = boolean | undefined;
/** Show/hide checkbox or display a radiobutton icon instead. */
export type CheckboxOption = boolean | "radio";
/** An icon may either be
 * a string-tag that references an entry in the `iconMap` (e.g. `"folderOpen"`)),
 * an HTML string that contains a `<` and is used as-is,
 * an image URL string that contains a `.` or `/` and is rendered as `<img src='...'>`,
 * a class string such as `"bi bi-folder"`,
 * or a boolean value that indicates if the default icon should be used or hidden.
 */
export type IconOption = boolean | string;

/*
 * `source` can be a url, an array of nodes, or an object with `url`, ...
 */
export interface SourceAjaxType {
  url: string;
  params?: any;
  body?: any;
  options?: RequestInit;
}
export type SourceListType = Array<WbNodeData>;
export interface SourceObjectType {
  _format?: "nested" | "flat";
  _version?: number;
  types?: NodeTypeDefinitionMap;
  columns?: ColumnDefinitionList;
  children: SourceListType;
  _keyMap?: { [key: string]: string };
  _positional?: Array<string>;
  // _typeList?: Array<string>;
  _valueMap?: { [key: string]: Array<string> };
}

/** Possible initilization for tree nodes. */
export type SourceType =
  | string
  | SourceListType
  | SourceAjaxType
  | SourceObjectType;

/** Passed to `find...()` methods. Should return true if node matches. */
export type MatcherCallback = (node: WunderbaumNode) => boolean;
/** Used for `tree.iconBadge` event. */
export type WbIconBadgeCallback = (
  e: WbIconBadgeEventType
) => WbIconBadgeEventResultType;
/** Passed to `sortChildren()` methods. Should return -1, 0, or 1. */
export type SortCallback = (a: WunderbaumNode, b: WunderbaumNode) => number;
/** When set as option, called when the value is needed (e.g. `colspan` type definition). */
export type BoolOptionResolver = (node: WunderbaumNode) => boolean;
/** When set as option, called when the value is needed (e.g. `icon` type definition). */
export type BoolOrStringOptionResolver = (
  node: WunderbaumNode
) => boolean | string;
/** A callback that receives a node instance and returns an arbitrary value type. */
export type NodeAnyCallback = (node: WunderbaumNode) => any;
/** A callback that receives a node instance and returns a string value. */
export type NodeStringCallback = (node: WunderbaumNode) => string;
/** A callback that receives a node instance and returns an iteration modifier. */
export type NodeVisitCallback = (node: WunderbaumNode) => NodeVisitResponse;
/** A callback that receives a node instance and returns a string value. */
export type NodeVisitResponse = "skip" | boolean | void;
/** A callback that receives a node-data dictionary and a node instance and returns an iteration modifier. */
export type NodeToDictCallback = (
  dict: WbNodeData,
  node: WunderbaumNode
) => NodeVisitResponse;
/** A callback that receives a node instance and returns a string value. */
export type NodeSelectCallback = (node: WunderbaumNode) => boolean | void;

/**
 * See also {@link WunderbaumNode.getOption|WunderbaumNode.getOption()}
 * to evaluate `node.NAME` setting and `tree.types[node.type].NAME`.
 */
export type DynamicBoolOption = boolean | BoolOptionResolver;
export type DynamicStringOption = string | BoolOptionResolver;
export type DynamicBoolOrStringOption =
  | boolean
  | string
  | BoolOrStringOptionResolver;
export type DynamicCheckboxOption = CheckboxOption | BoolOrStringOptionResolver;
export type DynamicIconOption = IconOption | BoolOrStringOptionResolver;

// type WithWildcards<T> = T & { [key: string]: unknown };
/** A plain object (dictionary) that represents a node instance. */
export interface WbNodeData {
  checkbox?: CheckboxOption;
  children?: Array<WbNodeData>;
  classes?: string;
  colspan?: boolean;
  expanded?: boolean;
  icon?: IconOption;
  iconTooltip?: boolean | string;
  key?: string;
  lazy?: boolean;
  /** Make child nodes single-select radio buttons. */
  radiogroup?: boolean;
  refKey?: string;
  selected?: boolean;
  statusNodeType?: NodeStatusType;
  title: string;
  tooltip?: boolean | string;
  type?: string;
  unselectable?: boolean;
  /** @internal */
  _treeId?: string;
  /** Other data is passed to `node.data` and can be accessed via `node.data.NAME` */
  [key: string]: unknown;
}

/* -----------------------------------------------------------------------------
 * EVENT CALLBACK TYPES
 * ---------------------------------------------------------------------------*/

/** A callback that receives a node instance and returns a string value. */
export type WbCancelableEventResultType = false | void;

export interface WbTreeEventType {
  /** Name of the event. */
  type: string;
  /** The affected tree instance. */
  tree: Wunderbaum;
  /** Exposed utility module methods
   * (see [API docs](https://mar10.github.io/wunderbaum/api/modules/util.html)).
   */
  util: any;
  /** Originating HTML event if any (e.g. `click`). */
  event?: Event;
  // [key: string]: unknown;
}

export interface WbNodeEventType extends WbTreeEventType {
  /** The affected target node. */
  node: WunderbaumNode;
  /**
   * Contains the node's type information, i.e. `tree.types[node.type]` if
   * defined. Set to `{}` otherwise. @see {@link Wunderbaum.types}
   */
  typeInfo: NodeTypeDefinition;
}

export interface WbActivateEventType extends WbNodeEventType {
  prevNode: WunderbaumNode;
  /** The original event. */
  event: Event;
}

export interface WbChangeEventType extends WbNodeEventType {
  /** Additional information derived from the original change event. */
  info: WbEventInfo;
  /** The embedded element that fired the change event. */
  inputElem: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
  /** The new value of the embedded element, depending on the input element type. */
  inputValue: any;
  /** Result of `inputElem.checkValidity()`. */
  inputValid: boolean;
}

export interface WbClickEventType extends WbTreeEventType {
  /** The original event. */
  event: MouseEvent;
  /** The clicked node if any. */
  node: WunderbaumNode;
  /** Additional information derived from the original mouse event. */
  info: WbEventInfo;
}

export interface WbDeactivateEventType extends WbNodeEventType {
  nextNode: WunderbaumNode;
  /** The original event. */
  event: Event;
}

export interface WbEditApplyEventType extends WbNodeEventType {
  /** Additional information derived from the original change event. */
  info: WbEventInfo;
  /** The input element of the node title that fired the change event. */
  inputElem: HTMLInputElement;
  /** The previous node title. */
  oldValue: string;
  /** The new node title. */
  newValue: string;
  /** Result of `inputElem.checkValidity()`. */
  inputValid: boolean;
}

export interface WbEditEditEventType extends WbNodeEventType {
  /** The input element of the node title that was just created. */
  inputElem: HTMLInputElement;
}

// export interface WbEnhanceTitleEventType extends WbNodeEventType {
//   titleSpan: HTMLSpanElement;
// }

export interface WbErrorEventType extends WbNodeEventType {
  error: any;
}

export interface WbExpandEventType extends WbNodeEventType {
  flag: boolean;
}

export interface WbFocusEventType extends WbTreeEventType {
  /** The original event. */
  event: FocusEvent;
  /** True if `focusin`, false if `focusout`. */
  flag: boolean;
}

export interface WbIconBadgeEventType extends WbNodeEventType {
  iconSpan: HTMLElement;
}

export interface WbIconBadgeEventResultType {
  /** Content of the badge `<span class='wb-badge'>` if any. */
  badge: string | number | HTMLSpanElement | null | false;
  /** Additional class name(s), separate with space. */
  badgeClass?: string;
  /** Tooltip for the badge. */
  badgeTooltip?: string;
}

export interface WbInitEventType extends WbTreeEventType {
  error?: any;
}

export interface WbKeydownEventType extends WbTreeEventType {
  /** The original event. */
  event: KeyboardEvent;
  node: WunderbaumNode;
  /** Additional information derived from the original keyboard event. */
  info: WbEventInfo;
}

export interface WbModifyChildEventType extends WbNodeEventType {
  /** Type of change: 'add', 'remove', 'rename', 'move', 'data', ... */
  operation: string;
  child: WunderbaumNode;
}

export interface WbReceiveEventType extends WbNodeEventType {
  response: any;
}

export interface WbSelectEventType extends WbNodeEventType {
  flag: boolean;
}

export interface WbRenderEventType extends WbNodeEventType {
  /**
   * True if the node's markup was not yet created. In this case the render
   * event should create embedded input controls (in addition to update the
   * values according to to current node data). <br>
   * False if the node's markup was already created. In this case the render
   * event should only update the values according to to current node data.
   */
  isNew: boolean;
  /** The node's `<span class='wb-node'>` element. */
  nodeElem: HTMLSpanElement;
  /** True if the node only displays the title and is stretched over all remaining columns. */
  isColspan: boolean;
  /**
   * Array of node's `<span class='wb-col'>` elements.
   * The first element is `<span class='wb-node wb-col'>`, which contains the
   * node title and icon (`idx: 0`, id: '*'`).
   */
  allColInfosById: ColumnEventInfoMap;
  /**
   * Array of node's `<span class='wb-node'>` elements,
   * *that should be rendered by the event handler*.
   * In contrast to `allColInfosById`, the node title is not part of this array.
   * If node.isColspan() is true, this array is empty (`[]`).
   * This allows to iterate over all relevant in a simple loop:
   * ```
   * for (const col of Object.values(e.renderColInfosById)) {
   *   switch (col.id) {
   *     default:
   *       // Assumption: we named column.id === node.data.NAME
   *       col.elem.textContent = node.data[col.id];
   *       break;
   *   }
   * }
   */
  renderColInfosById: ColumnEventInfoMap;
}

/**
 * Contains the node's type information, i.e. `tree.types[node.type]` if
 * defined.
 * @see {@link Wunderbaum.types} and {@link WunderbaumNode.getOption|WunderbaumNode.getOption()}
 * to evaluate `node.NAME` setting and `tree.types[node.type].NAME`.
 */
export interface NodeTypeDefinition {
  /** En/disable checkbox for matching nodes. */
  checkbox?: CheckboxOption;
  /** Optional class names that are added to all `div.wb-row` elements of matching nodes. */
  classes?: string;
  /** Only show title and hide other columns if any. */
  colspan?: boolean;
  /** Default icon for matching nodes. */
  icon?: IconOption;
  /** Default icon for matching nodes. */
  iconTooltip?: string | boolean;
  // and more
  [key: string]: unknown;
}

/* -----------------------------------------------------------------------------
 * DATA TYPES
 * ---------------------------------------------------------------------------*/

export type NodeTypeDefinitionMap = { [type: string]: NodeTypeDefinition };

/**
 * Column type definitions.
 * @see {@link Wunderbaum.columns}
 */
export interface ColumnDefinition {
  /** Column ID as defined in `tree.columns` definition ("*" for title column). */
  id: string;
  /** Column header (defaults to id) */
  title: string;
  /** Column header tooltip (optional) */
  tooltip?: string;
  /** Column width or weight.
   * Either an absolute pixel value (e.g. `"50px"`) or a relative weight (e.g. `1`)
   * that is used to calculate the width  inside the remaining available space.
   * Default: `"*"`, which is interpreted as `1`.
   */
  width?: string | number;
  /** Only used for columns with a relative weight.
   * Default: `4px`.
   */
  minWidth?: string | number;
  /** Optional class names that are added to all `span.wb-col` header AND data
   * elements of that column.
   */
  classes?: string;
  /** If `headerClasses` is a set, it will be used for the header element only
   * (unlike `classes`, which is used for body and header cells).
   */
  headerClasses?: string;
  /** Optional HTML content that is rendered into all `span.wb-col` elements of that column.*/
  html?: string;
  /** @internal */
  _weight?: number;
  /** @internal */
  _widthPx?: number;
  /** @internal */
  _ofsPx?: number;
  // ... and more
  [key: string]: unknown;
}

export type ColumnDefinitionList = Array<ColumnDefinition>;

/**
 * Column information (passed to the `render` event).
 */
export interface ColumnEventInfo {
  /** Column ID as defined in `tree.columns` definition ("*" for title column). */
  id: string;
  /** Column index (0: leftmost title column). */
  idx: number;
  /** The cell's `<span class='wb-col'>` element (null for plain trees). */
  elem: HTMLSpanElement | null;
  /** The value of `tree.columns[]` for the current index. */
  info: ColumnDefinition;
}

export type ColumnEventInfoMap = { [colId: string]: ColumnEventInfo };

/**
 * Additional information derived from mouse or keyboard events.
 * @see {@link Wunderbaum.getEventInfo}
 */
export interface WbEventInfo {
  /** The original HTTP Event.*/
  event: MouseEvent | KeyboardEvent;
  /** Canonical descriptive string of the event type including modifiers,
   * e.g. `Ctrl+Down`. @see {@link util.eventToString}
   */
  canonicalName: string;
  /** The tree instance. */
  tree: Wunderbaum;
  /** The affected node instance instance if any. */
  node: WunderbaumNode | null;
  /** The affected part of the node span (e.g. title, expander, ...). */
  region: NodeRegion;
  /** The definition of the affected column if any. */
  colDef?: ColumnDefinition;
  /** The index of affected column or -1. */
  colIdx: number;
  /** The column definition ID of affected column if any. */
  colId?: string;
  /** The affected column's span tag if any. */
  colElem?: HTMLSpanElement;
}

// export type WbTreeCallbackType = (e: WbTreeEventType) => any;
// export type WbNodeCallbackType = (e: WbNodeEventType) => any;
// export type WbRenderCallbackType = (e: WbRenderEventType) => void;

export type FilterModeType = null | "dim" | "hide";
export type SelectModeType = "single" | "multi" | "hier";
export type ApplyCommandType =
  | "addChild"
  | "addSibling"
  | "copy"
  | "cut"
  | "down"
  | "first"
  | "indent"
  | "last"
  | "left"
  | "moveDown"
  | "moveUp"
  | "outdent"
  | "pageDown"
  | "pageUp"
  | "parent"
  | "paste"
  | "remove"
  | "rename"
  | "right"
  | "up";

export type NodeFilterResponse = "skip" | "branch" | boolean | void;
export type NodeFilterCallback = (node: WunderbaumNode) => NodeFilterResponse;

/**
 * Possible values for {@link WunderbaumNode.update()} and {@link Wunderbaum.update()}.
 */
export enum ChangeType {
  /** Re-render the whole viewport, headers, and all rows. */
  any = "any",
  /** A node's title, icon, columns, or status have changed. Update the existing row markup. */
  data = "data",
  /** The `tree.columns` definition has changed beyond simple width adjustments. */
  colStructure = "colStructure",
  /** The viewport/window was resized. Adjust layout attributes for all elements. */
  resize = "resize",
  /** A node's definition has changed beyond status and data. Re-render the whole row's markup. */
  row = "row",
  /** Nodes have been added, removed, etc. Update markup. */
  structure = "structure",
  /** A node's status has changed. Update current row's classes, to reflect active, selected, ... */
  status = "status",
  /** Vertical scroll event. Update the 'top' property of all rows. */
  scroll = "scroll",
}

/* Internal use. */
export enum RenderFlag {
  clearMarkup = "clearMarkup",
  header = "header",
  redraw = "redraw",
  scroll = "scroll",
}

/** Possible values for {@link WunderbaumNode.setStatus()}. */
export enum NodeStatusType {
  ok = "ok",
  loading = "loading",
  error = "error",
  noData = "noData",
  paging = "paging",
}

/** Define the subregion of a node, where an event occurred. */
export enum NodeRegion {
  unknown = "",
  checkbox = "checkbox",
  column = "column",
  expander = "expander",
  icon = "icon",
  prefix = "prefix",
  title = "title",
}

/** Initial navigation mode and possible transition. */
export enum NavModeEnum {
  startRow = "startRow", // Start with row mode, but allow cell-nav mode
  cell = "cell", // Cell-nav mode only
  startCell = "startCell", // Start in cell-nav mode, but allow row mode
  row = "row", // Row mode only
}

/* -----------------------------------------------------------------------------
 * METHOD OPTIONS TYPES
 * ---------------------------------------------------------------------------*/

/** Possible values for {@link WunderbaumNode.addChildren()}. */
export interface AddChildrenOptions {
  /** Insert children before this node (or index)
   * @default undefined or null:  append as last child
   */
  before?: WunderbaumNode | number | null;
  /**
   * Set `node.expanded = true` according to tree.options.minExpandLevel.
   * This does *not* load lazy nodes.
   * @default true
   */
  applyMinExpanLevel?: boolean;
  /** (@internal Internal use, do not set! ) */
  _level?: number;
}

/** Possible values for {@link Wunderbaum.applyCommand()} and {@link WunderbaumNode.applyCommand()}. */
export interface ApplyCommandOptions {
  [key: string]: unknown;
}

/** Possible values for {@link Wunderbaum.expandAll()} and {@link WunderbaumNode.expandAll()}. */
export interface ExpandAllOptions {
  /** Restrict expand level @default 99 */
  depth?: number;
  /** Expand and load lazy nodes @default false  */
  loadLazy?: boolean;
  /** Ignore `minExpandLevel` option @default false */
  force?: boolean;
  /** Keep active node visible @default true */
  keepActiveNodeVisible?: boolean;
}

/** Possible values for {@link Wunderbaum.filterNodes()} and {@link Wunderbaum.filterBranches()}. */
export interface FilterNodesOptions {
  mode?: string;
  leavesOnly?: boolean;
  fuzzy?: boolean;
  highlight?: boolean;
  hideExpanders?: boolean;
  autoExpand?: boolean;
  noData?: boolean;
}

/** Possible values for {@link WunderbaumNode.makeVisible()}. */
export interface MakeVisibleOptions {
  /** Do not animate expand (currently not implemented). @default false */
  noAnimation?: boolean;
  /** Scroll node into visible viewport area if required. @default true */
  scrollIntoView?: boolean;
  /** Do not send events. @default false */
  noEvents?: boolean;
}

/** Possible values for {@link WunderbaumNode.navigate()}. */
export interface NavigateOptions {
  /** Activate the new node (otherwise focus only). @default true */
  activate?: boolean;
  /** Originating event (e.g. KeyboardEvent) if any. */
  event?: Event;
}

/** Possible values for {@link WunderbaumNode._render()}. */
export interface RenderOptions {
  /** Which parts need update? @default ChangeType.data */
  change?: ChangeType;
  /** Where to append a new node. @default 'last' */
  after?: any;
  /** @internal. @default false */
  isNew?: boolean;
  /** @internal. @default false */
  preventScroll?: boolean;
  /** @internal. @default false */
  isDataChange?: boolean;
  /** @internal. @default false */
  top?: number;
  /** @internal. @default true */
  resizeCols?: boolean;
}

/** Possible values for {@link WunderbaumNode.scrollIntoView()} `options` argument. */
export interface ScrollIntoViewOptions {
  /** Do not animate (currently not implemented). @default false */
  noAnimation?: boolean;
  /** Do not send events. @default false */
  noEvents?: boolean;
  /** Keep this node visible at the top in any case. */
  topNode?: WunderbaumNode;
  /** Add N pixel offset at top. */
  ofsY?: number;
}

/** Possible values for {@link Wunderbaum.scrollTo()} `options` argument. */
export interface ScrollToOptions extends ScrollIntoViewOptions {
  /** Which node to scroll into the viewport.*/
  node: WunderbaumNode;
}

/** Possible values for {@link WunderbaumNode.setActive} `options` argument. */
export interface SetActiveOptions {
  /** Generate (de)activate event, even if node already has this status (@default: false). */
  retrigger?: boolean;
  /** Do not generate (de)activate event  (@default: false). */
  noEvents?: boolean;
  // /** Mark node as focused node (default: true).
  //  * Combine with `focusTree: true` to set keyboard focus to tree container.
  //  */
  // focusNode?: boolean;
  /** Call `tree.setFocus()` to acquire keyboard focus (@default: false). */
  focusTree?: boolean;
  /** Optional original event that will be passed to the (de)activate handler. */
  event?: Event;
  /** Also call {@link Wunderbaum.setColumn()}. */
  colIdx?: number | string;
  /**
   * Focus embedded input control of the grid cell if any (requires colIdx >= 0).
   * If colIdx is 0 or '*', the node title is put into edit mode.
   * Implies `focusTree: true`, requires `colIdx`.
   */
  edit?: boolean;
}

/** Possible values for {@link WunderbaumNode.setColumn()} `options` argument. */
export interface SetColumnOptions {
  /**
   * Focus embedded input control of the grid cell if any .
   * If colIdx is 0 or '*', the node title is put into edit mode.
   * @default false
   */
  edit?: boolean;
  /** Horizontically scroll into view. @default: true */
  scrollIntoView?: boolean;
}

/** Possible values for {@link WunderbaumNode.setExpanded} `options` argument. */
export interface SetExpandedOptions {
  /** Ignore {@link WunderbaumOptions.minExpandLevel}. @default false */
  force?: boolean;
  /** Immediately update viewport (async otherwise). @default false */
  immediate?: boolean;
  /** Do not animate expand (currently not implemented). @default false */
  noAnimation?: boolean;
  /** Do not send events. @default false */
  noEvents?: boolean;
  /** Scroll up to bring expanded nodes into viewport. @default false */
  scrollIntoView?: boolean;
}

/** Possible values for {@link WunderbaumNode.update()} `options` argument. */
export interface UpdateOptions {
  /** Force immediate redraw instead of throttled/async mode. @default false */
  immediate?: boolean;
  // /** Remove HTML markup of all rendered nodes before redraw. @default false */
  // removeMarkup?: boolean;
}

/** Possible values for {@link WunderbaumNode.setSelected()} `options` argument. */
export interface SetSelectedOptions {
  /** Ignore restrictions, e.g. (`unselectable`). @default false */
  force?: boolean;
  /** Do not send `beforeSelect` or `select` events. @default false */
  noEvents?: boolean;
  /** Apply to all descendant nodes (only for `selectMode: 'multi'`). @default false */
  propagateDown?: boolean;
  // /** Apply to all ancestor nodes. @default false */
  // propagateUp?: boolean;
  /** Called for every node. May return false to prevent action. @default null */
  callback?: NodeSelectCallback;
}

/** Possible values for {@link WunderbaumNode.setStatus()} `options` argument. */
export interface SetStatusOptions {
  /** Displayed as status node title. */
  message?: string;
  /** Used as tooltip. */
  details?: string;
}

/** Options passed to {@link Wunderbaum.visitRows()}. */
export interface VisitRowsOptions {
  /** Skip filtered nodes and children of collapsed nodes. @default false */
  includeHidden?: boolean;
  /** Return the start node as first result. @default true */
  includeSelf?: boolean;
  /** Traverse in opposite direction, i.e. bottom up. @default false */
  reverse?: boolean;
  /** Start traversal at this node @default first (topmost) tree node */
  start?: WunderbaumNode | null;
  /** Wrap around at last node and continue at the top,
   * until the start node is reached again @default false */
  wrap?: boolean;
}

/* -----------------------------------------------------------------------------
 * wb_ext_filter
 * ---------------------------------------------------------------------------*/
export type FilterOptionsType = {
  /**
   * Element or selector of an input control for filter query strings
   * @default null
   */
  connectInput?: null | string | Element;
  /**
   * Re-apply last filter if lazy data is loaded
   * @default true
   */
  autoApply?: boolean;
  /**
   * Expand all branches that contain matches while filtered
   * @default false
   */
  autoExpand?: boolean;
  /**
   * Show a badge with number of matching child nodes near parent icons
   * @default true
   */
  counter?: boolean;
  /**
   * Match single characters in order, e.g. 'fb' will match 'FooBar'
   * @default false
   */
  fuzzy?: boolean;
  /**
   * Hide counter badge if parent is expanded
   * @default true
   */
  hideExpandedCounter?: boolean;
  /**
   * Hide expanders if all child nodes are hidden by filter
   * @default false;
   */
  hideExpanders?: boolean;
  /**
   * Highlight matches by wrapping inside <mark> tags
   * @default true
   */
  highlight?: boolean;
  /**
   * Match end nodes only
   * @default false
   */
  leavesOnly?: boolean;
  /**
   * Grayout unmatched nodes (pass "hide" to remove unmatched node instead)
   * @default 'dim'
   */
  mode?: FilterModeType;
  /**
   * Display a 'no data' status node if result is empty (hide-mode only).
   * Pass false to simply show an empy pane, or pass a string to customize the message.
   * @default true
   */
  noData?: boolean | string;
};

/* -----------------------------------------------------------------------------
 * wb_ext_edit
 * ---------------------------------------------------------------------------*/
/**
 * Note: <br>
 * This options are used for renaming node titles. <br>
 * There is also the `tree.change` event to handle modifying node data from
 * input controls that are embedded in grid cells.
 */
export type EditOptionsType = {
  /**
   * Used to debounce the `change` event handler for grid cells [ms].
   * @default 100
   */
  debounce?: number;
  /**
   * Minimum number of characters required for node title input field.
   * @default 1
   */
  minlength?: number;
  /**
   * Maximum number of characters allowed for node title input field.
   * @default null;
   */
  maxlength?: null | number;
  /**
   * Array of strings to determine which user input should trigger edit mode.
   * E.g. `["clickActive", "F2", "macEnter"]`: <br>
   * 'clickActive': single click on active node title <br>
   * 'F2': press F2 key <br>
   * 'macEnter': press Enter (on macOS only) <br>
   * Pass an empty array to disable edit mode.
   * @default []
   */
  trigger?: string[];
  /**
   * Trim whitespace before saving a node title.
   * @default true
   */
  trim?: boolean;
  /**
   * Select all text of a node title, so it can be overwritten by typing.
   * @default true
   */
  select?: boolean;
  /**
   * Handle 'clickActive' only if last click is less than this ms old (0: always)
   * @default 1000
   */
  slowClickDelay?: number;
  /**
   * Permanently apply node title input validations (CSS and tooltip) on keydown.
   * @default true
   */
  validity?: boolean;

  // --- Events ---

  /**
   * `beforeEdit(e)` may return an input HTML string. Otherwise use a default.
   * @category Callback
   */
  beforeEdit?: null | ((e: WbNodeEventType) => boolean) | string;
  /**
   *
   * @category Callback
   */
  edit?:
    | null
    | ((e: WbNodeEventType & { inputElem: HTMLInputElement }) => void);
  /**
   *
   * @category Callback
   */
  apply?:
    | null
    | ((e: WbNodeEventType & { inputElem: HTMLInputElement }) => any)
    | Promise<any>;
};

/* -----------------------------------------------------------------------------
 * wb_ext_dnd
 * ---------------------------------------------------------------------------*/

export type InsertNodeType =
  | "before"
  | "after"
  | "prependChild"
  | "appendChild";
// export type DndModeType = "before" | "after" | "over";

export type DropEffectType = "none" | "copy" | "link" | "move";
export type DropEffectAllowedType =
  | "none"
  | "copy"
  | "copyLink"
  | "copyMove"
  | "link"
  | "linkMove"
  | "move"
  | "all";

export type DropRegionType = "over" | "before" | "after";
export type DropRegionTypeSet = Set<DropRegionType>;
// type AllowedDropRegionType =
//   | "after"
//   | "afterBefore"
//   // | "afterBeforeOver" // == all == true
//   | "afterOver"
//   | "all" // == true
//   | "before"
//   | "beforeOver"
//   | "none" // == false == "" == null
//   | "over"; // == "child"

export interface DragEventType extends WbNodeEventType {
  /** The original event. */
  event: DragEvent;
  /** The source node. */
  node: WunderbaumNode;
}

export interface DropEventType extends WbNodeEventType {
  /** The original event. */
  event: DragEvent;
  /** The target node. */
  node: WunderbaumNode;
  /** The source node if any. */
  sourceNode: WunderbaumNode;
}

export type DndOptionsType = {
  /**
   * Expand nodes after n milliseconds of hovering
   * @default 1500
   */
  autoExpandMS?: 1500;
  // /**
  //  * Additional offset for drop-marker with hitMode = "before"/"after"
  //  * @default
  //  */
  // dropMarkerInsertOffsetX: -16;
  // /**
  //  * Absolute position offset for .fancytree-drop-marker relatively to ..fancytree-title (icon/img near a node accepting drop)
  //  * @default
  //  */
  // dropMarkerOffsetX: -24;
  // /**
  //  * Root Container used for drop marker (could be a shadow root)
  //  * (#1021 `document.body` is not available yet)
  //  * @default
  //  */
  // dropMarkerParent: "body";
  /**
   * true: Drag multiple (i.e. selected) nodes. Also a callback() is allowed
   * @default false
   */
  multiSource?: false;
  /**
   * Restrict the possible cursor shapes and modifier operations
   * (can also be set in the dragStart event)
   * @default "all"
   */
  effectAllowed?: DropEffectAllowedType;
  /**
   * Default dropEffect ('copy', 'link', or 'move') when no modifier is pressed.
   * Overidable in the dragEnter or dragOver event.
   * @default "move"
   */
  dropEffectDefault?: DropEffectType;
  /**
   * Use opinionated heuristics to determine the dropEffect ('copy', 'link', or 'move')
   * based on `effectAllowed`, `dropEffectDefault`, and modifier keys.
   * This is recalculated before each dragEnter and dragOver event and can be
   * overridden there.
   *
   * @default true
   */
  guessDropEffect: boolean;
  /**
   * Prevent dropping nodes from different Wunderbaum trees
   * @default false
   */
  preventForeignNodes?: boolean;
  /**
   * Prevent dropping items on unloaded lazy Wunderbaum tree nodes
   * @default true
   */
  preventLazyParents?: boolean;
  /**
   * Prevent dropping items other than Wunderbaum tree nodes
   * @default false
   */
  preventNonNodes?: boolean;
  /**
   * Prevent dropping nodes on own descendants
   * @default true
   */
  preventRecursion?: boolean;
  /**
   * Prevent dropping nodes under same direct parent
   * @default false
   */
  preventSameParent?: boolean;
  /**
   * Prevent dropping nodes 'before self', etc. (move only)
   * @default true
   */
  preventVoidMoves?: boolean;
  /**
   * Serialize Node Data to datatransfer object
   * @default true
   */
  serializeClipboardData?:
    | boolean
    | ((nodeData: WbNodeData, node: WunderbaumNode) => string);
  /**
   * Enable auto-scrolling while dragging
   * @default true
   */
  scroll?: boolean;
  /**
   * Active top/bottom margin in pixel
   * @default 20
   */
  scrollSensitivity?: 20;
  // /**
  //  * Scroll events every N microseconds
  //  * @default 50
  //  */
  // scrollnterval: 50;
  /**
   * Pixel per event
   * @default 5
   */
  scrollSpeed?: 5;
  // /**
  //  * Allow dragging of nodes to different IE windows
  //  * @default false
  //  */
  // setTextTypeJson: boolean;
  /**
   * Optional callback passed to `toDict` on dragStart @since 2.38
   * @default null
   * @category Callback
   */
  sourceCopyHook?: null;
  // Events (drag support)
  /**
   * Callback(sourceNode, data), return true, to enable dnd drag
   * @default null
   * @category Callback
   */
  dragStart?: null | ((e: DragEventType) => boolean);
  /**
   * Callback(sourceNode, data)
   * @default null
   * @category Callback
   */
  drag?: null | ((e: DragEventType) => void);
  /**
   * Callback(sourceNode, data)
   * @default null
   * @category Callback
   */
  dragEnd?: null | ((e: DragEventType) => void);
  // Events (drop support)
  /**
   * Callback(targetNode, data), return true, to enable dnd drop
   * @default null
   * @category Callback
   */
  dragEnter?:
    | null
    | ((e: DropEventType) => DropRegionType | DropRegionTypeSet | boolean);
  /**
   * Callback(targetNode, data)
   * @default null
   * @category Callback
   */
  dragOver?: null | ((e: DropEventType) => void);
  /**
   * Callback(targetNode, data), return false to prevent autoExpand
   * @default null
   * @category Callback
   */
  dragExpand?: null | ((e: DropEventType) => boolean);
  /**
   * Callback(targetNode, data)
   * @default null
   * @category Callback
   */
  drop?:
    | null
    | ((
        e: WbNodeEventType & {
          event: DragEvent;
          region: DropRegionType;
          suggestedDropMode: InsertNodeType;
          suggestedDropEffect: DropEffectType;
          sourceNode: WunderbaumNode;
          sourceNodeData: WbNodeData | null;
        }
      ) => void);
  /**
   * Callback(targetNode, data)
   * @default null
   * @category Callback
   */
  dragLeave?: null | ((e: DropEventType) => void);
};

/* -----------------------------------------------------------------------------
 * wb_ext_grid
 * ---------------------------------------------------------------------------*/
export type GridOptionsType = object;

/* -----------------------------------------------------------------------------
 * wb_ext_keynav
 * ---------------------------------------------------------------------------*/
export type KeynavOptionsType = object;

/* -----------------------------------------------------------------------------
 * wb_ext_loger
 * ---------------------------------------------------------------------------*/
export type LoggerOptionsType = object;
