package descriptors;

import java.awt.image.BufferedImage;

import preprocessing.Util;
import segmentation.Labels;
import segmentation.Regions;

public class Z {
	
	//public final static int PZM_FIRST_ORDER = 1;
	//public final static int PZM_LAST_ORDER  = 6;
	
    private double a;
    private double b;

    public int barycenter_x;
	
	public int barycenter_y;
    
    public double getA () {
    	return this.a;
    }
    
    public double getB () {
    	return this.b;
    }
    
    public void setA (double a) {
    	this.a = a;
    }
    
    public void setB (double b) {
    	this.b = b;
    }
    
    public double module ()
    {
        return Math.sqrt(this.a * this.a + this.b * this.b);
    }
    
    public double[] Computing_Rho_Max (Labels labels, Regions[] regions) {
    	int number_of_regions = labels.getNumberOfRegions();
    	int width  = labels.getWidth();
    	int height = labels.getHeight();
    	double[] rho_max = new double[number_of_regions];
    	for(int y = 1; y < height - 1; y++) {
    		for(int x = 1; x < width - 1; x++) {
    			int region_index = labels.getValue(x + y * width);
    			if (region_index != 0) {
    				int xg = regions[region_index].getXg (); 
    				int yg = regions[region_index].getYg ();
    				
    				double d = ( (x - xg) * (x - xg) + (y - yg) * (y - yg) );
    				if ( d > rho_max[region_index] ) {
    					rho_max[region_index] = d;
    				}
    			}
    		}
    	}
    	for(int i = 1; i < number_of_regions; i++) {
    		rho_max[i] = Math.sqrt(rho_max[i]);
    	}
    	return rho_max;
    }
    
    public double Computing_Rho_Max (BufferedImage image) {
    	
    	
    	int width = image.getWidth();
		int height = image.getHeight();
		int[] array = Util.getImageArray (image);
		double max=0;
		
		int surface=0;
	    barycenter_x=0;
	    barycenter_y=0;
	    for(int y=0;y<height;y++)
	        for(int x=0;x<width;x++)
	        {
	            if (array[x+y*width]==255) {
	            	barycenter_x+=x;
	            	barycenter_y+=y;
	                surface++;
	            }
	        }
	    barycenter_x/=surface;
	    barycenter_y/=surface;

    	for(int y = 0; y < height; y++) {
    		for(int x = 0; x < width; x++) {
    			if (array[x+y*width]==255) {
    				
    				double d = ( (x - barycenter_x) * (x - barycenter_x) + (y - barycenter_y) * (y - barycenter_y) );
    				if ( d > max ) {
    					max = d;
    				}
    			}
    		}
    	}
    	max=Math.sqrt(max);
    	
    	return max;
    }
    
    private double Zp0 (int p, double r)
    {
        double K0, Ki, k1, k2, k3, k4;
        double result;
        int i;

        if (p<2) {
            if (p==0) {
                return 1;
            }
            else {
                double rpm1 = expo(r, p-1);
                return ((2*p+1)*r-2*p)*rpm1;
            }
        }

        K0=p+1;
        if (p%2==1) K0=-K0;

        k1=p+2;
        k2=2;
        k3=p;
        k4=1;
        result=Ki=K0;
        for (i=1;i<=p;i++) {
            Ki=-Ki*k1*k3/k2/k4*r;
            result+=Ki;
            k1++;
            k2++;
            k3--;
            k4++;
        }
        return result;
    }
    
    public void V0 (int p, double rho, double theta)
    {
        double rpq = Zp0(p, rho);
        this.a = rpq;
        this.b = 0;
    }
    
    public void V (int p, int q, double rho, double theta)
    {
        double rpq = Zpq (p, q, rho);
        a = rpq * Math.cos(q*theta);
        b = rpq * Math.sin(q*theta);
    }
    
