package main;

import hypothesis_validation.HoG_Score;

import java.awt.image.BufferedImage; 
import java.io.File;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import parameters.detection_arguments;
import parameters.io_arguments;
import preprocessing.BicubicInterpolation;
import preprocessing.HistogramCorrection;
import preprocessing.Parser;
import preprocessing.Util;
import regrouping.Box;
import regrouping.Groupment;
import segmentation.Regions;
import tracking.Atributes;
import xml.Icdar;
import classification.SVM;
import classification.SVM_DATA;

public class TextDetection {
	
	/*Multiresolution Text Detection*/
	public ArrayList<Atributes> Detection ( 
			BufferedImage image, 
			ArrayList<SVM> svm_descriptors,
			ArrayList<SVM_DATA> svm_classifiers,
			io_arguments io_parameters,
			detection_arguments detection_parameters)  { 
		
		long start = System.currentTimeMillis();
		
		/*List of all recognized chain of characters (words) at all scales*/
		LinkedList<regrouping.Box> list = new LinkedList<regrouping.Box>();
		
		/*List of all recognized characters at all scales*/
		ArrayList<Regions> Characters_List = new ArrayList<Regions>(); 
		
		int minimum_surface = detection_parameters.Get_Minimum_Surface();

		int maximum_surface = detection_parameters.Get_Maximum_Surface();
		
		Groupment grouping = new Groupment();
		
		/*Constructing the image pyramid for all levels*/
		BufferedImage[] image_pyramid = Util.Build_Pyramid (image, detection_parameters.pyramid_levels);

		/*For all pyramid levels beginning with the image with best resolution (level zero) do ...*/
		for (int level = 0; level < detection_parameters.pyramid_levels; level++) {
	
			/*Due to the multiresolution, each resolution level is dedicated to detect a given range of text regions*/
			Refining_Intervals_and_Rules (minimum_surface, maximum_surface, level, detection_parameters);
			
			/*Converting the input (color) image to grey*/
			BufferedImage grey_image = Util.colorToGreyImage(image_pyramid[level]);
			
			/*Stretching the image values to [0-255]*/
			HistogramCorrection.Image_Stretch (grey_image);

			/*Doubling the size of the image to improve the detections*/
			//BufferedImage big_image = BicubicInterpolation.bicubic_interpolation(grey_image);
			BufferedImage big_image = Util.imageScale (grey_image, 2.0);

			/*Getting the complement of the image values [255 -> 0, 0 -> 255]*/
			BufferedImage neg_image = HistogramCorrection.Image_Inversion (big_image);
			
			/*Calling threads to perform the segmentation, classification and regrouping*/
			DetectionThread thread1 = new DetectionThread(big_image, false, level, svm_descriptors, svm_classifiers, detection_parameters);
			thread1.start();
			
			DetectionThread thread2 = new DetectionThread(neg_image, true, level, svm_descriptors, svm_classifiers, detection_parameters);
			thread2.start();

			/*Waiting that both threads finish*/
			while(  !( thread1.Has_Finished() && thread2.Has_Finished() )  ) {
				Thread.yield();
			}
			
			/*Getting the list with the characters chain (words) recognized at that scale by the both threads*/
			LinkedList<regrouping.Box> temp_list1 = thread1.Get_Text_List();

			LinkedList<regrouping.Box> temp_list2 = thread2.Get_Text_List();

			/*Updating the coordinates of each character chain (word) to the original level in order to the merging process*/
			grouping.Scale_Update (temp_list1, level);

			grouping.Scale_Update (temp_list2, level);

			/*Merging the results of {temp_list1} and {temp_list2} (found by the two threads) in {list}. 
			 *{list} contains all character chains (words) recognized at all scales of the pyramid.*/
			grouping.Lists_Fusion (list, temp_list1, level, detection_parameters);
			
			grouping.Lists_Fusion (list, temp_list2, level, detection_parameters);
						
			/*Merging all the characters regions recognized by both threads in the {Characters_List}*/
			Characters_List = thread1.Merge_Letter_List (0, Characters_List, level);
			
			Characters_List = thread2.Merge_Letter_List (0, Characters_List, level);
			
			if (detection_parameters.write_image) {
				Regions.show_list (Characters_List, image, "recognized_letters_"+level+".png");
			}
		} /*Pyramid-End*/ 
		
		
		//if (detection_parameters.write_image) {
		/*	Regions.show_list (Characters_List, image, "recognized_lettersA.png");
			LinkedList<regrouping.Box> NCharacters_List = grouping.Grouping_Regions (Characters_List, detection_parameters);
			Util.write_boxes (image, NCharacters_List, io_parameters, io_parameters.information + ".cocozildo", io_parameters.get_option(), -1);*/
		//}
		
		/*Writing all informations about all the detected characters in all pyramid levels*/
		Debugging_and_Writing_Characters (image, Characters_List, io_parameters, detection_parameters); 
			
		//BufferedImage temp_image2 = Util.imageScale (image, 1.0);
		//Util.write_boxes (temp_image2, list, io_parameters, io_parameters.information + ".cocozildo", io_parameters.get_option(), -1);
		
		
		/*Merging to character chains that have some intersection*/
		grouping.Auto_Fusion (list, detection_parameters);
			
		/*if (io_parameters.get_option().equals("l")) {
			BufferedImage temp_image = Util.imageScale (image, 1.0);
			Icdar.toXML (io_parameters.information, io_parameters.get_out_path() + io_parameters.information + ".nohog.xml", image.getWidth(), image.getHeight(), list, detection_parameters); 
			Util.write_boxes (temp_image, list, io_parameters, io_parameters.information + ".nohog", io_parameters.get_option(), -1);
		}*/
			
		/*Discarding character chains (image regions) that may be false positives*/
		ArrayList<Atributes> set_D = HoG_Score.HoG_Classification (image, list, detection_parameters, io_parameters.get_xml_header());
		
		Util.Compute_Processing_Time (start, "Text Detection");
		
		return set_D;
	} /*Detection-End*/
	
