package regrouping;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

import parameters.detection_arguments;
import segmentation.Labels;
import segmentation.Regions;


public class Groupment {
		
	public LinkedList<Box> Grouping_Regions (Labels image_segmented, Regions[] regions, detection_arguments detection_parameters)
	{			
		int number_of_regions = image_segmented.getNumberOfRegions();
		
		LinkedList<Box> list1 = new LinkedList<Box>();
		LinkedList<Box> list2 = new LinkedList<Box>();

		for(int i = 1; i < number_of_regions;i++) {
			
			Box box = new Box (image_segmented, regions[i], i);
			if (regions[i].getDec() < 0) {
				list1.addFirst(box);
			}
			else {
				list2.addFirst(box);
			}
		}
		
		Grouping_Step (list1, list2, detection_parameters);
		Grouping_Spaced (list1, detection_parameters, 1.1);
				
		return list1;
	}
	

	
	public LinkedList<Box> Grouping_Regions (ArrayList<Regions> regions, detection_arguments detection_parameters)
	{
			
		LinkedList<Box> list1 = new LinkedList<Box>();
		LinkedList<Box> list2 = new LinkedList<Box>();

		for(int i = 0; i < regions.size(); i++) {
			
			Box box = new Box (regions.get(i), i);
			if (regions.get(i).getDec() < 0) {
				list1.addFirst(box);
			}
			else {
				list2.addFirst(box);
			}
		}
		
		Grouping_Step (list1, list2, detection_parameters);
		Grouping_Spaced (list1, detection_parameters, 1.1);
		
		return list1;
	}
	
	void Grouping_Spaced (LinkedList<Box> list1, detection_arguments detection_parameters, double value) {

		boolean fusion;

		int previous;

		do {
			fusion = false;

			for (int i = 0; i < list1.size(); i++) {
				
				Box boxi = list1.get(i);
				
				if (boxi.getDec() >= 0) {
					continue;
				}
				previous = i;
				
				for (int j = i + 1; j < list1.size(); j++) {
					
					Box boxj = list1.get(j);
					
					if (boxj.getDec() >= 0) {
						previous = j;
					}
					else if ( boxi.Compatible(boxj, detection_parameters, value) && 
							  (!boxi.getUnited()) &&
							  (!boxj.getUnited()) ) {
						boxi.Fusion(boxj);
						list1.remove(j);
						fusion = true;
						j = previous;
					} else {
						previous = j;
					}
				}
			}
		} while(fusion);   
	}
	
	void Grouping_Step (LinkedList<Box> list1, LinkedList<Box> list2, detection_arguments detection_parameters) {

		boolean fusion;

		int previous;

		do {
			fusion = false;

			for (int i = 0; i < list1.size(); i++) {
				
				Box boxi = list1.get(i);
				
				if (boxi.getDec() >= 0) {
					continue;
				}
				previous = i;
				
				for (int j = i + 1; j < list1.size(); j++) {
					
					Box boxj = list1.get(j);
					
					if (boxj.getDec() >= 0) {
						previous = j;
					}
					else if (boxi.Compatible(boxj, detection_parameters)) {
						boxi.Fusion(boxj);
						list1.remove(j);
						fusion = true;
						j = previous;
						boxi.setUnited(true);
					} else {
						previous = j;
					}
				}
			}
		} while(fusion);   


		do {
			
			fusion = false;
			
			for (int i = 0; i < list1.size(); i++) {
				
				Box boxi = list1.get(i);
				
				if ( (boxi.getDec() >= 0) || (boxi.getCount() < detection_parameters.Get_Minimum_Grouping_Set()) ) {
					continue;
				}

				previous = list2.size()+1;	
				
				for (int j = 0; j < list2.size(); j++) {

					Box boxj = list2.get(j);

					if ( (boxi.Compatible(boxj, detection_parameters)) && 
							boxj.getH() < (boxi.getH()+0.2*boxi.getH())   && 
							boxj.getH() > (boxi.getH()-0.2*boxi.getH())   && 
							Math.abs(boxj.getYMin() - boxi.getYMin()) < 0.2 * boxi.getH() && 
							Math.abs(boxj.getYMax() - boxi.getYMax()) < 0.2 * boxi.getH() && 
							(boxj.getMoyenneEpaisMax()/boxj.getCount())<2*(boxi.getMoyenneEpaisMax()/boxi.getCount()) && 
							(boxj.getMoyenneEpaisMax()/boxj.getCount())*2>(boxi.getMoyenneEpaisMax()/boxi.getCount()) ) { 


						boxi.Fusion(boxj);
						list2.remove(j);
						fusion = true;
						j = previous;

					} 
					else {
						previous=j;
					}
				}

				previous = i;
				for (int j = i + 1; j < list1.size(); j++) {

					Box boxj = list1.get(j);

					if (boxj.getDec() >= 0) {
						previous = j;
					}
					else if (boxi.Compatible(boxj, detection_parameters)) {
						boxi.Fusion(boxj);
						list1.remove(j);
						fusion = true;
						j = previous;
					} else {
						previous = j;
					}

				}	
			}
		} while(fusion); 

	}
	
	
	public void Scale_Update (LinkedList<Box> list, int level)
	{
		/*for (int i = 0; i < list.size(); i++) {
			Box box = list.get(i);
			box.setXMin((int)(box.getXMin()*Math.pow(2,level)));
			box.setYMin((int)(box.getYMin()*Math.pow(2,level)));
			box.setXMax((int)(box.getXMax()*Math.pow(2,level)));
			box.setYMax((int)(box.getYMax()*Math.pow(2,level)));
			box.setW((int)(box.getW()*Math.pow(2,level)));
			box.setH((int)(box.getH()*Math.pow(2,level)));
		}*/
		for (int i = 0; i < list.size(); i++) {
			Box box = list.get(i);
			box.setXMin((int)(box.getXMin()*Math.pow(Math.sqrt(2.0),level)));
			box.setYMin((int)(box.getYMin()*Math.pow(Math.sqrt(2.0),level)));
			box.setXMax((int)(box.getXMax()*Math.pow(Math.sqrt(2.0),level)));
			box.setYMax((int)(box.getYMax()*Math.pow(Math.sqrt(2.0),level)));
			box.setW((int)(box.getW()*Math.pow(Math.sqrt(2.0),level)));
			box.setH((int)(box.getH()*Math.pow(Math.sqrt(2.0),level)));
		}
	}
	
