package main;

import fr.lip6.classifier.SMOSVM;
import hypothesis_validation.HoG_Score;
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 preprocessing.Parser;
import preprocessing.Util;

public class HoG_Convolution {
	
	static int border_x = 72; /*pixels*/
	
	static int border_y = 24; /*pixels*/
	
	static int ranking_size = 100; /*number of windows in the ranking*/
	
	static int pyramid_levels = 4; /*pyramid levels*/
	
	public static void main(String[] args) throws IOException {
		
		String input_image = args[0];
		
		String map_image = args[1];
		
		String rank_image = args[2];
		
		String hog_object = args[3];
		
		String hog_parameters = args[4];
		
		/*Reading HoG file parameters*/
		Parser parser = new Parser();
		
		ArrayList<String> list = parser.Get_Array_List (hog_parameters);
		
		BufferedImage in_image = ImageIO.read(new File(input_image)); 
		
		BufferedImage[] image_pyramid = Util.Build_Pyramid (in_image, pyramid_levels);
		
		SMOSVM<double[]> cls = null;
		try {
    		ObjectInputStream obj = new ObjectInputStream(new FileInputStream(hog_object));
    	    cls = (SMOSVM<double[]>) obj.readObject();
    	    obj.close();
    	}
    	catch (Exception e){
    		e.printStackTrace();
    	}
    	
    	HoG_Score score = new HoG_Score();
    
		for (int level = 0; level < pyramid_levels; level++) {
			
			int avaliations = 0, positives = 0;
							
			int width = image_pyramid[level].getWidth();
			
			int height = image_pyramid[level].getHeight();
	
			double[] image = new double[width*height];
			
			double[] weights= new double[width*height];
			
			for (int i = 0; i <  width*height; i++) {
				image[i] = -Double.MAX_VALUE; 
			}
			
			for (int i = 0; i <  width*height; i++) {
				weights[i] = 0; 
			}
			
			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++) {
					
					BufferedImage sub_image = image_pyramid[level].getSubimage(i, j, border_x, border_y);
					
					double v = score.get_score (sub_image, cls, list);
					
					int pos = (j + border_y/2) * width + (i + border_x/2);
					
					image[pos] = v;
					
					ranking (v, i, j, ranking_list, ranking_size);
					
					if (v > 0.0) {
						weights[pos] = v;
						positives++;
					}
					
					avaliations++;
				}
				if ((i % 25) == 0)
					System.err.printf(". ");
			}
			
			write_svm_weight_as_pgm_image (image, width, height, map_image+"_level_"+level+".pgm");
			
			write_svm_weight_as_pgm_image (weights, width, height, map_image+"_weights_"+level+".pgm");
						
			print_ranking_boxes (ranking_list, ranking_size, image_pyramid[level], rank_image+"_A_level_"+level+".png");
			
			print_ranking_crosses (ranking_list, ranking_size, image_pyramid[level], rank_image+"_B_level_"+level+".png");
						
			print_ranking_region (ranking_list, ranking_size, image_pyramid[level], rank_image+"_C_level_"+level+".png");
			
			System.out.printf("Classifications : %d, positives : %d\n", avaliations, positives);
		}		
	}
	
	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 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;
		}
	}	
	
	public static void print_ranking_boxes (
			double[][] rank, 
			int rank_size, 
			BufferedImage image,
			String file_name) {		
		
		BufferedImage temp = Util.imageScale (image, 1.0);

		Graphics2D g = temp.createGraphics();
		
		g.setStroke(new BasicStroke(1.0f));	 	
		
		g.setColor(Color.black);
		
		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(file_name));
		}
		catch (Exception e) { 
				System.err.printf("cannot write image\n");
		}
	}
	
	public static void print_ranking_crosses (
			double[][] rank, 
			int rank_size, 
			BufferedImage image,
			String file_name) {		
		
		BufferedImage temp = Util.imageScale (image, 1.0);

		Graphics2D g1 = temp.createGraphics();
		
		g1.setStroke(new BasicStroke(1.0f));		
		
		g1.setColor(Color.white);
		
		Graphics2D g2 = temp.createGraphics();
		
		g2.setStroke(new BasicStroke(1.0f));		
		
		g2.setColor(Color.black);
		
		for (int i = 0; i < rank_size; i++) {
			int size = 5;
			int x = (int)rank[i][1] + border_x/2; 
			int y = (int)rank[i][2] + border_y/2;
			g1.drawLine(x, y, x+size, y);
			g1.drawLine(x, y, x, y+size);
			g1.drawLine(x, y, x-size, y);
			g1.drawLine(x, y, x, y-size);
			g2.drawLine(x+1, y+1, x+1+size, y+1);
			g2.drawLine(x+1, y+1, x+1, y+1+size);
			g2.drawLine(x+1, y+1, x+1-size, y+1);
			g2.drawLine(x+1, y+1, x+1, y+1-size);
			
			if (i == 0) {
				g1.drawRect((x - border_x/2), (y - border_y/2), border_x, border_y);
				g2.drawRect((x + 1 - border_x/2), (y + 1 - border_y/2), border_x, border_y);
			}
		}
		
		g1.dispose();
		g2.dispose();
		
		try {
			ImageIO.write(temp, "png", new File(file_name));
		}
		catch (Exception e) { 
				System.err.printf("cannot write image\n");
		}
	}
	
	public static void print_ranking_region (
			double[][] rank, 
			int rank_size, 
			BufferedImage image,
			String file_name) {		
		
		int width = image.getWidth();
		
		int height = image.getHeight();
		
		int n = width * height;
				
		BufferedImage temp = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
		
		/*Black painting*/
		int black=0x000000;
		for (int i = 0; i < n; i++) {
			int x = i % width;
			int y = i / width;
			temp.setRGB(x, y, black);
		}
		
		/*Copy regions*/
		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;
			for (int xx = x; xx < (w+x); xx++) {
				for (int yy = y; yy < (h+y); yy++) {
					temp.setRGB(xx, yy, image.getRGB(xx, yy));
				}
			}
		}
		
		try {
			ImageIO.write(temp, "png", new File(file_name));
		}
		catch (Exception e) { 
				System.err.printf("cannot write image\n");
		}
	}
	
	/*This function ...*/
	public static void write_svm_weight_as_pgm_image (double[] array, int width, int height, String image_name) {

		int size = width * height;

		/*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 (image_name);
	    
	    /*PGM Headers*/
	    out_stream.printf("P2\n%d %d\n %d\n", width, height, 255);
	    
	    /*Writing data*/
	    parser.Write_Double_to_int_Print_Stream (out_stream, array);
	    
	    parser.Close_Print_Stream(out_stream, image_name);
	    
	    /*End-Normalization*/
	}
}
