package classification;

import java.awt.image.BufferedImage;
import java.io.FileReader;
import java.io.LineNumberReader;
import java.util.ArrayList;
import parameters.detection_arguments;
import preprocessing.Parser;
import preprocessing.Util;
import segmentation.Labels;
import segmentation.Regions;
import descriptors.Fourier;
import descriptors.Polar;
import descriptors.Z;
import descriptors.Z2;
import fr.lip6.type.TrainingSample;

public class Features {
	
	private int size;
	
	private int descriptor_size;
	
	private int number_of_regions; 
  
    private double[] mean; /**/
    
    private double[] standard_deviation; /**/

    private double[] data; /**/
    
    /**/
    public int Get_Descriptor_Size () {
    	return this.descriptor_size;
    }
    
    /**/
    public int Get_Number_of_Regions () {
    	return this.number_of_regions;
    }
    
    /**/
    public double Get_Data (int pos) {
    	return data[pos];
    }
    
    /**/
    public void Reads_Scale_Parameters_Model_From_Ascii_File (String file_name)
    {
    	Parser parser = new Parser();

    	try{ 
    		LineNumberReader file = new LineNumberReader(new FileReader(file_name));

    		size = Integer.parseInt(parser.getLineParameter (file, "\n", 0)); 

    		mean = parser.Get_Data (size, file);

    		standard_deviation = parser.Get_Data (size, file);

    		file.close();
    	}
    	catch(Exception e){
    		System.err.println ("Error: file \"" + file_name + "\" can not be loaded. ");
    		e.printStackTrace();
    	}
    }

    public void Combining_All (Classify fourier, Classify zernike, Classify polar, detection_arguments parameters) 
    {	
    	Reads_Scale_Parameters_Model_From_Ascii_File (parameters.all_normalization);

    	descriptor_size = 3;

    	int number_of_regions = fourier.Get_Size();
    	
    	this.number_of_regions = number_of_regions;
    	
    	assert ( (fourier.Get_Size() == polar.Get_Size()) && (polar.Get_Size() == zernike.Get_Size()) );
    	
    	data = new double [number_of_regions*descriptor_size];

    	for(int i = 0, j = 0; i < number_of_regions; i++) {
    		
    		double vfourier = fourier.Get_Score_Value(i);
    		data[j++] = (vfourier - mean[1])/standard_deviation[1];
    		
    		double vzernike = zernike.Get_Score_Value(i);
    		data[j++] = (vzernike - mean[2])/standard_deviation[2];

    		double vpolar = polar.Get_Score_Value(i);
    		data[j++] = (vpolar - mean[3])/standard_deviation[3];
    	}
    }
    
    public ArrayList<TrainingSample<double[]>> Combining_All (double[] fourier, double[] polar, double[] zernike, double[] mean, double[] deviation, boolean normalization, detection_arguments parameters) 
    {	
    	ArrayList<TrainingSample<double[]>> list = new ArrayList<TrainingSample<double[]>>();

    	int valid = 0;

    	for (int i = 0; i < deviation.length; i++) { if (deviation[i] != 0) {valid++;} }

    	assert ( (fourier.length == polar.length) && (polar.length == zernike.length) && (valid == 3));

    	int number_of_regions = fourier.length;

    	for(int i = 0; i < number_of_regions; i++) {

    		double[] descriptor = new double[valid];

    		if (normalization) {
    			descriptor[0] = (fourier[i] - mean[0])/deviation[0];
    			descriptor[1] = (polar[i]   - mean[1])/deviation[1];
    			descriptor[2] = (zernike[i] - mean[2])/deviation[2];
    		}
    		else { 
    			descriptor[0] = fourier[i];
    			descriptor[1] = polar[i];
    			descriptor[2] = zernike[i];
    		}


    		int unknow = 2;
    		list.add(new TrainingSample<double[]>(descriptor, unknow));
    	}
    	return list;
    }
    
    /**/
    private void Composing_Fourier_Vector (int[] v, int begin, int end, boolean variable, Fourier fourier) {

    	for(int i = begin; i < end; i++) {
    		if (standard_deviation[v[1]]!=0) {
    			if (variable) {
    				data[v[0]++] = (fourier.getX(i)-mean[v[1]])/standard_deviation[v[1]];
    			}
    			else {
    				data[v[0]++] = (fourier.getY(i)-mean[v[1]])/standard_deviation[v[1]];
    			}
    		}
    		v[1]++;
    	}
    }
    
