package org.basex.data;

import java.util.*;

import org.basex.util.*;
import org.basex.util.list.*;

/**
 * This class organizes namespace scopes.
 *
 * @author BaseX Team 2005-22, BSD License
 * @author Christian Gruen
 */
final class NSScope {
  /** Data reference. */
  private final Data data;
  /** Namespaces. */
  private final Namespaces nspaces;
  /** Stack with pre values. */
  private final IntList preStack = new IntList();
  /** Root namespace. */
  private final NSNode root;
  /** Tracks existing nodes. */
  private final ArrayList<NSNode> cache;

  /**
   * Default constructor.
   * @param pre pre value
   * @param data data reference
   */
  NSScope(final int pre, final Data data) {
    this.data = data;
    nspaces = data.nspaces;
    root = nspaces.cursor();
    cache = nspaces.cache(pre);
  }

  /**
   * Refreshes the namespace structure.
   * @param nsPre pre value with namespaces
   * @param c insertion counter
   */
  void loop(final int nsPre, final int c) {
    if(c == 0) nspaces.root(nsPre, data);
    while(!preStack.isEmpty() && preStack.peek() > nsPre) nspaces.close(preStack.pop());
  }

  /**
   * Opens a new level.
   * @param pre pre value
   */
  void open(final int pre) {
    nspaces.open();
    preStack.push(pre);
  }

  /**
   * Parses the specified namespaces and returns all namespaces that are not declared yet.
   * @param pre pre value
   * @param nsp source namespaces
   * @return {@code true} if new namespaces were added
   */
  boolean open(final int pre, final Atts nsp) {
    // collect new namespaces
    final Atts ns = new Atts();
    final int as = nsp.size();
    for(int a = 0; a < as; a++) {
      final byte[] prefix = nsp.name(a), uri = nsp.value(a);
      final int uriId = nspaces.uriIdForPrefix(prefix, true);
      if(uriId == 0 || uriId != nspaces.uriId(uri)) ns.add(prefix, uri);
    }
    nspaces.open(pre, ns);
    preStack.push(pre);
    return !ns.isEmpty();
  }

  /**
   * Shifts cached namespaces by the specified value.
   * @param diff shift
   */
  void shift(final int diff) {
    for(final NSNode node : cache) node.incrementPre(diff);
  }

  /**
   * Closes the namespace scope.
   */
  void close() {
    while(!preStack.isEmpty()) nspaces.close(preStack.pop());
    nspaces.cursor(root);
  }
}
