package yi.iu.processor.graphseg;

import java.awt.*;

import yi.util.io.*;
public abstract class DistanceMeasure
{
    public int RGNNUMAGU= 24;  //at the beginning, there are rgn_num1= (width/Basic_Region_size)*(height/Basic_Region_Size) of regions
                        //the possible region number could increase as time goes on, the estimated region number is given as
                        //rgn_num1*RGNNUMAGU.  For example, for 640*480 image and basic_region_size=8, the rgn_num1= 80*60; if
                        //RGNNUMAGU=16, then 80*60*16 is the estimated possible region number (maximum)
    public int BINS=16;        //the number of bins for this distance measure.  For example, for intensity, the BINS could be 16,32, or 64
    
    //int TINYAREATHRSH=8;    //see the function 	int distance(int v, int h[])
                            //when a region is too small, it doesn't like to combine with other regions
                            //this argument is used to adjust the distance between a pixel and a small region.  What's a small region is
                            //defined by this argument
                            
    //int DisEnlargeRatio=16; //the distance is enlarged a little to make the distance represented by a integer instead of a doulbe
    
    public final static int BIGNUM=65532;
    
    public final static int DISSCALE=64;   //make the distance more concise when convert it from double to integer, i.e., a enlarge ratio.
    boolean VERBOSE=false;
                                
    abstract public int[] getBareData(int[] source, Dimension d);

    //make the region information given the x,y coordinate in term of block
    public int[] makeRgnInfo(int[] bareData, int w, int h, int blkSize, int x, int y)
    {
        int[] hst= new int[BINS+1];
        int QUAN=256/BINS;
        //calculate the histogram
        for (int r=y*blkSize;r<(y+1)*blkSize;r++) {
            for (int c=x*blkSize;c<(x+1)*blkSize;c++) {
                hst[bareData[r*w+c]/QUAN]++;
            }
        }
        hst[BINS]=blkSize*blkSize;
        return hst;
    }
    
    public int[] makeRgnInfo(int[] bareData, int w, int h, int blkSize, int index)
    {
        int wr= w/blkSize;
        int y=index/wr;
        int x=index-y*wr;
        return makeRgnInfo(bareData,w,h,blkSize,x,y);
    }

    //calculate the information for the basic regions (e.g. histogram for every 8*8 blocks
    public int[][] getBscRgnInfo(int[] bareData, int w, int h, int BSCRGNSIZE)
    {
        int wr=w/BSCRGNSIZE, hr=h/BSCRGNSIZE;       //width and height in term of basci region
        
        //the histogram information has two parts: histogram, and the number of regions counted on
        int hists[][]= new int[hr*wr*RGNNUMAGU][BINS+1];
        if (VERBOSE) System.err.println("rgn#="+hists.length);
        int QUAN=256/BINS;
        for (int i=0;i<hr;i++) {
            for (int j=0;j<wr;j++) {
                /*
                //calculate the histgram information for every 8*8 basic region
                for (int m=0;m<BSCRGNSIZE;m++) {
                    for (int n=0;n<BSCRGNSIZE;n++) {
                        int index= bareData[(i*BSCRGNSIZE+m)*w+j*BSCRGNSIZE+n]/QUAN;
                        /*
                        if (i*wr+j==922||i*wr+j==923)
                        {
                            int t=bareData[(i*BSCRGNSIZE+m)*w+j*BSCRGNSIZE+n];
                            index= bareData[(i*BSCRGNSIZE+m)*w+j*BSCRGNSIZE+n]/QUAN;
                        }
                        */
                /*        
                        hists[i*wr+j][index]++;
                    }
                }
                
                //only 1 basic region is counted to form the histogram
                hists[i*wr+j][BINS]=BSCRGNSIZE*BSCRGNSIZE;
                */
                hists[i*wr+j]=makeRgnInfo(bareData, w, h, BSCRGNSIZE, j, i);
            }
        }
        return hists;
    }

/*
	//calculate the distance between a value of pixel and a histogram
	//it could be used to represent the possibility of a pixel belonging to a region
	int distance(int v, int h[]) {
	    /*
        int size= h[BINS];
        if (size==0) {
            System.err.println("try to combine a pixel with a 0-size region!");
            return 100;
        }
        int sumh=0;
        int sum=0;
        //calculate the distance from the left of the value
        for (int i=0;i<v;i++) {
            sumh+=h[i];
            sum+=sumh;
        }
        //calculate the distance from the right of the value
        sumh=0;
        for (int i=BINS-1;i>v;i--) {
            sumh+=h[i];
            sum+=sumh;
        }
        sum+=(size-h[v]);
        
        //is it necessary to adjust the sum according to another arguments
        //-------------------
        //choice 1: no adjust
        //sum=sum/size;
        //-------------------
        //choice 2: adjust it according to the sizes of the regions
        //sum*=(4096/(int)(Math.sqrt(size)));
        return sum;
        */
        
