package main;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;

import association.HungarianAlgorithm;
import association.Probabilities;
import parameters.detection_arguments;
import parameters.io_arguments;
import preprocessing.Parser;
import preprocessing.Util;
import tracking.Atributes;
import tracking.Pairs;
import classification.SVM;
import classification.SVM_DATA;

public class Strategy1 { 
	
	public static void Strategy ( 
		ArrayList<SVM> svm_descriptors, 
		ArrayList<SVM_DATA> svm_classifiers,
		io_arguments io_parameters, 
		detection_arguments detection_parameters
	) 
	throws FileNotFoundException, ClassNotFoundException, IOException  {
		
		
		ArrayList<ArrayList<Atributes>> T = new ArrayList<ArrayList<Atributes>>();
		
		ArrayList<ArrayList<Pairs>> Pi = new ArrayList<ArrayList<Pairs>>();
		
		/*Performing Text Detection at frame 0*/
		BufferedImage frame0 = Get_Frame (io_parameters.get_bframe(), io_parameters);
		detection_parameters = new detection_arguments(io_parameters.get_detection_parameters_file());
		T.add(TextDetection.Detection (frame0, svm_descriptors, svm_classifiers, io_parameters, detection_parameters));
	
		for (int iframe = io_parameters.get_bframe()+1; iframe <= io_parameters.get_eframe(); iframe++) {
			
			/*Performing Text Detection at frame i*/
			BufferedImage Vi = Get_Frame (iframe, io_parameters);
			BufferedImage Vj = Get_Frame (iframe-1, io_parameters);
			
			detection_parameters = new detection_arguments(io_parameters.get_detection_parameters_file());
			
			ArrayList<Atributes> D = new ArrayList<Atributes>();
			
			D = TextDetection.Detection (Vi, svm_descriptors, svm_classifiers, io_parameters, detection_parameters);			
			
			System.out.printf("Antes do Merge\n");
			Merge (T, D, D, Vi, Vj, Pi, "./inputs/hog_fingerprint.txt");
			System.out.printf("Depois do Merge\n");
			
		}
		
		Write_Detections (T, Pi, io_parameters);
		
	}
	