	public void Auto_Fusion (LinkedList<Box> list, detection_arguments detection_parameters)
	{
		boolean fusion;

		do {
			
			fusion = false;
			
			for (int i = 0; i < list.size(); i++) {
				
				Box boxi = list.get(i);
				
				if ( (boxi.getDec() >= 0) || (boxi.getCount() < detection_parameters.Get_Minimum_Grouping_Set()) ) {
					continue;
				}
			
				int previous = i;
			
				for (int j = i+1; j < list.size(); j++) {
				
					Box boxj = list.get(j);
					
					if (boxj.getDec() >= 0 || boxj.getCount() < detection_parameters.Get_Minimum_Grouping_Set()) {
						previous = j;
					}
					else if (boxi.EncompassB(boxj)) {
						//System.err.printf("this.x_min : %d, this.y_min : %d, this.x_max : %d, this.y_max : %d\n", boxi.getXMin()/2, boxi.getYMin()/2, boxi.getXMax()/2, boxi.getYMax()/2);
						//System.err.printf("getXMin() : %d, getYmin() : %d, getXMax() : %d, getYMax() : %d\n", boxj.getXMin()/2, boxj.getYMin()/2, boxj.getXMax()/2, boxj.getYMax()/2);
						
						boxi.Fusion(boxj);
						//System.err.printf("Novas coordenadas :%d %d %d %d\n", boxi.getXMin()/2, boxi.getYMin()/2, boxi.getXMax()/2, boxi.getYMax()/2);
						
						list.remove(j);
						j = previous;
						fusion = true;
					}
					else {
						previous = j;
					}
				}
			}
		} while (fusion); 

	}
	
	
	public void Lists_Fusion (LinkedList<Box> list1, LinkedList<Box> list2, int pyr_level, detection_arguments detection_parameters)
	{
		boolean fusion;
		
		do {
			
			fusion = false;
			
			for (int i = 0; i < list1.size(); i++) {
				
				Box boxi = list1.get(i);
				
				if (boxi.getDec() >= 0 || boxi.getCount() < detection_parameters.Get_Minimum_Grouping_Set()) {
					continue;
				}
				
				int previous = -1;
				
				for (int j = 0; j < list2.size(); j++) {
					
					Box boxj = list2.get(j);
					
					if (boxj.getDec() >= 0 || boxj.getCount() < detection_parameters.Get_Minimum_Grouping_Set()) {
						previous = j;
					}
					else if (boxi.Encompass(boxj)) {
						int tmp = j + 1;
						boxi.Fusion(boxj);
						list2.remove(j);
						fusion = true;
						
						if (previous != -1) {
							j = previous + 1;
						}
						else {
							j = tmp;
						}
					}
					else {
						previous = j;
					}
				}
			}

		} while(fusion);
		
		for(Iterator<Box> i = list2.iterator(); i.hasNext(); )
		{
			Box boxi = i.next();
			
			if(boxi.getDec() >= 0 || boxi.getCount() < detection_parameters.Get_Minimum_Grouping_Set()) {
				continue;
			}
			
			boxi.setPyramidLevel(pyr_level);
			list1.add(0, boxi);
			i.remove();
		}
	}
	   
	
	public static void Dispose_Small_Regions_Agroupments (BufferedImage image, LinkedList<Box> list, detection_arguments detection_parameters)
	{

		Graphics2D g = image.createGraphics();

		g.setStroke(new BasicStroke(2.0f));

		for (int i = 0; i < list.size(); i++) {

			Box box = list.get(i);

			if ( (box.getDec() > 0) || box.getCount() < detection_parameters.Get_Minimum_Grouping_Set()) {
				continue;
			}

			int x_min = (int)(box.getXMin()/2) - 1; int y_min = (int)(box.getYMin()/2) - 1;
			int x_max = (int)(box.getXMax()/2) + 1; int y_max = (int)(box.getYMax()/2) + 1;
			int width = x_max - x_min; int height = y_max - y_min;
			
			if (box.getValid() <= 0) {
				if (box.getPyramidLevel() == -1)
					g.setColor(Color.pink);
				else if (box.getPyramidLevel() == 0)
					g.setColor(Color.yellow);
				else if (box.getPyramidLevel() == 1)
					g.setColor(Color.green);
				else if (box.getPyramidLevel() == 2)
					g.setColor(Color.white);
				else 
					g.setColor(Color.gray);

				if (box.getValid() == -1000.0)
					g.setColor(Color.black);
			}
			else {
				g.setColor(Color.red);
			}
			g.drawRect(x_min, y_min, width, height);
		}
		g.dispose();
	}
	    
    
    public void Dispose_Region_And_Barycenters (BufferedImage image, Regions[] regions, int number_of_regions)
    {
    	Graphics2D g = image.createGraphics();
		
    	g.setStroke(new BasicStroke(4.0f));
    	
	    g.setColor(Color.yellow);
    	
    	for(int r = 1; r < number_of_regions; r++) {
    	
    		Regions region = regions[r];
    		
    		if (region.getDec() >= 0) continue;
    		
		    int lx1, ly1, lx2, ly2;
		    
		    /*Drawing the horizontal line of the character barycenter*/
		    lx1 = region.getXg() - 2; ly1 = region.getYg(); lx2 = region.getXg() + 2; ly2 = region.getYg();
		    g.drawLine(lx1, ly1, lx2, ly2);
		    
		    /*Drawing the vertical line of the character barycenter*/
		    lx1 = region.getXg(); ly1 = region.getYg()-2; lx2 = region.getXg(); ly2 = region.getYg()+2;
		    g.drawLine(lx1, ly1, lx2, ly2);
		    
		    /*Drawing a rectangle involving the character*/
			int x_min = region.getXmin()-1; int y_min = region.getYmin()-1;
		    int x_max = region.getXmax()+2; int y_max = region.getYmax()+2;
        	int width = x_max - x_min; int height = y_max - y_min;
	    	g.drawRect(x_min, y_min, width, height);
       		
    	}
    	g.dispose();
    }
    
