package main;

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

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintStream;
import java.util.ArrayList;

import javax.imageio.ImageIO;

import descriptors.HoG;

import parameters.detection_arguments;
import preprocessing.Parser;
import preprocessing.Util;

public class TestHoG1 {
	
	static int border_x = 48; /*pixels*/
	
	static int border_y = 16; /*pixels*/
	
	public static void main(String[] args) throws IOException {
		
		int pyramid_levels = 1;
		
		String input_image = args[0];
		
		/*Reading HoG file parameters*/
		detection_arguments detection_parameters = new detection_arguments(args[1]);
		
		int ranking_size = 100;
		
		BufferedImage in_image = ImageIO.read(new File(input_image)); 
		
		Util util = new Util();
		
		int[] array = util.getImageArray2 (in_image);
		
		Util.writeIntegerPGM (array, in_image.getWidth(), in_image.getHeight(), 0, 255, "lixo.pgm"); 

		BufferedImage[] image_pyramid = Util.Build_Pyramid (in_image, pyramid_levels);
		
		ArrayList<Double> mean = new ArrayList<Double>();
		
		ArrayList<Double> deviation = new ArrayList<Double>();
		
		HoG_Functions.read_hog_vectors (mean, deviation, detection_parameters.hog_normalization);

		
		SMOSVM<double[]> cls = null;
		try {
    		ObjectInputStream obj = new ObjectInputStream(new FileInputStream(detection_parameters.hog));
    	    cls = (SMOSVM<double[]>) obj.readObject();
    	    obj.close();
    	}
    	catch (Exception e){
    		e.printStackTrace();
    	}

		
		for (int level = 0; level < pyramid_levels; level++) {
							
			int width = image_pyramid[level].getWidth();
			
			int height = image_pyramid[level].getHeight();
			
			System.err.printf("width : %d, height : %d\n", width, height);
			
			double[] image = new double[width*height];
			
			for (int i = 0; i <  width*height; i++) {
				image[i] = -Double.MAX_VALUE;
			}
			
			/*double[] temp2 = new double[width*height];
					
			for (int k = 0; k < width*height; k++) {
				temp2[k] = array[k];
			}*/
			
			double[][] ranking_list = init_ranking (ranking_size);
			
			for (int i = 0; i < width - border_x; i++) {
				for (int j = 0; j < height - border_y; j++) {
					//System.err.printf("i : %d, j : %d, w : %d, h :%d\n", i, j, border_x, border_y);
					BufferedImage sub_image =  image_pyramid[level].getSubimage(i, j, border_x, border_y);
					double v = HoG_Functions.Get_HoG_Score (sub_image, mean, deviation, cls, detection_parameters);
					int pos = (j + border_y/2) * width + (i + border_x/2);
					/*if (v < 0) { 
						v = 0; 
					}*/
					//else {
					//	temp2[pos] = array[j * width + i];
					//}
					image[pos] = v;
					ranking (v, i, j, ranking_list, ranking_size);
				}
				if ((i % 25) == 0)
					System.err.printf("+ 25 lines : %d, %d\n", i, width - border_x);
			}
			
			write_svm_weight_image (image, width, height);
			
			/*for (int i = border_x/2; i < width - border_x/2; i++) {
				for (int j = border_y/2; j < height - border_y/2; j++) {
					BufferedImage sub_image =  image_pyramid[level].getSubimage(i - border_x/2, j - border_y/2, border_x, border_y);
					double v = HoG_Functions.Get_HoG_Score (sub_image, mean, deviation, cls);
					int pos = j * width + i;
					image[pos] = v;
					ranking (v, i, j, ranking_list, ranking_size);
					
					//try {
					//	ImageIO.write(sub_image, "png", new File("./test/win_"+String.format("%02d", i)+"_"+String.format("%02d", j)+".png"));
						//ImageIO.write(Original, "png", new File(out_path + String.format("%05d", iframe) + "/detection.png"));
					//}
					//catch (Exception e) {
					//	System.err.println("cannot store image");
					//}
					
				}
				if ((i % 25) == 0)
					System.err.printf("+ 25 lines : %d, %d\n", i, width - border_x);
				
			} */
			
			
			Util.writeDoubletoPGM (image, width, height, -2, 2, "svm_"+level+"_");
			//Util.writeDoubletoPGM (temp2, width, height, 0, 255, "pixels_"+level+"_");
			print_ranking (ranking_list, ranking_size, image_pyramid[level], "rank_"+level+".png", mean, deviation, cls);
		}		
	}
	
