package segmentation;

import parameters.detection_arguments;

public class Segmentation {
	
	final static int BACKGROUND = 0;
	final static int LOW_VALUE = 125;
	final static int HIGH_VALUE = 255;
	final static int UNKNOWN_VALUE = 175;

	public void Min_Max ( 
			int w, 
			int h, 
			int[] image, 
			int[] min_Y, 
			int[] max_Y, 
			int mask_size ) {
		
		int size = w * h;
		
		int[] min_X = new int[size];
		
		int[] max_X = new int[size]; 
   
   	    int b_min = 0;
   	    int b_max = 2 * mask_size;
   	    for(int i = mask_size; i < size - mask_size; i++, b_min++, b_max++) {
           
   	       int sup = image[b_min];
           int inf = image[b_min];
        
           for(int j = b_min + 1; j <= b_max; j++) {
              if (sup < image[j]) {
            	 sup = image[j];
              }
              else if (inf > image[j]) {
            	 inf = image[j];
              }
           }
           
           min_X[i] = inf;
           max_X[i] = sup;
   	    }   
   	   
   	    int ori_min = mask_size;
   	    b_min = mask_size;
   	    int ori_max = mask_size + 2 * mask_size * w;
   	    b_max = mask_size + 2 * mask_size * w;
   	    
   	    for(int i = mask_size + mask_size * w; i < w + mask_size * w - mask_size; i++) {
         
            for(int j = i; j < size - (mask_size * w); j += w) {
            	
                int sup = max_X[b_min];
                int inf = min_X[b_min];
                
                for(int k = b_min + w; k <= b_max; k += w) {
                	
                    if ( sup < max_X[k] ) {
                   	    sup = max_X[k];
                    }
                    if ( inf > min_X[k] ) { 
                 	    inf = min_X[k]; 
                    }
                }    
                min_Y[j] = inf;
                max_Y[j] = sup;
                b_min += w;
                b_max += w;
            }
            b_min = ++ori_min;
            b_max = ++ori_max;
   	    }
    }
		
	
	public void Double_Toggle_Mapping (
			int w, 
			int h, 
			int[] image, 
			int[] min, 
			int[] max, 
			int[] h1, 
			int[] h2,
			detection_arguments detection_parameters ){
	    
		int mask_size = detection_parameters.mask_size;
		
		int contrast1 = detection_parameters.contrast1;
			
		int contrast2 = detection_parameters.contrast2;	
		
		int percentage = detection_parameters.percentage;
		
        int offset = mask_size * w + mask_size;
           
        for(int y = mask_size; y < h - mask_size; y++) {
            
        	for(int x = mask_size; x < (w - mask_size); x++, offset++) {
                
            	if ( (max[offset] - min[offset]) < contrast1 ) {
                  	h1[offset] = h2[offset] = UNKNOWN_VALUE;
                }
                else if ( (max[offset] - min[offset]) < contrast2) {
                    
                	h1[offset] = UNKNOWN_VALUE;
                    
                    if ( (max[offset] - image[offset]) < percentage * (image[offset] - min[offset])/100 ) {
                    	h2[offset] = HIGH_VALUE;
                    }
                    else {
                    	h2[offset] = LOW_VALUE;
                    }
                } 
                else {
                    if ( (max[offset] - image[offset]) < percentage * (image[offset] - min[offset])/100 ) {
                    	h1[offset] = h2[offset] = HIGH_VALUE;
                    }    
                    else {
                    	h1[offset] = h2[offset] = LOW_VALUE;
                    }
                }
            }
        	offset += 2 * mask_size;
	    }
   	}
	
	public void Fast_Histeresis (
			int w, 
			int h, 
			int[] image_A,
			int[] image_B,
			int level) {
		
   	    int size = w * h;
		
        Labels labels_A = new Labels (w, h);
        
        Labels labels_B = new Labels (w, h);
        
        int num_of_labels_A = labels_A.create_labels_from_image (w, h, image_A);
        
        int num_of_labels_B = labels_B.create_labels_from_image (w, h, image_B);
        
        int[] values = new int [num_of_labels_B];
        
        labels_A.setNumberOfRegions(num_of_labels_A);
        
        Regions[] reg_img1 = Analyze_Regions (w, h, image_A, labels_A, level);
       
        for(int i = 0; i < num_of_labels_B; i++) {
           values[i] = UNKNOWN_VALUE;
        }     
      
        for (int i = 0; i < size; i++) {
            if ( (image_A[i] == HIGH_VALUE) || (image_A[i] == LOW_VALUE) ) {
                if ( reg_img1[labels_A.getValue(i)].getSurface() > 60 ) {
                	values[labels_B.getValue(i)] = image_A[i];
                }
            }
        }
        for(int i = 0; i < size; i++) {
            if (image_A[i] == UNKNOWN_VALUE) {
            	image_A[i] = values[labels_B.getValue(i)];
            }
        }
	}
		