	/*As the detection algorithm is applied in a multiresolution fashion. Each resolution level is dedicated
	 *to detect a given range of text regions scales. At coarser levels we aim at detecting large text areas,
	 *and ignoring texture details (high frequencies). At finer scales, our goal is to detect smaller regions,
	 *analysing more accurately the local image content. See the ICIP paper: SnooperText (page 2).*/
	static void Refining_Intervals_and_Rules ( 
			int minimum_surface, 
			int maximum_surface, 
			int pyramid_level, 
			detection_arguments detection_parameters ) {
		
		int number_of_pyramid_levels = detection_parameters.pyramid_levels;

		/*Defining the overlap between two consecutive scales*/
		int overlap = (int)(maximum_surface * 1.0);

		/*Defining the interval size that we aim to detect in each scale given that we have {number_of_pyramid_levels}*/
		double delta_l = ( (maximum_surface - minimum_surface) + overlap * (number_of_pyramid_levels - 1) )/(double)(number_of_pyramid_levels);

		/*As delta and overlap are constant over scales, we have: (eq. 1)*/
		double delta = delta_l/Math.pow(4,pyramid_level);

		/*The new region scale interval can be computed as: (eq. 2)*/
		double new_surface_interval = (minimum_surface + pyramid_level * (delta_l - overlap))/Math.pow(4,pyramid_level);

		/*Setting the new surface intervals (minimum and maximum) according to the scale*/
		detection_parameters.Set_Minimum_Surface ((int)(new_surface_interval));

		detection_parameters.Set_Maximum_Surface ((int)(new_surface_interval + delta));

		/*Changing the rules to have a chain of characters (instead of 3 characters 2 are sufficient as the images becomes smaller)*/
		
		/*My tests to Journal*/
		if ((pyramid_level == number_of_pyramid_levels) || (pyramid_level == (number_of_pyramid_levels-1))) {
			detection_parameters.Set_Minimum_Grouping_Set(2);
		}	
		
		
		//TIRAR
		/*if (pyramid_level == (number_of_pyramid_levels-2)) {
			detection_parameters.Set_Minimum_Grouping_Set(4);
			detection_parameters.Set_S3(0.6); //TIRAR
		}*/
		/*if (pyramid_level == (number_of_pyramid_levels-1)) {
			detection_parameters.Set_Minimum_Grouping_Set(1);
			detection_parameters.Set_S3(0.5);//TIRAR
		}*/
		
		/*Changing the rules to join two chains of characters (relaxing the conditions as the images becomes smaller). */
		if (detection_parameters.dataset.compareTo("itowns") != 0) {
			if (pyramid_level == 0) {
				detection_parameters.Set_S3(0.25);
			}
			else if (pyramid_level == 1) {
				detection_parameters.Set_S3(0.5);
			}
			else {
				detection_parameters.Set_S3(1.5);
			}
		}
	} /*Set_Image_Intervals-End*/
	
