package particlefiltering;

import fr.lip6.classifier.SMOSVM;
import hypothesis_validation.HoG_Score;

import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

import descriptors.HoG;
import parameters.tracking_arguments;
import preprocessing.Parser;

public class observation {

	/*
	  Calculates the histogram bin into which an HSV entry falls
	  
	  @param h Hue
	  @param s Saturation
	  @param v Value
	  
	  @return Returns the bin index corresponding to the HSV color defined by
	    \a h, \a s, and \a v.
	*/
	int histo_bin( double h, double s, double v, tracking_arguments tracking_parameters)
	{
	  int hd, sd, vd;

	  /* if S or V is less than its threshold, return a "colorless" bin */
	  vd = Math.min( (int)(v * tracking_parameters.NV / tracking_parameters.V_MAX), tracking_parameters.NV-1 );
	  if( s < tracking_parameters.S_THRESH  ||  v < tracking_parameters.V_THRESH )
	    return tracking_parameters.NH * tracking_parameters.NS + vd;

	  /* otherwise determine "colorful" bin */
	  hd = Math.min( (int)(h * tracking_parameters.NH / tracking_parameters.H_MAX), tracking_parameters.NH-1 );
	  sd = Math.min( (int)(s * tracking_parameters.NS / tracking_parameters.S_MAX), tracking_parameters.NS-1 );
	  return sd * tracking_parameters.NH + hd;
	}
	
	void rgb2hsv (double r, double g, double b, double hsv[]) {

		double v = (r > g  ?  r : g) > b  ?  (r > g  ?  r : g) : b;

		double temp = (r < g  ?  r : g) < b  ?  (r < g  ?  r : g) : b;

		double s;
		if (v == 0) {
			s = 0;
		}
		else {
			s = (v - temp)/v;
		}
		
		double Cr = 0, Cg = 0, Cb = 0;
		
		if ((v - temp) != 0) {
			Cr = (v - r) / (v - temp);
			Cg = (v - g) / (v - temp);
			Cb = (v - b) / (v - temp);
		}

		double h = 0;

		if (r == v) {
			h = Cb - Cg;
		}
		if (g == v) {
			h = 2 + Cr - Cb;
		}
		if (b == v) {
			h = 4 + Cg - Cr;
		}
		h = 60*h;

		if (h < 0) {
			h = h + 360.0;
		}
		hsv[0] = h;
		hsv[1] = s;
		hsv[2] = v;
	}
	
	
	/*
	  Calculates a cumulative histogram as defined above for a given array
	  of images
	  
	  @param img an array of images over which to compute a cumulative histogram;
	    each must have been converted to HSV colorspace using bgr2hsv()
	  @param n the number of images in imgs
	    
	  @return Returns an un-normalized HSV histogram for \a imgs
	*/
	public pHistogram calc_histogram (BufferedImage img, tracking_arguments tracking_parameters)
	{
		pHistogram histos = new pHistogram();
		histos.alloc_vector (tracking_parameters.NH*tracking_parameters.NS + tracking_parameters.NV);
		/*increment appropriate histogram bin for each pixel*/ 
		for(int r = 0; r < img.getHeight(); r++ ) {
			for(int c = 0; c < img.getWidth(); c++ )
			{
				int pixel = img.getRGB(c, r);
				double R = (pixel >> 16) & 255;
				double G = (pixel >> 8) & 255;
				double B = (pixel & 255);
				R *= 1.0/255.0; G *= 1.0/255.0; B *= 1.0/255.0;
				double [] hsbvals = new double[3]; 
				rgb2hsv (R, G, B, hsbvals);
				int bin = histo_bin( hsbvals[0], hsbvals[1], hsbvals[2], tracking_parameters);
				histos.set_data(bin, histos.get_data(bin)+1);
			}
		}		
		return histos;
	}
	
	/*Trying to combine HSV with the HOG descriptor*/
	public pHistogram Hibrid (BufferedImage img, double[] hog_descriptor, tracking_arguments tracking_parameters)
	{

		pHistogram histos = new pHistogram();
		histos.alloc_vector (tracking_parameters.NH*tracking_parameters.NS + tracking_parameters.NV + hog_descriptor.length);
		
		/* increment appropriate histogram bin for each pixel */ 
		for(int r = 0; r < img.getHeight(); r++ ) {
			for(int c = 0; c < img.getWidth(); c++ )
			{
				int pixel = img.getRGB(c, r);
				double R = (pixel >> 16) & 255;
				double G = (pixel >> 8) & 255;
				double B = (pixel & 255);
				R *= 1.0/255.0; G *= 1.0/255.0; B *= 1.0/255.0;
				double [] hsbvals = new double[3]; 
				rgb2hsv (R, G, B, hsbvals);
				int bin = histo_bin( hsbvals[0], hsbvals[1], hsbvals[2], tracking_parameters);
				histos.set_data(bin, histos.get_data(bin)+1);
			}
		}
		
		int begin = tracking_parameters.NH*tracking_parameters.NS + tracking_parameters.NV;

		for (int i = begin, j = 0; i < histos.get_vector().length; i++, j++) {
			histos.set_data(i, hog_descriptor[j]*100);
		}
		
		return histos;
	}
	
