package yi.iu.ft.local;

import java.util.Iterator;
import java.util.ArrayList;
import java.util.Arrays;
import yi.stat.hist.Hist;

/**
 * Utility class used to generate the globalized histogram for local features
 * (which implements the Globalizable interface).  This class is implemented
 * as singleton, so call getSingleton() to get the instance.  In addition, some
 * static function interfaces are also provided and they can be called directly
 * too.
 */
public class Globalize {
  private static Globalize m_singleton = null;

  private Globalize() {
    super();
  }

  public static Globalize getSingleton() {
    if (m_singleton == null) m_singleton = new Globalize();
    return m_singleton;
  }

  /**
   * Determine whether an instance is valid or not.  A constraint array is used
   * to specify the value ranges of all the attributes.  Say, there are two
   * attributes, each with values from 0 to 99 and will be put to 4 bins.
   * Constraint array of {false, false, true, false, true, false, false, false}
   * requires the first attribute has value of [50,75) and the second has [0,25)
   * @param f the local feature to be globalized
   * @param t globalization template
   * @param constraints the constriant array
   * @return an array with each element specify whether one instance is valid or
   *         not
   */
  public boolean[] instancesValid(Globalizable f, GlobalizationTempl t, boolean[] constraints) {
    boolean[] valid = new boolean[f.getInstanceNum()];
    Iterator data = f.listInstances();
    int count = 0;
    while (data.hasNext()) {
      double[] d = (double[])data.next();
      double[] g = new double[0];
      for (int i=0; i<d.length; i++) {
        double[] h = Hist.getSingleton().getHist(d[i], t.getMinOn(i), t.getBinSizeOn(i), t.getBinNumOn(i));
        g = concatenate(g, h);
      }
      valid[count] = true;
      for (int i=0; i<g.length; i++) {
        if (constraints[i] && g[i] ==0) {
          valid[count] = false;
          break;
        }
      }
      count++;
    }
    return valid;
  }

  public double[] getGlobalizedHist(Globalizable f, GlobalizationTempl t, boolean[] constraints)
  throws GlobalizationException{
    double[] g = new double[0];
    // get through every attribute pair (ai, aj)
    int anum = f.getLocalAttributeNum();
    if (anum == 0) throw new GlobalizationException("Attribute number = 0");
    int inum = f.getInstanceNum();
    boolean[] ivalid = instancesValid(f, t, constraints);
    for (int i=0; i<anum; i++) {
      for (int j=0; j<anum; j++) {
        Iterator stats = t.listStatsOn(i, j);
        if (stats == null) continue;
        // get the two data list
        double[] a1= new double[inum];
        double[] a2= new double[inum];
        Iterator data = f.listInstances();
        int count = 0;
        if (data != null) {
          while (data.hasNext()) {
            double[] d = (double[])data.next();
            a1[count] = d[i];
            a2[count] = d[j];
            count ++;
          }
        }
        if (count != inum) throw new GlobalizationException("Didn't get enough data");
        // now construct the histogram
        while (stats.hasNext()) {
          String stat = (String)stats.next();
          double[] h = Hist.makeHist(a1, a2, stat, t.getMinOn(i), t.getBinSizeOn(i), t.getBinNumOn(i), ivalid);
          g = concatenate(g, h);
        }
      }
    }
    return g;
  }

  public double[] getGlobalizedHist(Globalizable f, GlobalizationTempl t)
  throws GlobalizationException{
    double[] g = new double[0];
    // get through every attribute pair (ai, aj)
    int anum = f.getLocalAttributeNum();
    if (anum == 0) throw new GlobalizationException("Attribute number = 0");
    int inum = f.getInstanceNum();
    for (int i=0; i<anum; i++) {
      for (int j=0; j<anum; j++) {
        Iterator stats = t.listStatsOn(i, j);
        if (stats == null) continue;
        // get the two data list
        double[] a1= new double[inum];
        double[] a2= new double[inum];
        Iterator data = f.listInstances();
        int count = 0;
        if (data != null) {
          while (data.hasNext()) {
            double[] d = (double[])data.next();
            a1[count] = d[i];
            a2[count] = d[j];
            count ++;
          }
        }
        if (count != inum) throw new GlobalizationException("Didn't get enough data");
        // now construct the histogram
        while (stats.hasNext()) {
          String stat = (String)stats.next();
          double[] h = Hist.makeHist(a1, a2, stat, t.getMinOn(i), t.getBinSizeOn(i), t.getBinNumOn(i));
          g = concatenate(g, h);
        }
      }
    }
    return g;
  }

  public static double[] makeGlobalizedHist(Globalizable f, GlobalizationTempl t)
  throws GlobalizationException{
    return Globalize.getSingleton().getGlobalizedHist(f, t);
  }

  public static double[] makeGlobalizedHist(Globalizable f, GlobalizationTempl t, boolean[] constraints)
  throws GlobalizationException{
    return Globalize.getSingleton().getGlobalizedHist(f, t, constraints);
  }

  private double[] concatenate(double[] a, double[] b) {
    int alen = a.length, blen = b.length;
    double[] c = new double[alen + blen];
    for (int i = 0; i < alen; i++) c[i] = a[i];
    for (int i = 0; i < blen; i++) c[alen+i] = b[i];
    return c;
  }

}