        //---------
        //other choice
        //possible problems: the big area will eat the whole small area
        //if (h[v]==0) return BIGNUM;
        //else return BIGNUM/h[v];
        
        //---------
        //other choice
        //possible problem: e.g. sky is quantilize to 30 and 31, but the in a small block, all to 31, so it likes to belong to the small block
        //if (h[v]==0) return BIGNUM;
        //else return h[BINS]*256/h[v];
        
        //---------
        //other choice
        //possible problem: the tiny area is not easy to combine with others.  say, when size=1, the result will be always=256
        //if (h[v]==0) return BIGNUM;
        //int num=h[v];
        //if (v>0) num+=h[v-1];
        //if (v<BINS-1) num+=h[v+1];
        //return h[BINS]*256/num;
        
        //---------
        //other choice
        //possible problem: the tiny region is not easy to combine with others.  say, when size=1, the result will be always=256
/*        int size=h[BINS];
        int num=h[v];
        if (v>0) num+=h[v-1];
        if (v<BINS-1) num+=h[v+1];
        if (num==0) return BIGNUM;
        return size*DisEnlargeRatio/num;
        //the value of num/size represents the possibility of the pixel belonging to the region
        //for example, if num/size=1, then the possibility=100%; num/size=0, possibility=0;
        //so, when num/size=1, the distance=16; num/size=0.5, distance=32, if DisEnlargeRatio=16.
        
    }
*/    
    public int getThreshold(int base, int firstSize, int nowSize)
    {
        //return base*firstSize/nowSize;
        return base;
    }
    
	public int distance(int h1[], int h2[]) {
        int sum1=0;
        int sum2=0;
        int sum=0;
        int size1=h1[BINS], size2=h2[BINS];
        
        int adjust1=size1, adjust2=size2;
        double adjust_back=size1*size2;
        
        //if adjusts are too big, the calculation may lead to overflow, so make them smaller
        if (adjust1>1000&&adjust2>1000) {
            adjust2=(int)((double)adjust2/adjust1*1000);
            adjust1=1000;
            //eg. if adjust1=3000, adjust2=2000, after change, the adjust1=1000, adjust2=667
            //for value of number of elements in a bin, the value should be normalized by dividing size
            //say, h1[x] should be normalized to h1[x]/3000, but now to *adjust2/(adjust1*adjust2)=h1[x]/adjust1=/1000,
            //so the adjust_back should be : 
            adjust_back=(adjust1*adjust2)*((double)size1/adjust1);
        }
        
        //choice 1
        for (int i=0;i<BINS;i++) {
            sum1+=h1[i]; sum2+=h2[i];
            int d=sum1*adjust2-sum2*adjust1;
            sum+= Math.abs(d);
        }
        
        
        //choice 2
        //for (int i=0;i<BINS-1;i++) {
            //int d=h1[i]-h2[i];
            //sum+= Math.abs(d);
        //}
        //choice 3
        /*
        int[] h11=(int[])h1.clone();
        int[] h21=(int[])h2.clone();
        for (int i=0;i<l;i++) {
            if (i>0) {
                h11[i]+=h1[i-1];
                h21[i]+=h2[i-1];
            }
            if (i<l-1) {
                h11[i]+=h1[i+1];
                h21[i]+=h2[i+1];
            }
        }
        
        int[] h12=(int[])h11.clone();
        int[] h22=(int[])h21.clone();
        for (int i=0;i<l;i++) {
            if (i>1) {
                h12[i]+=h1[i-2];
                h22[i]+=h2[i-2];
            }
            if (i<l-2) {
                h12[i]+=h1[i+2];
                h22[i]+=h2[i+2];
            }
        }
        for (int i=0;i<l;i++) {
            //int d=h1[i]*adjust2-h2[i]*adjust1;
            //sum+= Math.abs(d);
            int d;
            d=h11[i]*adjust2-h21[i]*adjust1;
            sum+= Math.abs(d/3);
            d=h12[i]*adjust2-h22[i]*adjust1;
            sum+= Math.abs(d/5);
        }
        */
        
        //choice 2
        /*
        if (sum<FIRSTGROWTHRSH) return sum;
        int sum_shift=0;
        sum1=sum2=0;
        for (int i=0;i<l-1;i++) {
            sum1+=h1[i]; sum2+=h2[i+1];
            int d=sum1*adjust2-sum2*adjust1;
            sum_shift+= Math.abs(d);
        }
        if (sum_shift<sum) sum=sum_shift;
        sum_shift=0;
        sum1=sum2=0;
        for (int i=0;i<l-1;i++) {
            sum1+=h1[i+1]; sum2+=h2[i];
            int d=sum1*adjust2-sum2*adjust1;
            sum_shift+= Math.abs(d);
        }
        if (sum_shift<sum) sum=sum_shift;
        */
        if (sum<0)  {
            System.err.println("distance is negative");
            return BIGNUM;
        }


        
        //is it necessary to adjust the sum according to another arguments
        //-------------------
        //choice 1: no adjust
        sum=(int)((double)sum*DISSCALE/adjust_back);
        //choices about how to combine two histograms which have difference but only by a shift
        //choice 1
        /*
        if (sum<FIRSTGROWTHRSH) return sum;
        
        if (h1[BINS]<500||h2[BINS]<500) return sum;
        //try to find the continous changed regions
        int[] peaks=new int[l];
        int peak_thrsh1= h1[BINS]/3, peak_thrsh2= h2[BINS]/3;
        boolean have_peak1=false, have_peak2=false;
        for (int i=0;i<l;i++) {
            if (h1[i]>peak_thrsh1) { peaks[i]=1;have_peak1=true;}
            if (h2[i]>peak_thrsh2) { peaks[i]=1;have_peak2=true;}
        }
        if (have_peak1&&have_peak2) {
            boolean peakfound=false;
            boolean multipeak=false;
            int last_peak=-1;
            for (int i=0;i<l;i++) {
                if (peaks[i]==1) {
                    if (!peakfound)
                        peakfound=true;
                    else if (i-last_peak>1)
                        {multipeak=true;break;}
                    last_peak=i;
                }
                    
            }
            if (!multipeak)
                sum=FIRSTGROWTHRSH-1;
                
        }
        */
        
        //-------------------
        //choice 2: adjust it according to the sizes of the regions
        //sum/=(int)(Math.sqrt(adjust1*adjust2));
        //-------------------
        //choice 3: adjust it according to the size of the smaller region
        //if (adjust1<adjust2) adjust2=adjust1;
        //sum*=(int)(Math.sqrt(adjust2));
        //-------------------
        //choice 4: make the small region more like to be merged with a big region
        //if (adjust1<adjust2) adjust2=adjust1;   //make adjust1>=adjust2
        //sum=sum/(adjust1*adjust2);
        //sum/=(int)(adjust1/adjust2);
        
        return sum;
    }
    