	static void Merge (ArrayList<ArrayList<Atributes>> T, 
			ArrayList<Atributes> E, 
			ArrayList<Atributes> D, 
			BufferedImage Vi, 
			BufferedImage Vj,
			ArrayList<ArrayList<Pairs>> Pi,
			String hog_fingerprint_parameters_filename) {

		ArrayList<Atributes> Ti = new ArrayList<Atributes>();

		ArrayList<Pairs> Pi_i_j = new ArrayList<Pairs>();

		boolean transpose = false; 

		double sigma_s = 1.0;
		double sigma_p = 16.0;
		double sigma_a = 20.0;

		double assignment_threshold = 0.05;

		Parser parser = new Parser();	

		Probabilities Pr = new Probabilities(); 

		ArrayList<String> hog_fingerprint_parameters = parser.Get_Array_List (hog_fingerprint_parameters_filename);

		System.out.printf("Aqui0\n");
		if (E.size() != 0) {

			/*Computing the likelihood probability matrix between sets {Ti} and {Tj}*/
			if (D.size() != 0) {

				double[][] P = new double[E.size()][D.size()];

				for (int j = 0; j < E.size(); j++) {

					Atributes Re = E.get (j);

					for (int k = 0; k < D.size(); k++) {

						Atributes Rd = D.get (k);

						P[j][k] = Pr.prob_s (Re, Rd, sigma_s) * Pr.prob_p (Re, Rd, sigma_p) * Pr.prob_a (Vj, Vi, sigma_a, hog_fingerprint_parameters);
					}
				}

				/*Using Hungarian algorithm, to create the list with the best assignments*/
				HungarianAlgorithm HA = new HungarianAlgorithm();

				if (P.length > P[0].length) {
					P = HA.transpose(P);
					transpose = true;
				}

				int[][] A = new int[P.length][2];

				/*Getting the list of assignments*/
				A = HA.hgAlgorithm (P, "max"); 

				/*-------------------------------------------*/
				/*Connect those elements in {E} that have an association in {D}*/
				for (int i = 0; i < A.length; i++) {
					if (P[A[i][0]][A[i][1]] > assignment_threshold) {
						if (transpose) {
							Atributes Re = E.get(A[i][1]);
							Atributes Rd = D.get(A[i][0]);
							Pairs p_i_j = new Pairs();
							p_i_j.i = Re;
							p_i_j.j = Rd;
							Pi_i_j.add(p_i_j);
							Ti.add(Rd); /*Adicionar Re, ou média de Rd e Re?*/
						}
						else {
							Atributes Re = E.get(A[i][0]);
							Atributes Rd = D.get(A[i][1]);
							Pairs p_i_j = new Pairs();
							p_i_j.i = Re;
							p_i_j.j = Rd;
							Pi_i_j.add(p_i_j);
							Ti.add(Rd); /*Adicionar Re, ou média de Rd e Re?*/
						}
					}
				}
			} 
		}
		System.out.printf("Fim do Aqui0\n");

		System.out.printf("Aqui1\n");
		/*For those elements in {D} that do not have an association in {E} insert them*/
		for (int i = 0; i < D.size(); i++) {
			Atributes Rd = D.get (i);
			if (!Belong(Rd, Pi_i_j)) {
				Ti.add(Rd);				
			}
		}
		System.out.printf("Fim do Aqui1\n");
		T.add(Ti);
		Pi.add(Pi_i_j);
	}

	
	static boolean Belong (Atributes R, ArrayList<Pairs> Pi_i_j) {
		
		for (int i = 0; i < Pi_i_j.size(); i++) {
			Atributes tmp = Pi_i_j.get(i).j;
			if (tmp == R) {
				return true;
			}
		}	
		return false;
	}
	
	
	static void Write_Detections (ArrayList<ArrayList<Atributes>> T, ArrayList<ArrayList<Pairs>> Pi, io_arguments io_parameters) {
		
		
		for (int iframe = io_parameters.get_bframe(); iframe <= io_parameters.get_eframe(); iframe++) {

			/*Creating sub-directories (for each frame) to store the detection and tracking results*/
			File directory = new File (io_parameters.get_out_path() + String.format("%06d", iframe));
			directory.mkdir();
			
			/*Writing detection results*/
			BufferedImage Vi = Get_Frame (iframe, io_parameters);

			if (iframe == io_parameters.get_bframe()) {
				write_set_regions (Vi, T.get(iframe), null, io_parameters, false, iframe);
			}
			else {
				write_set_regions (Vi, T.get(iframe), Pi.get(iframe-1), io_parameters, false, iframe);
			}
		}
	}
		
	
	static BufferedImage Get_Frame (int iframe, io_arguments io_parameters) {
		/*Getting the image frame name*/
		String frame_name = io_parameters.get_frames_path() + String.format("%06d", iframe) + ".png";

		/*Reading the image frame*/
		try {
			BufferedImage frame = ImageIO.read(new File(frame_name));
			return frame;
		}
		catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
			return null;
		}
	}
	
	static void write_set_regions (BufferedImage frame, ArrayList<Atributes> set, ArrayList<Pairs> Pi_i_j, io_arguments io_parameters, boolean option, int iframe) {

		BufferedImage image = Util.imageScale (frame, 1.0);
		
		Graphics2D g = image.createGraphics();	
		for (int box = 0; box < set.size(); box++) {			
		   	g.setStroke(new BasicStroke(2.0f));	    	
		    			
			Atributes A = set.get (box);		
			
			if ( (Pi_i_j != null) && !Belong(A, Pi_i_j)) {
				g.setColor(Color.yellow); 
			}
			else  {
				g.setColor(Color.red);
			}
			
			int x = (int)(A.get_x()); 
		    int y = (int)(A.get_y());
		    int w = A.get_width(); 
		    int h = A.get_height();		    
		    g.drawRect(x, y, w, h);	    
		}
		g.dispose();
		try {
			ImageIO.write(image, "png", new File(io_parameters.get_out_path() + String.format("%06d", iframe) + "/detection.png"));
		}
		catch (Exception e) {
			System.err.printf("cannot write image %s\n", io_parameters.get_out_path() + String.format("%06d", iframe) + "/detection.png");
		} 
	}
}