    /*public void Dispose_Region_And_Barycenters2 (BufferedImage image, Regions[] regions, int number_of_regions)
    {
    	Graphics2D g1 = image.createGraphics();
	   	g1.setStroke(new BasicStroke(3.0f));
        g1.setColor(Color.BLUE);
        
        Graphics2D g2 = image.createGraphics();
	   	g2.setStroke(new BasicStroke(1.0f));
        g2.setColor(Color.yellow);
    	
    	for(int r = 1; r < number_of_regions; r++) {
    	
    		Regions region = regions[r];
    		
    		if (region.getDec() >= 0) continue;
    		
		    int lx1, ly1, lx2, ly2;
		    
		    lx1 = region.getXg() - 2; ly1 = region.getYg(); lx2 = region.getXg() + 2; ly2 = region.getYg();
		    g1.drawLine(lx1, ly1, lx2, ly2);
		    
		    lx1 = region.getXg(); ly1 = region.getYg()-2; lx2 = region.getXg(); ly2 = region.getYg()+2;
		    g1.drawLine(lx1, ly1, lx2, ly2);
		    
			int x_min = region.getXmin(); int y_min = region.getYmin();
		    int x_max = region.getXmax(); int y_max = region.getYmax();
        	int width = x_max - x_min; int height = y_max - y_min;
	    	g1.drawRect(x_min-1, y_min-1, width+1, height+1);
	    	g2.drawRect(x_min-3, y_min-3, width+3, height+3);
       		
    	}
    	g1.dispose();
    	g2.dispose();
    }*/
    
    
    public void Dispose_Big_Regions_Agroupments (BufferedImage image, LinkedList<Box> list, detection_arguments detection_parameters)
    {
    	Graphics2D g = image.createGraphics();
		
    	g.setStroke(new BasicStroke(2.0f));
    	
	    g.setColor(Color.BLUE);
    	
    	for (int i = 0; i < list.size(); i++) {
    		
			Box box = list.get(i);
        	
			if ( (box.getDec() > 0) || box.getCount() < detection_parameters.Get_Minimum_Grouping_Set()) {
        		continue;
        	}
        	
			/*Drawing a rectangle involving the character*/
			int x_min = box.getXMin(); int y_min = box.getYMin();
		    int x_max = box.getXMax(); int y_max = box.getYMax();
        	int width = x_max - x_min; int height = y_max - y_min;
	    	g.drawRect(x_min, y_min, width, height);
  	
    	}
    	g.dispose();
    }
}
