/**
   * Constructor declaration
   *
   * @param name HsqlName of the index
   * @param table table of the index
   * @param column array of column indexes
   * @param type array of column types
   * @param unique is this a unique index
   * @param constraint does this index belonging to a constraint
   * @param forward is this an auto-index for an FK that refers to a table defined after this table
   * @param visColumns count of visible columns
   */
  Index(
      Database database,
      HsqlName name,
      Table table,
      int[] column,
      int[] type,
      boolean isPk,
      boolean unique,
      boolean constraint,
      boolean forward,
      int[] pkcols,
      int[] pktypes,
      boolean temp) {

    indexName = name;
    colIndex = column;
    colType = type;
    pkCols = pkcols;
    pkTypes = pktypes;
    isUnique = unique;
    isConstraint = constraint;
    isForward = forward;
    useRowId = (!isUnique && pkCols.length == 0) || (colIndex.length == 0);
    colCheck = table.getNewColumnCheckList();

    ArrayUtil.intIndexesToBooleanArray(colIndex, colCheck);

    updatableIterators = Index.emptyIterator;
    updatableIterators.next = updatableIterators.last = updatableIterators;
    collation = database.collation;
    isTemp = temp;
    onCommitPreserve = table.onCommitPreserve;
  }
    void link(IndexRowIterator other) {

      other.next = next;
      next.last = other;
      other.last = this;
      next = other;
    }
  void clearAll(Session session) {

    setRoot(session, null);

    depth = 0;

    for (IndexRowIterator it = updatableIterators.next; it != updatableIterators; it = it.next) {
      it.release();
    }
  }
    public void release() {

      if (last != null) {
        last.next = next;
      }

      if (next != null) {
        next.last = last;
      }
    }
  RowIterator findFirstRowForDelete(Session session, Object[] rowdata, int[] rowColMap)
      throws HsqlException {

    Node node = findNotNull(session, rowdata, rowColMap, true);

    if (node == null) {
      return emptyIterator;
    } else {
      RowIterator it = new IndexRowIterator(session, this, node);

      updatableIterators.link((IndexRowIterator) it);

      return it;
    }
  }
  /** Delete a node from the index */
  void delete(Session session, Node x) throws HsqlException {

    if (x == null) {
      return;
    }

    for (IndexRowIterator it = updatableIterators.next; it != updatableIterators; it = it.next) {
      it.updateForDelete(x);
    }

    Node n;

    if (x.getLeft() == null) {
      n = x.getRight();
    } else if (x.getRight() == null) {
      n = x.getLeft();
    } else {
      Node d = x;

      x = x.getLeft();

      /*
                  // todo: this can be improved

                  while (x.getRight() != null) {
                      if (Trace.STOP) {
                          Trace.stop();
                      }

                      x = x.getRight();
                  }
      */
      for (Node temp = x; (temp = temp.getRight()) != null; ) {
        x = temp;
      }

      // x will be replaced with n later
      n = x.getLeft();

      // swap d and x
      int b = x.getBalance();

      x.setBalance(d.getBalance());
      d.setBalance(b);

      // set x.parent
      Node xp = x.getParent();
      Node dp = d.getParent();

      if (d == getRoot(session)) {
        setRoot(session, x);
      }

      x.setParent(dp);

      if (dp != null) {
        if (dp.getRight().equals(d)) {
          dp.setRight(x);
        } else {
          dp.setLeft(x);
        }
      }

      // for in-memory tables we could use: d.rData=x.rData;
      // but not for cached tables
      // relink d.parent, x.left, x.right
      if (xp == d) {
        d.setParent(x);

        if (d.getLeft().equals(x)) {
          x.setLeft(d);
          x.setRight(d.getRight());
        } else {
          x.setRight(d);
          x.setLeft(d.getLeft());
        }
      } else {
        d.setParent(xp);
        xp.setRight(d);
        x.setRight(d.getRight());
        x.setLeft(d.getLeft());
      }

      x.getRight().setParent(x);
      x.getLeft().setParent(x);

      // set d.left, d.right
      d.setLeft(n);

      if (n != null) {
        n.setParent(d);
      }

      d.setRight(null);

      x = d;
    }

    boolean isleft = x.isFromLeft();

    replace(session, x, n);

    n = x.getParent();

    x.delete();

    while (n != null) {
      x = n;

      int sign = isleft ? 1 : -1;

      switch (x.getBalance() * sign) {
        case -1:
          x.setBalance(0);
          break;

        case 0:
          x.setBalance(sign);

          return;

        case 1:
          Node r = child(x, !isleft);
          int b = r.getBalance();

          if (b * sign >= 0) {
            replace(session, x, r);
            set(x, !isleft, child(r, isleft));
            set(r, isleft, x);

            if (b == 0) {
              x.setBalance(sign);
              r.setBalance(-sign);

              return;
            }

            x.setBalance(0);
            r.setBalance(0);

            x = r;
          } else {
            Node l = child(r, isleft);

            replace(session, x, l);

            b = l.getBalance();

            set(r, isleft, child(l, !isleft));
            set(l, !isleft, r);
            set(x, !isleft, child(l, isleft));
            set(l, isleft, x);
            x.setBalance((b == sign) ? -sign : 0);
            r.setBalance((b == -sign) ? sign : 0);
            l.setBalance(0);

            x = l;
          }
      }

      isleft = x.isFromLeft();
      n = x.getParent();
    }
  }