    public double Zpq (int p, int q, double r)
    {
        double K0, Ki, k1, k2, k3, k4;
        double result;
        int i;
        if (p-q<2) {
            if (p==q) {
                return expo(r, p);
            }
            else {
                double rpm1 = expo(r, p-1);
                return ((2*p+1)*r-2*p)*rpm1;
            }
        }
        if (q==1 && p>3) {
            K0=(p+2)*(p+1)*p/3/2;
            if (p%2==0) K0=-K0; //(-1)^(p-1)
            K0*=r;
        } else if (q==p-2) {
            K0=(p-1)*(2*p-1);
            K0*=expo(r, q);
        } else {
            K0=function(p+q+1, 2*q+1, p-q);
            if ((p-q)%2==1) K0=-K0;
            K0*=expo(r, q);
        }
        k1=p+q+2;
        k2=2*q+2;
        k3=p-q;
        k4=1;
        result=Ki=K0;
        for (i=1;i<=(p-q);i++) {
            Ki=-Ki*k1*k3/k2/k4*r;
            result+=Ki;
            k1++;
            k2++;
            k3--;
            k4++;
        }
        return result;
    }
    
    private double expo(double r, int exp)
    {
        double result=1;
        while(exp>0) {
            if (exp%2!=0) {result*=r;}
            r*=r;
            exp/=2;
        }
        return result;
    }
    
    private double function(int a, int b, int c)
    {
        int max, min;
        double i;
        double r=1;
        if (b>c) {
            max=b;
            min=c;
        } else {
            max=c;
            min=b;
        }
        for(i=a;i>max;i--) {
            r*=i;
        }
        for(i=2;i<=min;i++) {
            r/=i;
        }
        return r;
    }
    
    public void conjugue ()
    {
        this.b = -this.b;
    }
    
	public void pseudo_zernike_moment_0 (int p, Labels img, Regions[] regions, double rho_max, int studied_region)
	{
		int width = img.getWidth();

		this.a = 0;
		this.b = 0;

		Regions reg = regions[studied_region];
		double r_a = 0.0;
		double r_b = 0.0;
		int x,y;

		int x_min = reg.getXmin ();
		int x_max = reg.getXmax ();

		int y_min = reg.getYmin ();
		int y_max = reg.getYmax ();

		for(y = y_min; y <= y_max; y++) {
			for(x = x_min; x <= x_max; x++)
			{
				int xg = reg.getXg (); 
				int yg = reg.getYg ();
				double xc = ((double)(x - xg))/rho_max;
				double yc = ((double)(y - yg))/rho_max;
				double rho = Math.sqrt(xc * xc + yc * yc);
				double theta = Math.atan2(yc, xc);
				if (img.getValue(x + y * width) == studied_region) { 
					V0 (p, rho, theta);
					conjugue ();
					r_a += this.a;
					r_b += this.b;
					this.a = 0.0;
					this.b = 0.0;
				}
			}
		}
		r_a *= ((p+1)/Math.PI);
		r_b *= ((p+1)/Math.PI);
		this.a = r_a;
		this.b = r_b;
	}
	
	public void pseudo_zernike_moment_0 (int p,  BufferedImage image, double rho_max, int xg, int yg)
	{
		int width = image.getWidth();
		int height = image.getHeight();
		int[] array = Util.getImageArray (image);

		this.a = 0;
		this.b = 0;

		double r_a = 0.0;
		double r_b = 0.0;
		int x,y;

		for(y = 0; y < height; y++) {
			for(x = 0; x < width; x++)
			{
				double xc = ((double)(x - xg))/rho_max;
				double yc = ((double)(y - yg))/rho_max;
				double rho = Math.sqrt(xc * xc + yc * yc);
				double theta = Math.atan2(yc, xc);
				if (array[x+y*width]==255) { 
					V0 (p, rho, theta);
					conjugue ();
					r_a += this.a;
					r_b += this.b;
					this.a = 0.0;
					this.b = 0.0;
				}
			}
		}
		r_a *= ((p+1)/Math.PI);
		r_b *= ((p+1)/Math.PI);
		this.a = r_a;
		this.b = r_b;
	}
	
    
    
}
