//Source file: D:\\Projects\\src\\yi\\iu\\imgdb\\learn\\ObjGMMBayAdapter.java

package yi.iu.imgdb.learn;

import java.util.*;
import yi.ds.hash.*;
import yi.stat.em.*;
import yi.util.data.*;
import yi.util.io.*;
import yi.util.config.*;
import org.jmat.gui.*;


/**
 * Like ObjGMMAdapter.  But all the objects are learned (each as a Gaussian), and
 * the probability p(o|f) will be calculated upon all the models through Baysian's
 * rules (like in the E step of EM); whereas in ObjGMMAdapter we use
 * (f|o)/f(|non-o)
 */
public class ObjGMMBayAdapter extends StatModelAdapter{

  /**
   * HashMap of GaussianMixtureModel.  Each entry corresponds to one object
   */
  HashMap m_models = new HashMap();

  /**
   * Weights of each object
   */
  DoubleScoreHashMap m_weights = new DoubleScoreHashMap();

  /**
   * This adapter only learns on one feature
   */
  String m_feature = null;

  /**
   * When draw the distribution, how many of points will be sampled.
   */
  public int DISTRIBUTION_DRAW_SLICE_NUM = 100;

  /**
   * Whether to show the compaison of data histogram and model distribution
   * one plot
   */
  public boolean SHOW_DATA_MODEL_PLOT = false;

  protected ObjGMMBayAdapter() {

  }

  /**
   * @param data_center
   * @param features
   * @param imgs
   */
  public ObjGMMBayAdapter(ImgDataCenter data_center, LinkedList img_list, LinkedList ft_list, LinkedList obj_list)
  {
    super(data_center, img_list, ft_list, obj_list);

    // this method only learns on one feature
    m_feature = (String)m_featureList.getFirst();

    int feature_len = data_center.getFeatureLength(m_feature);
//    int feature_len = m_imgDataCenter.getFeatureLength(m_feature);

    // learn the objects one by one
    for (Iterator o = m_objectList.iterator(); o.hasNext(); ) {
      Object object = o.next();

      System.out.println();
      System.out.println("learning on \"" + object + "\"" + "\n");

      // get training set
      LinkedList posTrainImgs = getPositiveInstances(img_list, object);

      // number of images having the object (it will be used for p(o)
      m_weights.putScore(object, posTrainImgs.size());

      // group the feature vectors in the positive ones to construct positive model
      double[][] data = m_imgDataCenter.groupFeatureVectors(posTrainImgs, m_feature);
      System.out.println("total positive training feature vectors #= " +
                         data.length + "\n");

      // maximum of the training positives.
      double[] max_data = yi.stat.StatUtil.maximumOnCol(data);
      double[] min_data = yi.stat.StatUtil.minimumOnCol(data);


      /** @todo change the feature length to show plot */
      // for SHOW_DISTRIBUTION, save the frequency functions
      // each entry is for one dimension.  Each entry of the linked list
      // is the one for a specified m (as in m mixture models)
      LinkedList[] ffs = new LinkedList[feature_len];

      /** @todo see whether m can be fixed; or a big m is enough */
      int M = 20;
      GaussianMixtureModel gmm =
          new AlignedGaussianMixtureModel(M, data, min_data, max_data); // suppose the data is within [0,1)
      gmm.learnTheModel();

      m_models.put(object, gmm);

    } // end loop of objects
  }



  /**
   * Given score for each images in the given image list for each object.
   * The score here is after log.
   * @param imgs
   * @return StatTestRst
   */
  public StatTestRst test(LinkedList imgs) {
    StatTestRst rst = new StatTestRst();
    for (Iterator j = imgs.iterator(); j.hasNext(); ) {
      String img = (String)j.next();
      System.out.println(img);
      FeatureRepresentationList fr_list = m_imgDataCenter.getImgFeatureList(img, m_feature);
      LinkedList feature_list = Conversion.toLinedList(fr_list.as2DDoubleArray());
      DoubleScoreHashMap prob = getProbabilities(m_models, feature_list);
      for (Iterator o = prob.keySet().iterator(); o.hasNext(); ) {
        Object object = o.next();
        double d = prob.getScore(object);
        rst.putScore(object, img, d);
      }
    }
    return rst;
  }

