package fj.data.fingertrees;

import fj.data.vector.V2;
import fj.data.vector.V3;

import static fj.data.vector.V.v;

/**
 * A builder of trees and tree components, supplied with a particular monoid and measuring function.
 */
public final class MakeTree<V, A> {
  private final Measured<V, A> m;
  private final Empty<V, A> empty;

  MakeTree(final Measured<V, A> m) {
    this.m = m;
    this.empty = new Empty<>(m);
  }

  // Tree constructors

  /**
   * Constructs an empty tree.
   *
   * @return The empty tree.
   */
  public FingerTree<V, A> empty() {
    return empty;
  }

  /**
   * Constructs a singleton tree.
   *
   * @param a A single element for the tree.
   * @return A tree with the given value as the single element.
   */
  public FingerTree<V, A> single(final A a) {
    return new Single<>(m, a);
  }

  /**
   * Constructs a deep tree. This structure consists of two digits, of 1 to 4 elements each, on the left and right,
   * with the rest of the tree in the middle.
   *
   * @param prefix The leftmost elements of the tree.
   * @param middle The subtree, which is a Finger Tree of 2-3 nodes.
   * @param suffix The rightmost elements of the tree.
   * @return A new finger tree with the given prefix, suffix, and middle.
   */
  public FingerTree<V, A> deep(final Digit<V, A> prefix, final FingerTree<V, Node<V, A>> middle,
                               final Digit<V, A> suffix) {
    return deep(m.sum(prefix.measure(), m.sum(middle.measure(), suffix.measure())), prefix, middle, suffix);
  }

  /**
   * Constructs a deep tree with the given annotation value.
   *
   * @param v      The value with which to annotate this tree.
   * @param prefix The leftmost elements of the tree.
   * @param middle The subtree, which is a Finger Tree of 2-3 nodes.
   * @param suffix The rightmost elements of the tree.
   * @return A new finger tree with the given prefix, suffix, and middle, and annotated with the given value.
   */
  public FingerTree<V, A> deep(final V v, final Digit<V, A> prefix, final FingerTree<V, Node<V, A>> middle,
                               final Digit<V, A> suffix) {
    return new Deep<>(m, v, prefix, middle, suffix);
  }

  // Digit constructors

  /**
   * A digit of one element.
   *
   * @param a The element of the digit.
   * @return A digit of the given element.
   */
  public One<V, A> one(final A a) {
    return new One<>(m, a);
  }

  /**
   * A digit of two elements.
   *
   * @param a The first element of the digit.
   * @param b The second element of the digit.
   * @return A digit of the given elements.
   */
  public Two<V, A> two(final A a, final A b) {
    return new Two<>(m, v(a, b));
  }

  /**
   * A digit of three elements.
   *
   * @param a The first element of the digit.
   * @param b The second element of the digit.
   * @param c The third element of the digit.
   * @return A digit of the given elements.
   */
  public Three<V, A> three(final A a, final A b, final A c) {
    return new Three<>(m, v(a, b, c));
  }

  /**
   * A digit of four elements.
   *
   * @param a The first element of the digit.
   * @param b The second element of the digit.
   * @param c The third element of the digit.
   * @param d The fifth element of the digit.
   * @return A digit of the given elements.
   */
  public Four<V, A> four(final A a, final A b, final A c, final A d) {
    return new Four<>(m, v(a, b, c, d));
  }

  // Node constructors

  /**
   * A binary tree node.
   *
   * @param a The left child of the node.
   * @param b The right child of the node.
   * @return A new binary tree node.
   */
  public Node2<V, A> node2(final A a, final A b) {
    return new Node2<>(m, v(a, b));
  }

  /**
   * A trinary tree node.
   *
   * @param a The left child of the node.
   * @param b The middle child of the node.
   * @param c The right child of the node.
   * @return A new trinary tree node.
   */
  public Node3<V, A> node3(final A a, final A b, final A c) {
    return new Node3<>(m, v(a, b, c));
  }

  /**
   * A binary tree node
   *
   * @param v A vector of the node's elements.
   * @return A new binary tree node.
   */
  public Node2<V, A> node2(final V2<A> v) {
    return new Node2<>(m, v);
  }

  /**
   * A trinary tree node
   *
   * @param v A vector of the node's elements.
   * @return A new trinary tree node.
   */
  public Node3<V, A> node3(final V3<A> v) {
    return new Node3<>(m, v);
  }

}