	/*Writing and Debugging all characters found in every scale of the image pyramid*/
	static void Debugging_and_Writing_Characters (
			BufferedImage image, 
			ArrayList<Regions> Characters_List, 
			io_arguments io_parameters, 
			detection_arguments detection_parameters ) {
		
		Groupment grouping = new Groupment();
		
		Parser parser = new Parser();
		
		/*Writing the all the characters found in the pyramid*/
		if (detection_parameters.write_image) {
			Regions.show_list (Characters_List, image, "recognized_letters.png");
		}
		
		/*Trying to break two linked characters to improve the recognition*/
		//Regions.redefine_regions (L); 
		
		/*Writing debugging information about all characters found in the pyramid*/
		if (detection_parameters.write_image) {
			
			String file_name = "dump_reg.txt";
			
			PrintStream out = parser.Open_Print_Stream (file_name); 
			
			for (int i = 0; i< Characters_List.size(); i++) {
				Regions box = Characters_List.get(i);
				out.printf("xmin : %d, ymin : %d, xmax : %d. ymax : %d, h : %d, w : %d, cx : %d, cy : %d, dec : %f\n", box.getXmin(), box.getYmin(), box.getXmax(), box.getYmax(), box.getH(), box.getW(), box.getXg(), box.getYg(), box.getDec());
			}
			parser.Close_Print_Stream (out, file_name);
		}
		
		/*Grouping all founded characters in the pyramid in chains (words) according some specific rules (S1, S2, and S3).*/
		LinkedList<regrouping.Box> chains = grouping.Grouping_Regions (Characters_List, detection_parameters);

		/*Writing debugging information about all chains found in the pyramid*/
		if (detection_parameters.write_image) {

			String file_name = "dump_box.txt";

			PrintStream out = parser.Open_Print_Stream (file_name); 

			for (int i = 0; i< chains.size(); i++) {
				Box box = chains.get(i);
				out.printf("xmin : %d, ymin : %d, xmax : %d. ymax : %d, h : %d, w : %d, cx : %d, cy : %d, dec : %f\n", box.getXMin(), box.getYMin(), box.getXMax(), box.getYMax(), box.getH(), box.getW(), box.getCx(), box.getCy(), box.getDec());
			}
			parser.Close_Print_Stream (out, file_name);
		}

		/*Copying the original image*/
		BufferedImage temp_image1 = Util.imageScale (image, 1.0);
		
		//BufferedImage temp_image2 = Util.imageScale (image, 1.0);

		/*Disposing the chains in a copy of the original image*/
		Groupment.Dispose_Small_Regions_Agroupments (temp_image1, chains, detection_parameters);
		
		/*Discarding character chains (image regions) that may be false positives*/
		/*ArrayList<Atributes> set_Temp = HoG_Functions.HoG_Classification (image, chains, hogmean, hogstd, detection_parameters, io_parameters.get_xml_header());
		
		//Writing an image with the boxes found by the detection step
		Util.write_set_regions (temp_image2, set_Temp, io_parameters, io_parameters.information + ".temp", io_parameters.get_option(), -1);
		
		if (io_parameters.get_option().equals("l")) {
			Icdar.toXML (io_parameters.information, io_parameters.get_out_path() + io_parameters.information + ".temp.xml", image.getWidth(), image.getHeight(), set_Temp, detection_parameters);
		}*/
		
		/*Writing an XML file with all the chains founded (chains composed by the characters found in all image scales)*/
		/*if (io_parameters.get_option().equals("d")) {
			Icdar.toXML (io_parameters.get_xml_header(), io_parameters.get_xml_name()+".regions.xml", image.getWidth(), image.getHeight(), chains, detection_parameters); 

			//Writing all the chains founded (chains composed by the characters found in all image scales)
			try {
				ImageIO.write(temp_image1, "png", new File(io_parameters.get_out_image()+".regions.png"));
			}
			catch (Exception e) {
				System.err.println("cannot store image");
			} 
		}*/
		/*else if (io_parameters.get_option().equals("l")) {
			Icdar.toXML (io_parameters.information, io_parameters.get_out_path() + io_parameters.information + ".regions.xml", image.getWidth(), image.getHeight(), chains, detection_parameters); 
			
			//Writing all the chains founded (chains composed by the characters found in all image scales)
			try {
				ImageIO.write(temp_image1, "png", new File(io_parameters.get_out_path() + io_parameters.information + ".regions.png"));
			}
			catch (Exception e) {
				System.err.println("cannot store image");
			} 
		}*/
	} /*Debugging_and_Writing_Characters-End*/
} /*Class-End*/