  public DoubleScoreHashMap getProbabilities(HashMap gmms, Collection features) {
    DoubleScoreHashMap rsts = new DoubleScoreHashMap();
    for (Iterator i = features.iterator(); i.hasNext(); ) {
      double[] feature = (double[]) i.next();
      OutputResult.print(System.out, feature);
      // get density from each model
      DoubleScoreHashMap probs = new DoubleScoreHashMap();
      double prob_sum= 0;
      for (Iterator m= gmms.keySet().iterator(); m.hasNext(); ) {
        Object obj = m.next();
        GaussianMixtureModel gmm = (GaussianMixtureModel)gmms.get(obj);
        double density = 0;
        try {
          density = gmm.getDensity(feature);
        } catch (ModelSingularException  e) {
          e.printStackTrace();
        }
//        if (((String)obj).compareToIgnoreCase("bg")==0) {
//          density = 1;
//        }
        double obj_weight = m_weights.getScore(obj);
        System.out.println(obj.toString() + ": " + density + ", " + obj_weight);
        density *= obj_weight;
        prob_sum += density;
        probs.putScore(obj, density);
      }
      // normalize the prob
      for (Iterator m= probs.keySet().iterator(); m.hasNext();) {
        Object obj = m.next();
        double prob = probs.getScore(obj);
        probs.putScore(obj, prob/prob_sum);
        System.out.println(obj.toString() + ": " + prob/prob_sum + ": " + prob + "/ " + prob_sum);
      }
      for (Iterator j = probs.keySet().iterator(); j.hasNext(); ) {
        Object obj = j.next();
        double prob = ((Double)probs.get(obj)).doubleValue();
        double log_prob = Math.log(prob);
        rsts.putScoreOnCondition(obj, log_prob, true);
      }
    }
    return rsts;
  }


  /**
   * Given the learning images, seperate them to four parts: positive training
   * images, negative training images, positive verifying images, and negative
   * verifying images.  This code ensures that the portion of positive images
   * in training images is similar to that in verifying images
   * @param img_list
   * @param object
   */
  LinkedList getPositiveInstances(LinkedList img_list, Object object) {
    LinkedList positive = new LinkedList();
    for (Iterator i = img_list.iterator(); i.hasNext(); ) {
      String img = (String) i.next();
      LinkedList desc_list = m_imgDataCenter.getImgDescriptions(img);
      if (desc_list !=null && desc_list.contains(object)) {
        positive.add(img);
      }
    }
    return positive;
  }


  public static class Test {

    public final static String USAGE = "Usage: [-d img_data_center_file] [-m model_file] [-i img_list_file] [-f feature] [-r rst_file_name]";

    public static void main(String[] args) {

      // init configuration
      Configuration.init(args);

      try {
        // read in an ObjGMMAdapter object
        String model_file = ReadArgu.getOption(
            'm', args,
            "model0.ser"
            );

        ObjGMMBayAdapter gmm = (ObjGMMBayAdapter)ObjGMMBayAdapter.createFromFile(model_file);

        // init the datacenter
        String data_center_name = ReadArgu.getOption(
            'd', args,
            "groundtruth.ser"
            );

        ImgDataCenter data_center = ImgDataCenter.createFromFile(
            data_center_name);

        // get the image list file name
        String img_list_file = ReadArgu.getOption('i', args);

        // image list
        LinkedList img_list = null;
        if (img_list_file != null) {
          String[] images = yi.util.io.ReadArgu.readStrings(img_list_file);
          img_list = yi.util.data.Conversion.asLinkedList(images);
        }

        // System.out.println(gmm.m_models);

        StatTestRst rst = gmm.test(img_list);

        HashMap rpcs = rst.rpcAnalysis(data_center); // right positive curves


        DoubleScoreHashMap roc_scores = rst.getROCScores(rpcs);
        System.out.println("roc scores:");
        System.out.println(roc_scores.toStringBySkyOrder());

        Plot2DPanel pp = rst.drawROCs(rpcs);

        String rst_output_file = ReadArgu.getOption('r', args);
        if (rst_output_file != null) {
          pp.setFixedBounds(0,0,1);
          pp.setFixedBounds(1,0,1);
          pp.invalidate();
          pp.toGraphicFile(new java.io.File(rst_output_file + ".png"));
        }

      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

}