	public static double[][] init_ranking (int rank_size) {
		double[][] rank = new double[rank_size][3];
		for (int i = 0; i < rank_size; i++) {
			rank[i][0] = -Double.MAX_VALUE;
			rank[i][1] = -1;
			rank[i][2] = -1;
		}
		return rank;
	}
	
	public static void out_ranking (double[][] rank, int rank_size, double score) {
		System.out.printf("Score recebido : %f\n", score);
		for (int i = 0; i < rank_size; i++) {
			System.out.printf("pos : %d,  score : %f, x : %f, y : %f\n", i, rank[i][0], rank[i][1], rank[i][2]);
		}
		System.out.printf("##################\n");
	}
	
	public static void ranking (double score, int x, int y, double[][] rank, int rank_size) {
		double min = Double.MAX_VALUE;
		int pos = Integer.MAX_VALUE;
		for (int i = 0; i < rank_size; i++) {
			if (rank[i][0] < min) {
				min = rank[i][0];
				pos = i;
			}
		}
		
		assert (pos != Integer.MAX_VALUE);
			
		if (rank[pos][0] < score) {
			rank[pos][0] = score;
			rank[pos][1] = x;
			rank[pos][2] = y;
		}
		//out_ranking (rank, rank_size, score);
	}
	
	public static void print_ranking (double[][] rank, int rank_size, BufferedImage image, String name, ArrayList<Double> mean, ArrayList<Double> std, SMOSVM<double[]> cls) {
		
		out_ranking (rank, rank_size, -1);
		
		
    	
    	for (int i = 0; i < rank_size; i++) {
			int x = (int)rank[i][1]; 
			
			int y = (int)rank[i][2];
			
			int w = border_x;
			
			int h = border_y;

			if ((x >= 0) && (y >= 0) && (x + w < image.getWidth()-1) && (y + h < (image.getHeight()-1))) {
				System.err.printf("x : %d, y : %d, w : %d, h : %d, width : %d, height : %d\n", x, y, w, h, image.getWidth(), image.getHeight());
				BufferedImage sub_image = image.getSubimage(x, y, w, h);


				HoG hog = new HoG();
				//double[] hog_descriptor = hog.HoGFixedNumberOfCells (image, 4, 4, 2); 
				double[] hog_descriptor = hog.hog(sub_image, 5, 1, 3, 16, 16, false);
				
				/*for(int k = 0; k < hog_descriptor.length; k++) {
		    		hog_descriptor[k] = (hog_descriptor[k]-mean.get(k))/std.get(k);
		    	}*/ 
		    	double v = cls.valueOf(hog_descriptor);

				System.out.printf("Window : %d, Score : %f\n", i, v);

				for (int j = 0; j < hog_descriptor.length; j++) {
					System.out.printf("%f ", hog_descriptor[j]);
				}
				System.out.printf("\n"); 
				
				try {
				 	ImageIO.write(sub_image, "png", new File("./test/win_"+String.format("%02d", i)+".png"));
					//ImageIO.write(Original, "png", new File(out_path + String.format("%05d", iframe) + "/detection.png"));
				}
				catch (Exception e) {
					System.err.println("cannot store image");
				}
				
			}	    	
    	}
		
		BufferedImage temp = Util.imageScale (image, 1.0);
		
		Graphics2D g = temp.createGraphics();
		g.setStroke(new BasicStroke(1.0f));		
		g.setColor(Color.yellow);
		
		for (int i = 0; i < rank_size; i++) {
			int x = (int)rank[i][1]; 
			
			int y = (int)rank[i][2];
			
			int w = border_x;
			
			int h = border_y;

			g.drawRect(x, y, w, h);
			

			
		}
		
		g.dispose();
		
		try {
			ImageIO.write(temp, "png", new File(name));
		}
		catch (Exception e) { 
				System.err.printf("cannot write image\n");
		}
		
	}
	
