package ipipan.poliqarp.stat;

import ipipan.poliqarp.logic.Match;
import ipipan.poliqarp.logic.Segment;
import ipipan.poliqarp.logic.Interpretation;

import java.util.Random;
import java.util.HashSet;

/**
 * Class responsible for selecting grouping information from Matches.
 * A Selector consists of an array of SelectionParts (indicating
 * segments and their attributes) and an interpretation selection mode
 * (first, random, combine) for segments with multiple
 * interpretations.  By default a random interpretation is chosen.
 */
public class Selector {
   /**
    * Pick random interpretation.
    */
   public static final int INTERP_RANDOM  = 1;

   /**
    * Pick first interpretation.  Added for taggers ranking
    * interpretations according to some rank, right now rather useless.
    */
   public static final int INTERP_FIRST = 2;

   /**
    * In case of multiple interpretations, combine different values
    * of an attribute into one, separated by | (for example gen|acc).
    */
   public static final int INTERP_COMBINE = 3;

   static private String[] fakeStringArray = new String[0];

   int interpMode;
   int requirements;
   Random generator;
   SelectorPart[] sel;

   public Selector(String groupby, Tagset tagset)
   throws StatQueryException {
      String[] g;
      int i;

      i = groupby.indexOf(" interp ");
      if (i > -1) {
         interpMode = getInterpMode(groupby.substring(i + 8));
         groupby = groupby.substring(0, i);
      } else
         interpMode = INTERP_RANDOM;
      if (interpMode == INTERP_RANDOM)
         generator = new Random();

      g = groupby.split(" *, *");

      sel = new SelectorPart[g.length];
      requirements = 0;

      for (i = 0; i < g.length; i++) {
         sel[i] = new SelectorPart(g[i], tagset);
         requirements |= sel[i].getRequirements();
      }
   }

   /**
    * Parses interpretation selection mode.
    */
   static int getInterpMode(String s) {
      if (s.equalsIgnoreCase("random"))
         return INTERP_RANDOM;

      if (s.equalsIgnoreCase("first"))
         return INTERP_FIRST;

      if (s.equalsIgnoreCase("combine"))
         return INTERP_COMBINE;

      return 0;
   }

   /**
    * Checks if calculating the selector's value for a query match
    * requires transmitting orths, lemmas and/or tags from the
    * server.
    *
    * @return requirements for segment transmission, as defined in
    * SelectionPart
    */
   public int getRequirements() {
      return requirements;
   }

   /**
    * Turns a Match into a String representing the values of the
    * grouping attributes.
    *
    * @param match a match to evaluate
    * @return values of the selector's attributes for a given match,
    * separated by spaces.  If the interpretation mode is set to
    * "combine", different possible values of a single attribute are
    * separated by | (without spaces).
    */
   public String value(Match match) {
      Segment[] mr, ml, m;
      StringBuilder res = new StringBuilder();
      int i, j, s;
      Interpretation[] disamb;
      HashSet<String> i12ns;
      String[] i12na;
      String tmp;

      if (match == null)
         System.out.println("Selector.value: null match");

      ml = match.getLeftMatch();
      mr = match.getRightMatch();

      if (ml != null && ml.length > 0) {
         m = new Segment[mr.length + ml.length];
         System.arraycopy(mr, 0, m, 0, mr.length);
         System.arraycopy(ml, 0, m, mr.length, ml.length);
      } else
         m = mr;

      for (i = 0; i < sel.length; i++) {
         if (i > 0)
            res.append(' ');

         s = sel[i].getSegno();
         if (s > 0) s --;
         if (s < 0) s = m.length + s;
         if (s < 0 || s >= m.length) {
            res.append(' ');
            continue;
         }

         tmp = sel[i].orthValue(m[s]);
         if (tmp != null) {
            res.append(tmp);
            continue;
         }

         disamb = m[s].getDisamb();

         if (interpMode == INTERP_RANDOM || interpMode == INTERP_FIRST) {
            if (interpMode == INTERP_RANDOM)
               j = generator.nextInt(disamb.length);
            else
               j = 0;

            res.append(sel[i].tagValue(disamb[j]));
            continue;
         }

         i12ns = new HashSet<String>();

         for (j = 0; j < disamb.length; j ++)
            i12ns.add(sel[i].tagValue(disamb[j]));

         i12na = i12ns.toArray(fakeStringArray);

         for (j = 0; j < i12na.length; j ++) {
            if (j > 0)
               res.append('|');
            res.append(i12na[j]);
         }
      }
      return res.toString();
   }
}
