package yi.iu.processor.color;

import yi.math.VectorUtil;
import java.util.*;
import yi.stat.em.SimplifiedEMCumulator;
import yi.stat.em.*;

/**
 * Class ColorPair is the data structure to store color pairs.  It is currently
 * used to represent the color feature of lines, but it is not restricted to it
 * later.
 */
public class ColorPair extends java.awt.Point{

  public ColorPair() {}

  public ColorPair(int color1, int color2) {
    super(color1, color2);
  }

  public String toString() {
    int[] color1 = ColorUtil.RGB2IntArray(x);
    int[] color2 = ColorUtil.RGB2IntArray(y);
    return new String("(" +
                      "[" + color1[0] + ", " + color1[1] + ", " + color1[2] + "], " +
                      "[" + color2[0] + ", " + color2[1] + ", " + color2[2] + "])");
  }

  public int[] getColors() {
    return new int[] {x, y};
  }

  /**
   * Find the best matched color pair to the color pair from the given colors.
   * Select two colors from the parameter "colors" (all rgb color), compare
   * each combination to itself, and return the best two and the distance.
   * Caller should declare the bes_matches to return the best matche, or passing
   * it as null to skip the value return.  The match pair must be from different
   * element of "colors"
   * @param colors
   * @param best_match
   * @return
   */
  public double bestMatch(int[][] colors, ColorPair best_match) {

    int[] color_x = ColorUtil.RGB2IntArray(x);
    int[] color_y = ColorUtil.RGB2IntArray(y);

    double[] partial_dist_x = new double[colors.length];
    double[] partial_dist_y = new double[colors.length];

    for (int i=0; i<colors.length; i++) {
      partial_dist_x[i] = VectorUtil.EuclideanDistance(color_x, colors[i]);
      partial_dist_x[i] = partial_dist_x[i] * partial_dist_x[i];
      partial_dist_y[i] = VectorUtil.EuclideanDistance(color_y, colors[i]);
      partial_dist_y[i] = partial_dist_y[i] * partial_dist_y[i];
    }
    double min_dist = 256 * 256 * 6;
    int min_dist_index_x = -1, min_dist_index_y = -1;
    int half = colors.length / 2;
    for (int i=0; i<partial_dist_x.length; i++) {
      for (int j=0; j<partial_dist_y.length; j++) {
        if (i==j) continue;
        if (i <= half && j >= half || i >= half && j <= half) { // must on different side
          double dist = partial_dist_x[i] + partial_dist_y[j];
          if (min_dist_index_x == -1 || dist < min_dist) {
            min_dist = dist;
            min_dist_index_x = i;
            min_dist_index_y = j;
          }
        }
      }
    }
    if (best_match != null) {
      best_match.x = ColorUtil.intArray2RGB(colors[min_dist_index_x]);
      best_match.y = ColorUtil.intArray2RGB(colors[min_dist_index_y]);
    }
    return Math.sqrt(min_dist);
  }

  public static boolean belongsTo(int[][] colors, SimplifiedEMCumulator em_model,
                           ColorPair match_cp, double threshold) {

    int color_num = colors.length;
    int half = color_num / 2;

    for (int i = 0; i < color_num; i++) {
      for (int j = 0; j < color_num; j++) {
        if (i == j)
          continue;
        if (i <= half && j >= half || i >= half && j <= half) { // must on different side
          double[] data = new double[] {
              colors[i][0], colors[i][1], colors[i][2],
              colors[j][0], colors[j][1], colors[j][2]
          };
          boolean fall_in = emModelFallsIn(em_model, data, threshold);
          if (fall_in) {
            if (match_cp != null) {
              match_cp.x = ColorUtil.intArray2RGB(colors[i]);
              match_cp.y = ColorUtil.intArray2RGB(colors[j]);
            }
            return true;
          }
        }
      }
    }
    return false;
  }

  public static HashSet belongsTo(int[][] colors, HashMap em_models, HashMap model_probs, double threshold) {

    int color_num = colors.length;
    int half = color_num / 2;

    HashSet results = new HashSet();

    for (int i = 0; i < color_num; i++) {
      for (int j = 0; j < color_num; j++) {
        if (i == j)
          continue;
        if (i <= half && j >= half || i >= half && j <= half) { // must on different side
          double[] data = new double[] {
              colors[i][0], colors[i][1], colors[i][2],
              colors[j][0], colors[j][1], colors[j][2]
          };
          HashMap probs = SimplifiedEMCumulator.getProbabilities(em_models,
              model_probs, data);

          for (Iterator it = probs.keySet().iterator(); it.hasNext(); ) {
            Object key = it.next();
            double prob = ( (java.lang.Double) probs.get(key)).doubleValue();
            if (prob > threshold) {
              results.add(key);
            }
          }
        }
      }
    }
    return results;
  }

  static boolean emModelFallsIn(SimplifiedEMCumulator em_model, double[] data, double threshold) {
    try {
      return em_model.fallenIn(data, threshold);
    } catch (GaussianModelSingularException e) {
    }
    return false;
  }


}