package org.basex.data;

import java.util.Arrays;
import java.util.Iterator;
import org.basex.util.Array;
import org.basex.util.Util;

/**
 * AllMatches full-text container,
 * referencing several {@link FTMatch} instances.
 *
 * @author BaseX Team 2005-11, BSD License
 * @author Christian Gruen
 */
public final class FTMatches implements Iterable<FTMatch> {
  /** Full-text matches. */
  public FTMatch[] match = {};
  /** Number of entries. */
  public int size;
  /** Current number of tokens. */
  public int sTokenNum;

  /**
   * Constructor.
   * @param s sets the token number
   */
  public FTMatches(final int s) {
    reset(s);
  }

  /**
   * Resets the match counter.
   * @param s sets the token number
   */
  public void reset(final int s) {
    sTokenNum = s;
    size = 0;
  }

  /**
   * Adds a match entry.
   * @param p position
   */
  public void or(final int p) {
    or(p, p);
  }

  /**
   * Adds a match entry.
   * @param s start position
   * @param e end position
   */
  public void or(final int s, final int e) {
    add(new FTMatch().add(new FTStringMatch(s, e, sTokenNum)));
  }

  /**
   * Adds a match entry.
   * @param s start position
   * @param e end position
   */
  public void and(final int s, final int e) {
    final FTStringMatch sm = new FTStringMatch(s, e, sTokenNum);
    for(final FTMatch m : this) m.add(sm);
  }

  /**
   * Adds a match entry.
   * @param m match to be added
   */
  public void add(final FTMatch m) {
    if(size == match.length) match = size == 0 ?
        new FTMatch[1] : Arrays.copyOf(match, size << 1);
    match[size++] = m;
  }

  /**
   * Removes the specified match.
   * @param i match offset
   */
  public void delete(final int i) {
    Array.move(match, i + 1, -1, --size - i);
  }

  /**
   * Checks if at least one of the matches contains only includes.
   * @return result of check
   */
  public boolean matches() {
    for(final FTMatch m : this) if(m.match()) return true;
    return false;
  }

  /**
   * Combines two matches as phrase.
   * @param all second match list
   * @param dis word distance
   * @return true if matches are left
   */
  public boolean phrase(final FTMatches all, final int dis) {
    int a = 0, b = 0, c = 0;
    while(a < size && b < all.size) {
      final int e = all.match[b].match[0].s;
      final int d = e - match[a].match[0].e - dis;
      if(d == 0) {
        match[c] = match[a];
        match[c++].match[0].e = e;
      }
      if(d >= 0) ++a;
      if(d <= 0) ++b;
    }
    size = c;
    return size != 0;
  }

  @Override
  public Iterator<FTMatch> iterator() {
    return new Iterator<FTMatch>() {
      private int c = -1;
      @Override
      public boolean hasNext() { return ++c < size; }
      @Override
      public FTMatch next() { return match[c]; }
      @Override
      public void remove() { Util.notexpected(); }
    };
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder();
    sb.append(Util.name(this) + "[" + sTokenNum + "]");
    for(final FTMatch m : this) sb.append("\n  " + m);
    return sb.toString();
  }
}
