import type {ChannelValue} from "../channel.js";
import type {CompareFunction, Transformed} from "./basic.js";

/** Options for the tree transform. */
export interface TreeTransformOptions {
  /**
   * The location of each node in the hierarchy; typically slash-separated
   * strings, as with UNIX-based file systems or URLs. Defaults to identity,
   * assuming the mark’s data are path strings.
   */
  path?: ChannelValue;

  /**
   * The path separator, used for inferring the hierarchy from the **path**
   * channel; defaults to forward slash (/).
   */
  delimiter?: string;

  /**
   * How to orient the tree; one of:
   *
   * - *left* (default) - place the root on the left, with children extending right
   * - *right* - place the root on the right, with children extending left
   *
   * Use the **insetLeft** and **insetRight** *x* scale options if horizontal
   * padding is desired, say to make room for labels.
   */
  treeAnchor?: "left" | "right";

  /**
   * How to layout the tree; defaults to [d3.tree][1] which implements the
   * Reingold–Tilford “tidy” algorithm. Use [d3.cluster][2] instead to align
   * leaf nodes; see also Plot.cluster.
   *
   * [1]: https://d3js.org/d3-hierarchy/tree
   * [2]: https://d3js.org/d3-hierarchy/cluster
   */
  treeLayout?: () => any;

  /**
   * How much space to reserve between adjacent nodes in the layout; by default,
   * non-siblings are at least twice as far apart as siblings. If the
   * **treeSeparation** is not null, it is a function that is passed two nodes
   * in the hierarchy and returns the desired (relative) amount of separation;
   * see [d3-hierarchy’s _tree_.separation][1] for more.
   *
   * [1]: https://d3js.org/d3-hierarchy/tree#tree_separation
   */
  treeSeparation?: CompareFunction | null;

  /**
   * How to order nodes prior to laying them out; by default, nodes are laid out
   * in input order. If the **treeSort** option is not null, it is typically a
   * function that is passed two nodes in the hierarchy and compares them,
   * similar to [_array_.sort][1]; see [d3-hierarchy’s _node_.sort][2] for more.
   * The **treeSort** option can also be specified as a string, in which case it
   * refers either to a named column in data, or if it starts with “node:”, a
   * node value such as *node:name*.
   *
   * [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
   * [2]: https://d3js.org/d3-hierarchy/hierarchy#node_sort
   */
  treeSort?: CompareFunction | {node: (node: any) => any} | string | null;
}

/**
 * Populates the *x* and *y* channels with the positions for each node, and
 * applies a default **frameAnchor** based on the specified **treeAnchor**. This
 * transform is intended to be used with dot, text, and other point-based marks.
 * This transform is rarely used directly; see the tree mark.
 *
 * The treeNode transform will derive output columns for any *options* that have
 * one of the following named node values:
 *
 * * *node:name* - the node’s name (the last part of its path)
 * * *node:path* - the node’s full, normalized, slash-separated path
 * * *node:internal* - true if the node is internal, or false for leaves
 * * *node:external* - true if the node is a leaf, or false for internal nodes
 * * *node:depth* - the distance from the node to the root
 * * *node:height* - the distance from the node to its deepest descendant
 *
 * In addition, if any option value is specified as an object with a **node**
 * method, a derived output column will be generated by invoking the **node**
 * method for each node in the tree.
 */
export function treeNode<T>(options?: T & TreeTransformOptions): Transformed<T>;

/**
 * Populates the *x1*, *y1*, *x2*, and *y2* channels, and applies the following
 * defaults: **curve** is *bump-x*, **stroke** is #555, **strokeWidth** is 1.5,
 * and **strokeOpacity** is 0.5. This transform is intended to be used with
 * link, arrow, and other two-point-based marks. This transform is rarely used
 * directly; see the tree mark.
 *
 * The treeLink transform will derive output columns for any *options* that have
 * one of the following named link values:
 *
 * * *node:name* - the child node’s name (the last part of its path)
 * * *node:path* - the child node’s full, normalized, slash-separated path
 * * *node:internal* - true if the child node is internal, or false for leaves
 * * *node:external* - true if the child node is a leaf, or false for external nodes
 * * *node:depth* - the distance from the child node to the root
 * * *node:height* - the distance from the child node to its deepest descendant
 * * *parent:name* - the parent node’s name (the last part of its path)
 * * *parent:path* - the parent node’s full, normalized, slash-separated path
 * * *parent:depth* - the distance from the parent node to the root
 * * *parent:height* - the distance from the parent node to its deepest descendant
 *
 * In addition, if any option value is specified as an object with a **node**
 * method, a derived output column will be generated by invoking the **node**
 * method for each child node in the tree; likewise if any option value is
 * specified as an object with a **link** method, a derived output column will
 * be generated by invoking the **link** method for each link in the tree, being
 * passed two node arguments, the child and the parent.
 */
export function treeLink<T>(options?: T & TreeTransformOptions): Transformed<T>;
