package yi.iu.processor.graphseg;

import java.awt.*;
import yi.util.io.*;
import yi.iu.processor.*;
import yi.iu.processor.channel.*;

public class DM_DIFFXY extends DistanceMeasure
{
    int SMALL_SIZE_THRSH=16;
    int[] quan_table;
    //int[] quan_range={2,2,3,3,3,4,4,4,4,5,5,5,5,5};
    //int[] quan_range={1,1,1,2,2,3,5,8,13,21,34,55,89};
    int[] quan_range={2,2,3,3,3,4,4,4,4,5,5,5,5,5,20};
    
    public DM_DIFFXY() {
        quan_table= new int[256];
        int ptr=0;
        for (int i=0;i<quan_range.length;i++) {
            for (int j=0;j<quan_range[i];j++)
                quan_table[ptr++]=i;
        }
        for (int j=ptr;j<quan_table.length;j++) quan_table[j]= quan_range.length;
        //OutputResult.outputResult("quan_table", quan_table);
    };
    
    //return the bare data (including the diff_x and diff_y information)
    public int[] getBareData(int[] source, Dimension d)
    {
	    int width=d.width, height=d.height;
	    int size=width*height;
	    int[] info= new int[size*2];      //the return information includes 2 planes (diff_x and diff_y)

    	int[] intensity= (new IntensityMaker()).getProcessed(source,d);

	    int QUAN=256/BINS;
	    int ptr=0, ptr_image=0;
	    int[] c_stat=new int[256];
	    for (int y=0;y<height;y++) {
	        for (int x=0;x<width;x++) {
	            if (x>0) info[ptr]= Math.abs(intensity[ptr_image]-intensity[ptr_image-1]);
	            else info[ptr]= Math.abs(intensity[ptr_image+1]-intensity[ptr_image]);
	            //c_stat[info[ptr]]++;
	            //info[ptr]/=QUAN;
	            info[ptr]=quan_table[info[ptr]];
	            ptr++;
	            if (y>0) info[ptr]= Math.abs(intensity[ptr_image]-intensity[ptr_image-width]);
	            else info[ptr]= Math.abs(intensity[ptr_image+width]-intensity[ptr_image]);
    	        //c_stat[info[ptr]]++;
    	        //info[ptr]/=QUAN;
    	        info[ptr]=quan_table[info[ptr]];
    	        ptr++; ptr_image++;
    	    }
	    }
	    //OutputResult.outputResult("diffxy", c_stat);
	    return info;
    }
        
    //To save calculation
    //the histogram of diff_x [0..BINS-1], diff_y [BINS..BINS2-1]
    //the size of the region [SIZE]
    int BINS2= BINS+BINS;
    int SIZE= BINS2;
    //make the region information given the x,y coordinate in term of block
    //the region information including : the histogram of DIFF_X, DIFF_Y and the size of the region
    public int[] makeRgnInfo(int[] bareData, int w, int h, int blkSize, int x, int y)
    {
        int[] hst= new int[3*(BINS+1)];
        int QUAN=256/BINS;

        //calculate the region information (the histogram)
        for (int r=y*blkSize;r<(y+1)*blkSize;r++) {
            for (int c=x*blkSize;c<(x+1)*blkSize;c++) {
                int ptr= 2*(r*w+c);
                hst[bareData[ptr++]]++;
                hst[BINS+bareData[ptr]]++;
                hst[SIZE]++;
            }
        }
        return hst;
    }

    public int distance(int h1[], int h2[]) {
        //if the difference between the weights of hue are large enough, return a big number;
        int size1=h1[SIZE], size2=h2[SIZE];
        int small_size= size1; if (size2<small_size) small_size=size2;
        
        int diff_x= distance(h1,h2,0,BINS,SIZE);
        int diff_y= distance(h1,h2,BINS,BINS2,SIZE);

        int diff=diff_x; if (diff_y>diff) diff=diff_y;
        
        //adjust the distance according to the size of region
        //if (small_size<SMALL_SIZE_THRSH) diff*=(double)small_size*small_size/(SMALL_SIZE_THRSH*SMALL_SIZE_THRSH);
        if (small_size<SMALL_SIZE_THRSH) diff=diff*small_size/(SMALL_SIZE_THRSH);

        //return diff_h+diff_c+diff_i;
        return (int)diff;
    }
        
	//return the distance between two histogram on a single property
	int distance(int h1[], int h2[], int begin, int end, int size_i) {
        int sum1=0;
        int sum2=0;
        int sum=0;
        int size1=h1[size_i], size2=h2[size_i];
            
        if (size1==0) return 0;
        if (size2==0) return 0;
            
        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>1024&&adjust2>1024) {
            adjust2=(int)((double)adjust2/adjust1*1024);
            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=begin;i<end;i++) {
            sum1+=h1[i]; sum2+=h2[i];
            int d=sum1*adjust2-sum2*adjust1;
            sum+= Math.abs(d);
        }
            
        if (sum<0)  {
            System.err.println("distance is negative");
            return BIGNUM;
        }
        sum=(int)((double)sum*DISSCALE/adjust_back);
        return sum;
    }
        
    public boolean VerifyRgnInfo(int[] bareData, int w, int h, int blkSize, int[] segs, int[][] rgnInfo)
    {
        return true;
    }
        
    public void printDiffOfHists(int[][] rgnInfo, int x, int y)
    {
        if (x==y) return ;
        int[] h1=rgnInfo[x], h2=rgnInfo[y];

        int size1=h1[SIZE], size2=h2[SIZE];
        int small_size= size1; if (size2<small_size) small_size=size2;
        
        int diff_x= distance(h1,h2,0,BINS,SIZE);
        int diff_y= distance(h1,h2,BINS,BINS2,SIZE);

        int diff=diff_x; if (diff_y>diff) diff=diff_y;

        if (small_size<SMALL_SIZE_THRSH) diff=diff*small_size/(SMALL_SIZE_THRSH);

        System.err.println("distance "+x+"<->"+y+":"+diff);
        System.err.println("        diff_x="+diff_x+" diff_y="+diff_y);
        OutputResult.outputResult("region_"+x,h1);
        OutputResult.outputResult("region_"+y,h2);
    }
        
}