    void rgnUnion(int[] h1, int[] h2)
    {
        int l= h1.length;
        for (int i=0;i<l;i++) h1[i]+=h2[i];
    }
    
    void removeRgn(int[] h1, int[] h2){
        int l= h1.length;
    	for (int i=0;i<l;i++) {
    	    h1[i]-=h2[i];
    	}
    }
    
    void moveRgn(int[] h1, int[] h2, int[] h3){
        int l= h1.length;
    	for (int i=0;i<l;i++) {
    	    h1[i]-=h3[i];
    	    h2[i]+=h3[i];
    	}
    }
    
    public boolean VerifyRgnInfo(int[] bareData, int w, int h, int blkSize, int[] segs, int[][] rgnInfo)
    {
    	int wr=w/blkSize, hr=h/blkSize;
    	int[][] test= new int[rgnInfo.length][BINS+1];
    	int QUAN=256/BINS;
    	for (int i=0;i<hr;i++) {
    	    for (int j=0;j<wr;j++) {
    	        int rgnN= segs[i*wr+j];
    	        for (int y=0;y<blkSize;y++) {
    	            for (int x=0;x<blkSize;x++) {
    	                int c= bareData[(i*blkSize+y)*w+j*blkSize+x]/QUAN;
    	                test[rgnN][c]++;
    	                test[rgnN][BINS]++;
    	            }
    	        }
    	    }
    	}
    	boolean OK=true;
    	for (int i=0;i<rgnInfo.length;i++) {
    	    if (test[i][BINS]==0) continue;
    	    for (int j=0;j<=BINS;j++) {
    	        if (rgnInfo[i][j]!=test[i][j])
    	            {OK=false;i=rgnInfo.length; break;}
    	    }
    	}
    	return OK;
    }
    
    public void printDiffOfHists(int[][] rgnInfo, int x, int y)
    {
        if (x==y) return;
        int d=distance(rgnInfo[x],rgnInfo[y]);
        System.err.println("distance "+x+"<->"+y+": "+d);
        OutputResult.outputResult("region_"+x,rgnInfo[x]);
        OutputResult.outputResult("region_"+y,rgnInfo[y]);
    }
}