package edu.washington.escience.myria.parallel;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;

import edu.washington.escience.myria.api.encoding.QueryConstruct;
import edu.washington.escience.myria.api.encoding.QueryConstruct.ConstructArgs;

/**
 * A {@link QueryPlan} node that signifies running each of its children serially.
 */
public final class DoWhile extends QueryPlan {
  /** The query plans in the body. */
  private final List<QueryPlan> body;
  /** The relation to be scanned for the while loop continuation condition. */
  private final String condition;
  /** Whether this while loop has ever been run. */
  private boolean hasRun;

  /**
   * Construct a {@link QueryPlan} that runs the body tasks in sequence, and loops if the singleton boolean relation
   * specified by {@code condition} is {@code true}.
   * 
   * @param body the tasks to be run.
   * @param condition the relation to be scanned for the while loop continuation condition. Must be a singleton boolean
   *          relation.
   */
  public DoWhile(final List<? extends QueryPlan> body, final String condition) {
    this.body = ImmutableList.copyOf(Objects.requireNonNull(body, "body"));
    this.condition = Objects.requireNonNull(condition, "condition");
    hasRun = false;
  }

  /**
   * {@inheritdoc}
   * 
   * Instantiating a {@link DoWhile} is a somewhat complex operation.
   * 
   * 1. Every {@link DoWhile} tracks whether or not the loop has been entered in {@link #hasRun}. For all times but the
   * first, the {@link #condition} is checked and, if {@code false}, the {@link DoWhile} is simply removed from the
   * {@code planQ} and nothing is added. This signifies the loop termination.
   * 
   * 2. When we want to execute the loop, we want to do two things: run the body, then gather the value of the
   * termination condition and store it at the {@link Server} in a query global variable. Thus we want to add the
   * sequence of operations {@code body, then [gatherCondition]} to the plan.
   * 
   * @param planQ the queue of {@link QueryPlan} tasks
   * @param subQueryQ the queue of {@link SubQuery} tasks
   * @param args the {@link QueryConstruct#ConstructArgs} arguments needed to instantiate a query plan
   */
  @Override
  public void instantiate(final LinkedList<QueryPlan> planQ, final LinkedList<SubQuery> subQueryQ,
      final ConstructArgs args) {
    QueryPlan checkTask = planQ.peekFirst();
    Verify.verify(checkTask == this, "this %s should be the first object on the queue, not %s!", this, checkTask);
    if (hasRun) {
      Boolean b = (Boolean) args.getServer().getQueryGlobal(args.getQueryId(), condition.toString());
      Preconditions.checkState(b != null,
          "Query %s: DoWhile Loop has run, but global variable for loop condition (%s) has not been set", condition
              .toString());
      if (!b.booleanValue()) {
        planQ.removeFirst();
        return;
      }
      for (QueryPlan q : body) {
        q.reset();
      }
    }
    // Note that the setDoWhileCondition will actually end up being after the body in the planQ
    planQ.addFirst(QueryConstruct.setDoWhileCondition(condition));
    planQ.addAll(0, body);
    hasRun = true;
  }

  @Override
  public void reset() {
    hasRun = false;
    for (QueryPlan q : body) {
      q.reset();
    }
  }
}

@@ -2795,27 +2795,16 @@
 ondition
-.toString()
 );%0A     
@@ -2959,34 +2959,8 @@
 tion
-%0A              .toString()
 );%0A 

remove unnecessary toString() calls