package org.basex.query.util;

import org.basex.data.Data;
import org.basex.query.QueryContext;
import org.basex.query.expr.Context;
import org.basex.query.expr.Expr;
import org.basex.query.expr.ParseExpr;
import org.basex.query.path.Axis;
import org.basex.query.path.AxisPath;
import org.basex.query.path.AxisStep;
import org.basex.query.path.Path;
import org.basex.util.Array;

/**
 * Container for all information needed to determine whether an index is
 * accessible or not.
 *
 * @author BaseX Team 2005-11, BSD License
 * @author Christian Gruen
 * @author Sebastian Gath
 */
public final class IndexContext {
  /** Query context. */
  public final QueryContext ctx;
  /** Data reference. */
  public final Data data;
  /** Index Step. */
  public final AxisStep step;
  /** Flag for iterative evaluation. */
  public final boolean iterable;

  /** Costs of index access: smaller is better, 0 means no results. */
  private int costs;
  /** Flag for ftnot expressions. */
  public boolean not;
  /** Flag for sequential processing. */
  public boolean seq;

  /**
   * Constructor.
   * @param c query context
   * @param d data reference
   * @param s index step
   * @param i iterable flag
   */
  public IndexContext(final QueryContext c, final Data d, final AxisStep s,
      final boolean i) {
    ctx = c;
    data = d;
    step = s;
    iterable = i;
  }

  /**
   * Rewrites the specified expression for index access.
   * @param ex expression to be rewritten
   * @param root new root expression
   * @param text text flag
   * @return index access
   */
  public Expr invert(final Expr ex, final ParseExpr root, final boolean text) {
    // handle context node
    if(ex instanceof Context) {
      if(text) return root;
      // add attribute step
      if(step.test.name == null) return root;
      return Path.get(root.input, root,
          AxisStep.get(step.input, Axis.SELF, step.test));
    }

    final AxisPath orig = (AxisPath) ex;
    final AxisPath path = orig.invertPath(root, step);
    if(!text) {
      // add attribute step
      final AxisStep s = orig.step(orig.steps.length - 1);
      if(s.test.name != null) {
        Expr[] steps = { AxisStep.get(s.input, Axis.SELF, s.test) };
        for(final Expr e : path.steps) steps = Array.add(steps, e);
        path.steps = steps;
      }
    }
    return path;
  }

  /**
   * Adds the estimated costs.
   * @param c cost to be added
   */
  public void addCosts(final int c) {
    costs = Math.max(1, costs + c);
  }

  /**
   * Sets the estimated costs.
   * @param c cost to be added
   */
  public void costs(final int c) {
    costs = c;
  }

  /**
   * Returns the estimated costs.
   * @return costs
   */
  public int costs() {
    return costs;
  }
}