    /**/
    public void Extracting_Fourier (Labels labels, Regions[] regions, int descriptor_size, detection_arguments parameters) {
    	
    	int fourier_size = parameters.fourier_size;

    	int number_of_regions = this.number_of_regions = labels.getNumberOfRegions();

    	this.descriptor_size = descriptor_size;

    	Reads_Scale_Parameters_Model_From_Ascii_File (parameters.fourier_normalization);

    	data = new double [number_of_regions*descriptor_size];

    	int[] v = {0, 0};

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

    		Fourier fourier = new Fourier();

    		/**/
    		fourier.fourier_fibd_contour (labels, regions, i);

    		/**/
    		fourier.Discretize (fourier_size);

    		/**/
    		int center = fourier.Compute_TF_Image ();

    		/**/
    		v[1] = 1;
    		Composing_Fourier_Vector (v, 0, center, true, fourier);
    		Composing_Fourier_Vector (v, 0, center, false, fourier);
    		Composing_Fourier_Vector (v, center+2, fourier_size, true, fourier);
    		Composing_Fourier_Vector (v, center+2, fourier_size, false, fourier);
    	}
    }
    
    public void Extracting_Fourier2 (Labels labels, Regions[] regions, int descriptor_size, detection_arguments parameters) {
    	
    	int fourier_size = parameters.fourier_size;

    	int number_of_regions = this.number_of_regions = labels.getNumberOfRegions();

    	this.descriptor_size = descriptor_size;

    	Reads_Scale_Parameters_Model_From_Ascii_File (parameters.fourier_normalization);

    	data = new double [number_of_regions*descriptor_size];

    	int[] v = {0, 0};
    	
    	Fourier fou = new Fourier();
    	
    	int matrix_size = fou.matrix_size (fourier_size); 
    	
    	double[] sin = new double[matrix_size];
    	
    	double[] cos = new double[matrix_size];
    	
    	fou.cos_sin_matrices (sin, cos, fourier_size);

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

    		Fourier fourier = new Fourier();

    		/**/
    		fourier.fourier_fibd_contour (labels, regions, i);

    		/**/
    		fourier.Discretize (fourier_size);

    		/**/
    		int center = fourier.Compute_TF_Image (sin, cos);

    		/**/
    		v[1] = 1;
    		Composing_Fourier_Vector (v, 0, center, true, fourier);
    		Composing_Fourier_Vector (v, 0, center, false, fourier);
    		Composing_Fourier_Vector (v, center+2, fourier_size, true, fourier);
    		Composing_Fourier_Vector (v, center+2, fourier_size, false, fourier);
    	}
    }
        
    /**/
    public ArrayList<TrainingSample<double[]>> Extracting_Fourier2 (Labels labels, Regions[] regions, double[] mean, double[] deviation, boolean normalization, detection_arguments parameters) {

    	ArrayList<TrainingSample<double[]>> list = new ArrayList<TrainingSample<double[]>>();

    	int fourier_size = parameters.fourier_size;

    	int number_of_regions = this.number_of_regions = labels.getNumberOfRegions();

    	int valid = 0;

    	for (int i = 0; i < deviation.length; i++) { if (deviation[i] != 0) {valid++;} }


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

    		Fourier fourier = new Fourier();

    		/**/
    		fourier.fourier_fibd_contour (labels, regions, i);

    		/**/
    		fourier.Discretize (fourier_size);

    		/**/
    		int center = fourier.Compute_TF_Image ();

    		double[] descriptor = new double[valid];

    		int j = 0, t = 0;

    		for(int k = 0; k < center; k++, j++) {
    			if (deviation[j] != 0) {
    				if (normalization) {
    					descriptor[t] = (fourier.getX(k)-mean[j])/deviation[j];
    				}
    				else {
    					descriptor[t] = fourier.getX(k);
    				}
    				t++;
    			}
    		} 
    		for(int k = 0; k < center; k++, j++) { 
    			if (deviation[j] != 0) {
    				if (normalization) {
    					descriptor[t] = (fourier.getY(k)-mean[j])/deviation[j];
    				}
    				else {
    					descriptor[t] = fourier.getY(k);
    				}
    				t++;
    			}
    		}
    		for(int k = center + 2; k < fourier_size; k++, j++) { 
    			if (deviation[j] != 0) {
    				if (normalization) {
    					descriptor[t] = (fourier.getX(k)-mean[j])/deviation[j];
    				}
    				else {
    					descriptor[t] = fourier.getX(k);
    				}
    				t++;
    			}
    		} 
    		for(int k = center + 2; k < fourier_size; k++, j++) { 
    			if (deviation[j] != 0) {
    				if (normalization) {
    					descriptor[t] = (fourier.getY(k)-mean[j])/deviation[j];
    				}
    				else {
    					descriptor[t] = fourier.getY(k);
    				}
    				t++;
    			}
    		}


    		int unknow = 2;
    		list.add(new TrainingSample<double[]>(descriptor, unknow));
    	}

    	return list;
    }

    
    /**/
    public void Extracting_Zernike (Labels labels, Regions[] regions, int descriptor_size, detection_arguments parameters)
    {
   	
    	int zernike_first_order = parameters.zernike_first_order;

    	int zernike_last_order = parameters.zernike_last_order;

    	int number_of_regions = this.number_of_regions = labels.getNumberOfRegions();

    	this.descriptor_size = descriptor_size;

    	Reads_Scale_Parameters_Model_From_Ascii_File (parameters.zernike_normalization);

    	data = new double [number_of_regions*descriptor_size];

    	Z[][] pzm = new Z[zernike_last_order + 1][2 * zernike_last_order + 1];

    	Z z_max = new Z();

    	/**/
    	double[] rho_max = z_max.Computing_Rho_Max (labels, regions);

    	/**/
    	for(int i = 1, j = 0; i < number_of_regions; i++) {
    		
    		for(int p = zernike_first_order, index = 1; p <= zernike_last_order; p++) {
    			Z z = new Z();
    			z.pseudo_zernike_moment_0 (p, labels, regions, rho_max[i], i);
    			pzm[p][zernike_last_order] = z;
     			for(int q = 1; q <= p; q++) {
    				Z2 z2 = new Z2();
    				z2.pseudo_zernike_moment(p, q, labels, regions, rho_max[i], i);
    				pzm[p][-q + zernike_last_order] = z2.getNum1();
    				pzm[p][+q + zernike_last_order] = z2.getNum2();
    			}
    			for(int q = -p; q <= p; q++) {
    				if (standard_deviation[index] != 0) {
    					data[j++]=(pzm[p][q+zernike_last_order].module() - mean[index])/standard_deviation[index];
    				}
    				index++;
    			}
    		}
    	}
    } /*Function-End*/
    
    
    
    /**/
    public ArrayList<TrainingSample<double[]>> Extracting_Zernike (Labels labels, Regions[] regions, double[] mean, double[] deviation, boolean normalization, detection_arguments parameters) {

    	ArrayList<TrainingSample<double[]>> list = new ArrayList<TrainingSample<double[]>>();

    	int zernike_first_order = parameters.zernike_first_order;

    	int zernike_last_order = parameters.zernike_last_order;

    	int number_of_regions = this.number_of_regions = labels.getNumberOfRegions();

    	this.descriptor_size = descriptor_size;

    	int valid = 0;

    	for (int i = 0; i < deviation.length; i++) { if (deviation[i] != 0) {valid++;} }
    	
    	//System.err.printf("Zernike size %d\n", valid);

    	Z[][] pzm = new Z[zernike_last_order + 1][2 * zernike_last_order + 1];

    	Z z_max = new Z();

    	/**/
    	double[] rho_max = z_max.Computing_Rho_Max (labels, regions);

    	/**/
    	for(int i = 1, j = 0; i < number_of_regions; i++) {

    		int t = 0;

    		double[] descriptor = new double [valid];
    		
    		for(int p = zernike_first_order, index = 0; p <= zernike_last_order; p++) {
    			Z z = new Z();
    			z.pseudo_zernike_moment_0 (p, labels, regions, rho_max[i], i);
    			pzm[p][zernike_last_order] = z;
    			for(int q = 1; q <= p; q++) {
    				Z2 z2 = new Z2();
    				z2.pseudo_zernike_moment(p, q, labels, regions, rho_max[i], i);
    				pzm[p][-q + zernike_last_order] = z2.getNum1();
    				pzm[p][+q + zernike_last_order] = z2.getNum2();
    			}
    			for(int q = -p; q <= p; q++) {
    				if (deviation[index] != 0) {
    					if (normalization) {
    						descriptor[t] = (pzm[p][q+zernike_last_order].module() - mean[index])/standard_deviation[index];
    					}
    					else {
    						descriptor[t] = pzm[p][q+zernike_last_order].module();
    					}
    					t++;
    				}
    				index++;
    			}
    		}
    		int unknow = 2;
			list.add(new TrainingSample<double[]>(descriptor, unknow));
    	}
    	return list; 
    } /*Function-End*/
    
    /**/
    public void Extracting_Polar2 (Labels labels, Regions[] regions, int descriptor_size, detection_arguments parameters) {
    	
    	int angle = parameters.polar_angle;
    	
    	int radius = parameters.polar_radius;
    	
    	int number_of_regions = this.number_of_regions = labels.getNumberOfRegions();

    	this.descriptor_size = descriptor_size;
    	
    	Reads_Scale_Parameters_Model_From_Ascii_File (parameters.polar_normalization);

    	data = new double [number_of_regions*descriptor_size];
    	
    	Polar polar1 = new Polar();
    	
    	int matrix_size1 = polar1.matrix_size1 (angle, radius);
    	
    	double[] sin1 = new double[matrix_size1];
    	
    	double[] cos1 = new double[matrix_size1];
    	
    	polar1.cos_sin_matrices1(angle, radius, sin1, cos1);
    	
    	int matrix_size2 = polar1.matrix_size2 (angle);
    	
    	double[] sin2 = new double[matrix_size2];
    	
    	double[] cos2 = new double[matrix_size2];
    	
    	polar1.cos_sin_matrices2 (angle, sin2, cos2);
    	
    	for(int i = 1, k = 0; i < number_of_regions; i++) {
    		
    		Polar polar2 = new Polar();
    		
    		int[] image_polaire = polar2.Compute_Polar_Image (labels, regions, sin1, cos1, i, angle, radius);

    		int[] TF = polar2.Compute_TF_Image (image_polaire, sin2, cos2, angle, radius);

    		for(int j = 0, l = 1; j < (angle*radius); j++, l++) {
    			if (standard_deviation[l]!=0) {
    				data[k++] = (((double)TF[j])-mean[l])/standard_deviation[l];
    			}
      		}
    	}
    } /*Function-End*/

    /**/
    public void Extracting_Polar (Labels labels, Regions[] regions, int descriptor_size, detection_arguments parameters) {
    	
    	int angle = parameters.polar_angle;
    	
    	int radius = parameters.polar_radius;
    	
    	int number_of_regions = this.number_of_regions = labels.getNumberOfRegions();

    	this.descriptor_size = descriptor_size;
    	
    	Reads_Scale_Parameters_Model_From_Ascii_File (parameters.polar_normalization);

    	data = new double [number_of_regions*descriptor_size];

    	for(int i = 1, k = 0; i < number_of_regions; i++) {
    	
    		Polar polar = new Polar();
   		
    		BufferedImage image_polaire = polar.Compute_Polar_Image (labels, regions, i, angle, radius);

    		BufferedImage TF = polar.Compute_TF_Image (image_polaire, angle, radius);

    		int[] array = Util.getImageArray(TF);

    		for(int j = 0, l = 1; j < (angle*radius); j++, l++) {
    			if (standard_deviation[l]!=0) {
    				data[k++] = (((double)array[j])-mean[l])/standard_deviation[l];
    			}
      		}
    	}
    } /*Function-End*/
    
    /**/
    public ArrayList<TrainingSample<double[]>> Extracting_Polar (Labels labels, Regions[] regions, double[] mean, double[] deviation, boolean normalization, detection_arguments parameters) {
    	
    	ArrayList<TrainingSample<double[]>> list = new ArrayList<TrainingSample<double[]>>();
    	
    	int angle = parameters.polar_angle;
    	
    	int radius = parameters.polar_radius;
    	
    	int number_of_regions = this.number_of_regions = labels.getNumberOfRegions();

    	this.descriptor_size = descriptor_size;
    	
    	int valid = 0;

    	for (int i = 0; i < deviation.length; i++) { if (deviation[i] != 0) {valid++;} }

    	//System.err.printf("Polar descriptor size %d\n", valid);
    	
    	//double[] descriptor = new double [valid];

    	for(int i = 1; i < number_of_regions; i++) {
    	
    		double[] descriptor = new double [valid];
    		
    		Polar polar = new Polar();
   		
    		BufferedImage image_polaire = polar.Compute_Polar_Image (labels, regions, i, angle, radius);

    		BufferedImage TF = polar.Compute_TF_Image (image_polaire, angle, radius);

    		int[] array = Util.getImageArray(TF);

    		int t = 0;
    		
    		for(int j = 0; j < (angle*radius); j++) {
    			if (deviation[j] != 0) {
    				if (normalization) {
    					descriptor[t] = (((double)array[j])-mean[j])/deviation[j];
    				}
    				else {
    					descriptor[t] = array[j];
    				}
    				t++;
    			}
    		}
    		int unknow = 2;
			list.add(new TrainingSample<double[]>(descriptor, unknow));
    	}
    	return list;
    	
    } /*Function-End*/
    
    
} /*Class-End*/