	/*This function */
	public Regions[] Analyze_Regions (int w, int h, int[] image, Labels labels, int level) {
		
		int num_of_labels = labels.getNumberOfRegions();
		
		System.err.printf("Number of labels : %d\n", num_of_labels);
		
		Regions[] regions = new Regions [num_of_labels];
		
		for (int i = 0; i < num_of_labels; i++) {
			
			regions[i] = new Regions ();
			
			regions[i].setLevel(level);
			
			if (i > 0) {
				regions[i].setXmin(Integer.MAX_VALUE);
				regions[i].setYmin(Integer.MAX_VALUE);
			}
		}
		
		int a = 0, b = 0, c = 0, d = 0;

		for(int y = 1; y < h - 1; y++) {
			
			for(int x = 1; x < w - 1; x++) {
				
				int offset = x + y * w;

				if ( image[offset] == LOW_VALUE || image[offset] == HIGH_VALUE ) {

					regions[labels.getValue(offset)].add_point_to_region (x, y);
					a++;
				} 
				else if ( image[offset] == UNKNOWN_VALUE) {

					regions[labels.getValue(offset)].add_point_to_region (x, y);
					
					Sum_Neighbours (w, offset, image, LOW_VALUE, labels, regions);
					
					Sum_Neighbours (w, offset, image, HIGH_VALUE, labels, regions);
					b++;
				}
				else if (image[offset] == BACKGROUND) {
					c++;
				}
				d++;
			}
		}

		System.err.printf("A : %d, B : %d, C : %d, D : %d\n", a, b, c, d);

		for (int i = 1; i < num_of_labels; i++) {

			int surface = regions[i].getSurface();

			System.err.printf("label : %d, surface : %d\n", i, surface);
			
			if (surface == 0) {
				continue;
			}
			
			//System.err.printf("label : %d, surface : %d\n", i, surface);
			
			int xg = regions[i].getXg();
			int yg = regions[i].getYg();
			regions[i].setXg(xg/surface);
			regions[i].setYg(yg/surface);
		}
		
		return regions; 
	}
	

	
	
	/*This function */
	static void Sum_Neighbours (int w, int offset, int[] image, int VALUE, Labels labels, Regions[] regions) {
		
		int label = labels.getValue(offset);
		
		if ( (image[offset - 1] == VALUE) || (image[offset + 1] == VALUE) || 
			 (image[offset - w] == VALUE) || (image[offset + w] == VALUE) ) {
			
			int neighbours = 0;
			
			if (VALUE == LOW_VALUE) {
				neighbours = regions[label].getLowValuedNeighbour();
			}
			else if (VALUE == HIGH_VALUE) {
				neighbours = regions[label].getHighValuedNeighbour();
			}
			
			neighbours += ( (image[offset - 1] % VALUE) + (image[offset + 1] % VALUE) + 
					        (image[offset - w] % VALUE) + (image[offset + w] % VALUE) );
			
			if (VALUE == LOW_VALUE) {
				regions[label].setLowValueNeighbour(neighbours);
			}
			else if (VALUE == HIGH_VALUE) {
				regions[label].setHighValueNeighbour(neighbours);
			}
		}
	}
	

	/*This function */
	public void Size_Check (int w, int h, int[] image, Labels labels, int pyramid_level, detection_arguments detection_parameters)
	{
		Regions[] regions = Analyze_Regions (w, h, image, labels, pyramid_level);
	    
	    int size = w * (h - 1) - 1;
	    
	    for (int i = w + 1; i < size; i++) {
	    	
	    	int label = labels.getValue(i);
      
            //System.err.printf("indice : %d, label : %d, surf: %d, min : %d, max : %d, wr : %d, hr : %d\n", i, label, regions[label].getSurface(), detection_parameters.Get_Minimum_Surface(), detection_parameters.Get_Maximum_Surface(), wr, hr);
            
	        if (regions[label].getSurface() < detection_parameters.Get_Minimum_Surface()){
	        	image[i] = BACKGROUND;
            }
	    	else if (regions[label].getSurface() > detection_parameters.Get_Maximum_Surface()) {
	    		image[i] = BACKGROUND;
	        }
	        else {
	            int wr = regions[label].getXmax() - regions[label].getXmin() + 1;
	            int hr = regions[label].getYmax() - regions[label].getYmin() + 1;
	            if (wr > detection_parameters.maximum_region_width) {
	            	image[i] = BACKGROUND;
	            }
	            else if (wr < detection_parameters.minimum_region_width) {
	            	image[i] = BACKGROUND;
	            }
	            else if (hr < detection_parameters.minimum_region_height) {
	            	image[i] = BACKGROUND;
	            }
	            else if (hr > detection_parameters.maximum_region_height) {
	            	image[i] = BACKGROUND;
	            }
	        }
	    }
	}

	/*This function */
	public void Remove_Unknown_Value (int w, int h, int[] image, Labels labels, int pyramid_level, detection_arguments detection_parameters) {
		
		int size = w * (h - 1) - 1;
		
		Regions[] regions = Analyze_Regions (w, h, image, labels, pyramid_level);
		
		for(int i = w + 1; i < size; i++) {
			if (image[i] == UNKNOWN_VALUE) {
				int value = labels.getValue(i);
				if (regions[value].getSurface() < detection_parameters.homogeneus_maximum_size_region) {
					if (regions[value].getLowValuedNeighbour() > regions[value].getHighValuedNeighbour()) {
						image[i] = LOW_VALUE;                	
					}
					else {
						image[i] = HIGH_VALUE;
					}
				}
			}
		}
	}
} /*Class-End*/