	/*
	  Normalizes a histogram so all bins sum to 1.0
	  
	  @param histo a histogram
	*/
	public void normalize_histogram ( pHistogram hist )
	{
		double sum = 0.0;
		int n = hist.get_length();

		/* compute sum of all bins and multiply each bin by the sum's inverse */
		for(int i = 0; i < n; i++ )
			sum += hist.get_data(i);
		double inv_sum = 1.0 / sum;
		for(int i = 0; i < n; i++ ) {
			double v = hist.get_data(i);
			hist.set_data(i, v*inv_sum);
		}
	}

	
	/*
	  Computes a reference histogram for each of the object regions defined by
	  the user

	  @param frame video frame in which to compute histograms; should have been
	    converted to hsv using bgr2hsv in observation.h
	  @param regions regions of \a frame over which histograms should be computed
	  @param n number of regions in \a regions
	  @param export if TRUE, object region images are exported

	  @return Returns an \a n element array of normalized histograms corresponding
	    to regions of \a frame specified in \a regions.
	*/
	public pHistogram compute_ref_histos (BufferedImage image, tracking_arguments tracking_parameters) throws FileNotFoundException, ClassNotFoundException, IOException
	{
		Parser parser = new Parser();

        ArrayList<String> hog_parameters = parser.Get_Array_List (tracking_parameters.hog_parameters_similarity);
		
		pHistogram histogram = get_histogram (image, tracking_parameters, hog_parameters); 
		
		normalize_histogram (histogram);

		return histogram;
	}
	
	/*
	  Computes squared distance metric based on the Battacharyya similarity
	  coefficient between histograms.
	  
	  @param h1 first histogram; should be normalized
	  @param h2 second histogram; should be normalized
	  
	  @return Returns a squared distance based on the Battacharyya similarity
	    coefficient between \a h1 and \a h2
	*/
	public double histo_dist_sq (pHistogram h1, pHistogram h2)
	{
	  double sum = 0.0;
	  int n = h1.get_length();
	  
	  /* According the the Battacharyya similarity coefficient, D = \sqrt{ 1 - \sum_1^n{ \sqrt{ h_1(i) * h_2(i) } } } */
	  for(int i = 0; i < n; i++)
	    sum += Math.sqrt( h1.get_data(i)*h2.get_data(i));
	  return 1.0 - sum;
	}
	
	/*
	  Computes the likelihood of there being a player at a given location in an image
	  
	  @param img image that has been converted to HSV colorspace using bgr2hsv()
	  @param r row location of center of window around which to compute likelihood
	  @param c col location of center of window around which to compute likelihood
	  @param w width of region over which to compute likelihood
	  @param h height of region over which to compute likelihood
	  @param ref_histo reference histogram for a player; must have been
	    normalized with normalize_histogram()
	  
	  @return Returns the likelihood of there being a player at location
	    (\a r, \a c) in \a img
	*/
	public double likelihood 
	   (  BufferedImage original_image, 
	      int r, int c, int w, int h, 
	      pHistogram ref_histo, 
	      tracking_arguments tracking_parameters,
	      ArrayList<String> hog_parameters_similarity,
	      ArrayList<String> hog_parameters_svm,
	      SMOSVM<double[]> cls) throws FileNotFoundException, ClassNotFoundException, IOException
	{
		BufferedImage text_image = null;

		if ( (c - w / 2 + w) > original_image.getWidth() || (r - h / 2 + h) > original_image.getHeight() || (c - w / 2) < 0 || (r - h / 2) < 0) {
			return 0.0;
		}
		else  {
			text_image = original_image.getSubimage(c - w / 2, r - h / 2, w, h);
		}

		pHistogram  histogram = get_histogram (text_image, tracking_parameters, hog_parameters_similarity); 
			
		normalize_histogram (histogram);

		/* compute likelihood as e^{\lambda D^2(h, h^*)} */
		double d_sq = histo_dist_sq (histogram, ref_histo);
		
		double score = 1.0;
		
		//if (tracking_parameters.similarity_option.equals("COMPLEX")) {
		/*	score = HoG_Functions.score (text_image, cls, hog_parameters_svm);
			if (score < 0) {
				score = 0.0;
			}
			else if (score > 1.0) {
				score = 1.0;
			} */       
		//}

		/* final weight */
		double weight = Math.exp(-tracking_parameters.LAMBDA * d_sq ) * score;

		return weight;	  
	}
	
	public pHistogram get_histogram (
			BufferedImage image, 
			tracking_arguments tracking_parameters,
			ArrayList<String> hog_parameters ) 
	       throws FileNotFoundException, ClassNotFoundException, IOException 
	{		
		pHistogram histogram = new pHistogram();
		
		if (tracking_parameters.similarity_option.equals("HSV")) {
			histogram = calc_histogram (image, tracking_parameters);
		}
		else if (tracking_parameters.similarity_option.equals("HOG")) {
			HoG_Score score = new HoG_Score();
			double[] descriptor = score.get_descriptor (false, image, hog_parameters);
            histogram.set_length(descriptor.length);
			histogram.copy_vector(descriptor); 
		}
		else {
			System.err.printf("Option does not exist!\n");
			System.exit(1);
		}
		return histogram;
	}	
}
