package org.basex.query.ft;

import static org.basex.query.util.Err.*;
import static org.basex.query.QueryText.*;
import org.basex.data.FTMatch;
import org.basex.data.FTMatches;
import org.basex.query.QueryContext;
import org.basex.query.QueryException;
import org.basex.query.item.FTNode;
import org.basex.query.iter.FTIter;
import org.basex.query.util.IndexContext;
import org.basex.util.InputInfo;

/**
 * FTMildnot expression.
 *
 * @author BaseX Team 2005-11, BSD License
 * @author Christian Gruen
 */
public final class FTMildNot extends FTExpr {
  /**
   * Constructor.
   * @param ii input info
   * @param e1 first expression
   * @param e2 second expression
   * @throws QueryException query exception
   */
  public FTMildNot(final InputInfo ii, final FTExpr e1, final FTExpr e2)
      throws QueryException {
    super(ii, e1, e2);
    if(usesExclude()) FTMILD.thrw(input);
  }

  @Override
  public FTNode item(final QueryContext ctx, final InputInfo ii)
      throws QueryException {
    return mildnot(expr[0].item(ctx, input), expr[1].item(ctx, input));
  }

  @Override
  public FTIter iter(final QueryContext ctx) throws QueryException {
    return new FTIter() {
      final FTIter i1 = expr[0].iter(ctx);
      final FTIter i2 = expr[1].iter(ctx);
      FTNode it1 = i1.next();
      FTNode it2 = i2.next();

      @Override
      public FTNode next() throws QueryException {
        while(it1 != null && it2 != null) {
          final int d = it1.pre - it2.pre;
          if(d < 0) break;

          if(d > 0) {
            it2 = i2.next();
          } else {
            if(mildnot(it1, it2).all.size != 0) break;
            it1 = i1.next();
          }
        }
        final FTNode it = it1;
        it1 = i1.next();
        return it;
      }
    };
  }

  /**
   * Processes a hit.
   * @param it1 first item
   * @param it2 second item
   * @return specified item
   */
  FTNode mildnot(final FTNode it1, final FTNode it2) {
    it1.all = mildnot(it1.all, it2.all);
    return it1;
  }

  /**
   * Performs a mild not operation.
   * @param m1 first match list
   * @param m2 second match list
   * @return resulting match
   */
  private static FTMatches mildnot(final FTMatches m1, final FTMatches m2) {
    final FTMatches all = new FTMatches(m1.sTokenNum);
    for(final FTMatch s1 : m1) {
      boolean n = true;
      for(final FTMatch s2 : m2) n &= s1.notin(s2);
      if(n) all.add(s1);
    }
    return all;
  }

  @Override
  public boolean indexAccessible(final IndexContext ic) throws QueryException {
    int is = ic.costs();
    for(final FTExpr e : expr) {
      if(!e.indexAccessible(ic)) return false;
      is = Math.min(Integer.MIN_VALUE, is + ic.costs());
    }
    ic.costs(is);
    return true;
  }

  @Override
  public String toString() {
    return toString(" " + NOT + " " + IN + " ");
  }
}
