package org.basex.gui.view.folder;

import org.basex.data.*;
import org.basex.query.value.seq.*;
import org.basex.util.list.*;

/**
 * This is an iterator for the folder nodes.
 *
 * @author BaseX Team 2005-22, BSD License
 * @author Christian Gruen
 */
final class FolderIterator {
  /** Tree visualization reference. */
  private final FolderView view;

  /** Current y position. */
  int y;
  /** Current level. */
  int level;
  /** Current pre value. */
  int pre;

  /** Stack for parent nodes. */
  private final IntList parents = new IntList();
  /** Panel height. */
  private final int height;
  /** Flag for a found context node. */
  private boolean found;
  /** Current context set position. */
  private int cp = -1;
  /** Current parent node. */
  private int par;
  /** Current iterator mode. */
  private int mode;
  /** Temporary level. */
  private int ll;

  /**
   * Default constructor.
   * @param v view reference
   */
  FolderIterator(final FolderView v) {
    this(v, 0, Integer.MAX_VALUE);
  }

  /**
   * Constructor, specifying the start y position and the maximum height.
   * @param v view reference
   * @param yy start y value
   * @param h panel height
   */
  FolderIterator(final FolderView v, final int yy, final int h) {
    height = h;
    view = v;
    view.focusedPos = -1;
    y = yy;
  }

  /**
   * Returns the total view height.
   * @return view height
   */
  int height() {
    while(more());
    return y;
  }

  /**
   * Checks if there are more nodes to iterate.
   * @return true for more data
   */
  boolean more() {
    if(view.opened == null) return false;
    if(!found) {
      view.focusedPos++;
      found = pre == view.gui.context.focused;
    }
    if(mode == 0) return moreCS();
    y += view.lineH;
    if(y > height) return false;

    final Data data = view.gui.context.data();
    if(data == null || pre >= data.meta.size) return false;

    final int kind = data.kind(pre);
    return mode == 2 || (kind == Data.ELEM || kind == Data.DOC) &&
      view.opened[pre] ? moreData(data) : moreCS();
  }

  /**
   * Checks if there are more context nodes to check.
   * @return true for more data
   */
  private boolean moreCS() {
    final DBNodes current = view.gui.context.current();
    if(current == null || ++cp >= current.size()) return false;
    par = current.pre(cp);
    pre = par;
    level = 0;
    ll = 0;
    mode = 1;
    return true;
  }

  /**
   * Checks if there are more data nodes to check.
   * @param data data reference
   * @return true for more data
   */
  private boolean moreData(final Data data) {
    level = ll;
    int kind = data.kind(pre);

    final boolean[] open = view.opened;
    if(open == null) return false;

    pre += open[pre] ? 1 : data.size(pre, kind);
    while(pre < data.meta.size) {
      kind = data.kind(pre);
      final int p = data.parent(pre, kind);
      // search current root
      while(p < par && level > 0) par = parents.get(--level);
      if(p < par) break;
      if(p == par) {
        // store child as new root
        kind = data.kind(par);

        if((kind == Data.ELEM || kind == Data.DOC) && open[pre]) {
          parents.set(level++, par);
          par = pre;
          ll = level;
        } else {
          ll = level++;
        }
        mode = 2;
        return true;
      }
      pre += 1;
    }
    return moreCS();
  }
}