	/*This function ...*/
	public static void write_svm_weight_image (double[] array, int width, int height) {

		int size = width * height;

		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			
		/*Normalization*/
		double vmax = 0.0, vmin = 0.0, min = 0.0, max = 255.0;
	    
	    if (vmax == vmin) {
	        vmax = -Double.MAX_VALUE; vmin = +Double.MAX_VALUE;
	        for (int i = 0; i < size; i++) {
                vmax = Math.max(vmax, array[i]);
                if (array[i] != -Double.MAX_VALUE) {
	                vmin = Math.min(vmin, array[i]);
                }
	        }
	    }
	    
	    for (int i = 0; i < size; i++) {
	    	if (array[i] == -Double.MAX_VALUE) {
                array[i] = vmin;
            }
	    }
	 
	    for (int i = 0; i < size; i++) {
	    	array[i] = ( (max - min)*(array[i] - vmin))/(vmax - vmin) + min;
        }	    
	    
	    Parser parser = new Parser();
	    
	    PrintStream out_stream = parser.Open_Print_Stream ("score_values.txt");
	    
	    parser.Write_Double_to_int_Print_Stream (out_stream, array);
	    
	    parser.Close_Print_Stream(out_stream , "score_values.txt");
	    
	    /*End-Normalization*/
		
		
		for (int i = 0; i < size; i++) {
			
			double pixel = array[i];
			
			int x = i % width;
			
			int y = i / width;
			
			double[] RGB = new double[3];
			
			choose_color(RGB, pixel);
			
			int rgb;
			
			//int A = (0 << 24);
	   		int R = ((int)(RGB[0]*255) << 16);
			int G = ((int)(RGB[1]*255) << 8);
			int B = ((int)(RGB[2]*255));
			//System.err.printf("R : %f G : %f B : %f\n", RGB[0], RGB[1], RGB[2]);
			rgb = ( R | G | B );				
			
			image.setRGB(x, y, rgb);
		}

		try {
			ImageIO.write(image, "png", new File("loco.png"));
		}
		catch (Exception e) { 
				System.err.printf("cannot write image\n");
		}
		
	}
	
	/*This function ...*/
	public static void write_svm_weight_image2 (double[] array, int width, int height) {

		int size = width * height;

		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			
		/*Normalization*/
		double vmax = 0.0, vmin = 0.0, min = 0.0, max = 255.0;
	    
	    if (vmax == vmin) {
	        vmax = -Double.MAX_VALUE; vmin = +Double.MAX_VALUE;
	        for (int i = 0; i < size; i++) {
                vmax = Math.max(vmax, array[i]);
	            vmin = Math.min(vmin, array[i]);
	        }
	    }
	 
	    for (int i = 0; i < size; i++) {
	    	array[i] = ( (max - min)*(array[i] - vmin))/(vmax - vmin) + min;
        }	    
	    /*End-Normalization*/
		
		
		for (int i = 0; i < size; i++) {
			
			double pixel = array[i];
			
			int x = i % width;
			
			int y = i / width;
			
			int rgb;
			
			int A = (0 << 24);
	   		int R = ((int)(pixel) << 16);
			int G = ((int)(pixel) << 8);
			int B = (int)((255 - pixel));
			rgb = ( R | G | B | A );				
			
			image.setRGB(x, y, rgb);			
		}

		try {
			ImageIO.write(image, "png", new File("loco.png"));
		}
		catch (Exception e) { 
				System.err.printf("cannot write image\n");
		}
		
	}
	
	static void choose_color(double RGB[], double eigenvalue)
	{
		
		double[] cmin = {0.0,  0.0, 1.0};
		double[] cmax = {1.0,  1.0, 0.0};
		double[] ca = {0.0, -0.1, 0.6};
		double[] cb = {0.6, -0.3, 0.0};
		double max = 255.0;
		double min = 0.0;

		if (eigenvalue < min) {
			RGB[0] = cmin[0]; 
			RGB[1] = cmin[1];
			RGB[2] = cmin[2];
		}
		else if (eigenvalue > max) {
			RGB[0] = cmax[0];   
			RGB[1] = cmax[1]; 
			RGB[2] = cmax[2];
		}
		else {
			double log_e = (eigenvalue);
			double log_max = (max);
			double log_min = (min);
			double s = (log_e - log_min)/(log_max - log_min);
			double r = 1 - s;
			double u = Math.sin(2*Math.PI * s);
			double v = 0.3*(1 - Math.cos(2*Math.PI * s))/2.0;
			int k;
			for (k = 0; k < 3; k++) {
				RGB[k] = r * cmin[k] + s * cmax[k] + u * ca[k] + v * cb[k];
				if((RGB[k] >= 0) && (RGB[k] <= 1.0)){
					System.err.printf("R : %f G : %f B : %f\n", RGB[0], RGB[1], RGB[2]);
				}
			}
		} 
	}

	
	
	
}
