package org.basex.index.ft;

import java.io.IOException;
import org.basex.data.Data;
import org.basex.data.FTMatches;
import org.basex.index.Index;
import org.basex.index.IndexCache;
import org.basex.io.random.DataAccess;
import org.basex.util.list.IntList;

/**
 * This abstract class defines methods for the available full-text indexes.
 *
 * @author BaseX Team 2005-11, BSD License
 * @author Christian Gruen
 */
public abstract class FTIndex implements Index {
  /** Cache for number of hits and data reference per token. */
  final IndexCache cache = new IndexCache();
  /** Data reference. */
  final Data data;
  /** Scoring mode. 1 = document based, 2 = text-node based .*/
  final int scm;
  /** Minimum scoring value. */
  final double max;
  /** Minimum scoring value. */
  final double min;

  /**
   * Returns a new full-text index instance.
   * @param d data reference
   * @param wild wildcard index
   * @return index instance
   * @throws IOException IOException
   */
  public static FTIndex get(final Data d, final boolean wild)
      throws IOException {
    return wild ? new FTTrie(d) : new FTFuzzy(d);
  }

  /**
   * Constructor.
   * @param d data reference
   */
  FTIndex(final Data d) {
    data = d;
    scm = d.meta.scoring;
    max = Math.log(data.meta.maxscore + 1);
    min = Math.log(data.meta.minscore - 1);
  }

  /**
   * Returns an iterator for an index entry.
   * @param off offset on entries
   * @param size number of pre/pos entries
   * @param da data source
   * @param fast fast evaluation
   * @return iterator
   */
  final synchronized FTIndexIterator iter(final long off, final int size,
      final DataAccess da, final boolean fast) {

    // cache results
    da.cursor(off);
    final IntList vals = new IntList();
    for(int c = 0, lp = 0; c < size;) {
      if(lp == 0) {
        if(scm > 0) vals.add(da.readNum());
        lp = da.readNum();
        vals.add(lp);
      }
      final int pr = lp;
      vals.add(da.readNum());
      while(++c < size) {
        lp = da.readNum();
        vals.add(lp);
        if(lp != pr) break;
        vals.add(da.readNum());
      }
    }

    return new FTIndexIterator() {
      final FTMatches all = new FTMatches(toknum);
      int c, pre, lpre;
      double sc = -1;

      @Override
      public synchronized boolean more() {
        if(c == vals.size()) return false;
        if(lpre == 0) {
          if(scm > 0) sc = (Math.log(vals.get(c++)) - min) / (max - min);
          lpre = vals.get(c++);
        }
        pre = lpre;

        all.reset(toknum);
        all.or(vals.get(c++));

        while(c < vals.size() && (lpre = vals.get(c++)) == pre) {
          final int n = vals.get(c++);
          if(!fast) all.or(n);
        }
        return true;
      }

      @Override
      public synchronized FTMatches matches() {
        return all;
      }

      @Override
      public synchronized int next() {
        return pre;
      }

      @Override
      public synchronized int size() {
        return size;
      }

      @Override
      public synchronized double score() {
        return sc;
      }

      @Override
      public String toString() {
        return Integer.toString(size);
      }
    };
  }
}
