package yi.iu;

import yi.iu.processor.*;
import yi.iu.processor.filter.GaussianSmooth;
import yi.iu.processor.channel.*;
import yi.iu.processor.morph.*;
import yi.iu.SImage;
import yi.iu.ui.DisplayFrame;
import yi.util.io.OutputResult;

import java.awt.*;
import java.util.Vector;

public class Histogram
{
    static int PEAKTHESHOLD=14;
    static int PEAK=1;
    boolean VERBOSE=true;

    int HISTUNIT=10000;      //the unit of histogram.
    static public int DATARANGE= 256;         //the default range of possible value of the histogram is [0..256]
    public int[] hist=null;

    public Histogram(int[] data)
    {
        hist= new int[DATARANGE];
        int size= data.length;
        for (int i=0;i<size;i++) {
            int index= data[i];
            if (index>=DATARANGE) {
                System.err.println("wrong histogram index "+index+" / "+DATARANGE);
                continue;
            }
            hist[index]++;
        }
    }

    // make a 2D image of the histogram
    SImage get2DImage()
    {
        int width=DATARANGE, height=256,size=width*height;
        int image[]= new int[width*height];
        //find the biggest first
        int biggest=0;
        for (int i=0;i<width;i++) {
            if (hist[i]>biggest) biggest= hist[i];
        }
        if (VERBOSE) System.err.println("The biggest:"+biggest);
        //make the histogram image
        for (int i=0;i<width;i++) {
            int h= hist[i]*height/biggest;
            for (int j=0;j<h;j++) {
                image[j*width+i]=0xff000000;
            }
            for (int j=h;j<height;j++) {
                image[j*width+i]=0xffffffff;
            }
        }
        return new SImage(width,height,SImage.GRAY,image);
    }

    int[] getPeaks()
    {
        int[] h1= (new GaussianSmooth(0.5)).getProcessed(hist,new Dimension(DATARANGE, 1));
        int[] h2= (new GaussianSmooth(1.5)).getProcessed(hist,new Dimension(DATARANGE, 1));
        int[] hd= new int[DATARANGE];
        for (int i=0;i<DATARANGE; i++) {
            //hd[i]= h1[i]-h2[i];
            //if ( h1[i]-h2[i]<PEAKTHESHOLD) hd[i]=0;
            //if ( h1[i]-h2[i]>=PEAKTHESHOLD) hd[i]=PEAK;
            if ( h1[i]-h2[i]>=PEAKTHESHOLD) hd[i]=h1[i]=h2[i];
        }
        //(new DisplayFrame("h1",get2DImage(h1, new Dimension(DATARANGE,256)))).show();
        //(new DisplayFrame("h2",get2DImage(h2, new Dimension(DATARANGE,256)))).show();
        //(new DisplayFrame("h1-h2",get2DImage(hd, new Dimension(DATARANGE,256)))).show();
        //OutputResult.outputResult(hd, DATARANGE, 1);
        return hd;

    }

    public int[] getProcessed(int source[], Dimension d)
    {
        yi.iu.ui.DisplayFrame f;
        //get the red channel
        yi.iu.processor.channel.RedChannelMaker rm= new yi.iu.processor.channel.RedChannelMaker();
        int[] red= rm.getProcessed(source, d);
        //get the histogram image
        int[] hist= getHistogram(red,d);
        unify(hist,HISTUNIT);
        int[] histimage= get2DImage(hist,new Dimension(DATARANGE,256)).data;
        f= new yi.iu.ui.DisplayFrame(this, histimage, DATARANGE, 256);
        f.show();
        //smooth the histogram
        yi.iu.processor.filter.GaussianSmooth gs= new yi.iu.processor.filter.GaussianSmooth(3);
        int smooth[]= gs.getProcessed(hist,new Dimension(DATARANGE,1));
        int[] smoothimage= get2DImage(smooth,new Dimension(DATARANGE,256)).data;
        f= new yi.iu.ui.DisplayFrame(this, smoothimage, DATARANGE, 256);
        f.show();
        getHistSeg(red,d,smooth,DATARANGE);
        return null;
    }

    public int[] getProcessedPseudoColor(int source[], Dimension d)
    {
        return getProcessed(source,d);
    }

    // make a histogram from a bare channel image
    int[] getHistogram(int source[], Dimension d)
    {
        int hist[]= new int[DATARANGE];
        int size= d.width*d.height;
        for (int i=0;i<size;i++) {
            int index= source[i];
            if (index>=DATARANGE) {
                System.err.println("wrong histogram index "+index+" / "+DATARANGE);
                continue;
            }
            hist[index]++;
        }
        return hist;
    }

    //unify the histogram.  for example, the biggest is unified to 255
    void unify(int[] hist,int uni)
    {
        int width=hist.length;
        //find the biggest first
        int biggest=0;
        for (int i=0;i<width;i++) {
            if (hist[i]>biggest) biggest= hist[i];
        }
        if (VERBOSE) System.err.println("The biggest:"+biggest);
        //make the histogram image
        for (int i=0;i<width;i++) {
            int h= hist[i]*uni/biggest;
            if (h==uni) h--;
            hist[i]=h;
        }
    }

    // make a 2D image from 1D array, like a histogram
    SImage get2DImage(int[] hist, Dimension d)
    {
        int width=d.width, height=d.height,size=width*height;
        int image[]= new int[width*height];
        //find the biggest first
        int biggest=0;
        for (int i=0;i<width;i++) {
            if (hist[i]>biggest) biggest= hist[i];
        }
        if (VERBOSE) System.err.println("The biggest:"+biggest);
        //make the histogram image
        for (int i=0;i<width;i++) {
            int h= hist[i]*height/biggest;
            for (int j=0;j<h;j++) {
                image[j*width+i]=0xff000000;
            }
            for (int j=h;j<height;j++) {
                image[j*width+i]=0xffffffff;
            }
        }
        return new SImage(width, height, SImage.RGB, image);
    }

    void displayHills(int[] hist, int tones, Hill[] hills)
    {
        int image[]= get2DImage(hist, new Dimension(tones,256)).data;
        for (int i=0;i<hills.length;i++) {
            int left= hills[i].left, right=hills[i].right;
            for (int j=0; j<256; j++) {
                image[j*tones+left]=0xffff0000;
                image[j*tones+right]=0xff00ff00;
            }
        }
        yi.iu.ui.DisplayFrame f= new yi.iu.ui.DisplayFrame(this, image, tones, 256);
        f.show();
    }

    //make segmentation based on the histogram
    //source: source image, d: dimension of source image
    //smooth: smoothed histgram, tones: # of value the histogram has
    void getHistSeg(int source[], Dimension d, int hist[], int tones)
    {
        //get the hills in the histogram
        //Hill hills[]= getHills(hist, tones);
        //displayHills(hist, tones, hills);
        //getHistBasedOnHills(source, d, hills);
    }

    //return the segments based on hills
    void getHistBasedOnHills(int[] source, Dimension d, Hill[] hills)
    {
        for (int i=0; i<hills.length; i++) {
            if (i!=2) continue;
            //mask first
            int left=hills[i].left,right=hills[i].right;
            //int left=144,right=255;
            int[] masked= new int[source.length];
            for (int j=0; j<source.length; j++)
                if (source[j]>=left&&source[j]<=right) masked[j]=source[j];

            int[] image= yi.util.ImageUtil.makeNewImageFromBinary(masked);
            yi.iu.ui.DisplayFrame f= new yi.iu.ui.DisplayFrame("hill "+left+"-"+right, image, d.width, d.height);
            f.show();

            /*
            //do summation 7*7, and we can do segmentation based on the summation, not good?
            int[] stct= new int[49];
            for (int k=0;k<49;k++) stct[k]=1;
            Masker masker= new Masker(stct, 7, 7);
            int[] sum= masker.getProcessed(masked, d);
            displaySum(sum,d);
            */


            if (i==2) {
                Closing morph= new Closing(Morph.DISK, 3);
                masked= morph.getProcessed(masked, d);
                image= yi.util.ImageUtil.makeNewImageFromBinary(masked);
                f= new yi.iu.ui.DisplayFrame(this, image, d.width, d.height);
                f.show();
            }
            /*
            //closing lose several important point, not good?
            if (i==2) {
                Opening morph= new Opening(Morph.DISK, 3);
                masked= morph.getProcessed(masked, d);
                image= yi.util.ImageUtil.makeNewImageFromBinary(masked);
                f= new yi.iu.ui.DisplayFrame(this, image, d.width, d.height);
                f.show();
            }
            */

        }
    }
/*
    void displaySum(int[] sum, Dimension d)
    {
        int size=sum.length;
        int[] image= yi.util.ImageUtil.makeNewImageFromGray(sum);
        yi.iu.ui.DisplayFrame f= new yi.iu.ui.DisplayFrame(this, image, d.width, d.height);
        f.show();
    }

    static final int LOCALMAXIMA=1;
    static final int LOCALMINIMAL=2;
    //return the hills of the 1-D data which is from [0,tones)
    Hill[] getHills(int data[], int tones)
    {
        int slope= (LOCALSIZE-1)/2;
        int size= data.length;
        int analysis[]= new int[size];
        int j;

        //get the local maximas
        for (int i=0;i<size;i++) {
            for (j=-slope;j<=slope;j++) {
                int compared=i+j;
                if (compared<0||compared>=size) continue;
                if (data[compared]>data[i]) j=slope+3;      //to jump out to loop
            }
            if (j!=slope+4) analysis[i]=LOCALMAXIMA;
        }

        //get the local minimals
        for (int i=0;i<size;i++) {
            for (j=-slope;j<=slope;j++) {
                int compared=i+j;
                if (compared<0||compared>=size) continue;
                if (data[compared]<data[i]) j=slope+3;      //to jump out to loop
            }
            if (j!=slope+4) analysis[i]=LOCALMINIMAL;
        }

        //if there are some maximas in a line (continuous), select the middle one
        for (int i=0;i<size;i++) {
            if (analysis[i]==LOCALMAXIMA) {
                int left=i,right=i;
                for (i++;i<size;i++ ) {
                    if (analysis[i]==LOCALMAXIMA) right=i;
                    if (analysis[i]==LOCALMINIMAL) break;
                }
                if (right>left) {
                    for (j=left;j<=right;j++) analysis[j]=0;
                    analysis[left+(right-left)/2]=LOCALMAXIMA;
                }
            }
        }

        //if there are some minimals in a line (continuous), select the left one and right one
        for (int i=0;i<size;i++) {
            if (analysis[i]==LOCALMINIMAL) {
                int left=i,right=i;
                for (i++;i<size;i++ ) {
                    if (analysis[i]==LOCALMINIMAL) right=i;
                    if (analysis[i]==LOCALMAXIMA) break;
                }
                if (right>left) {
                    for (j=left+1;j<right;j++) analysis[j]=0;
                }
            }
        }

        //find the Hills from one minimal to another minimal
        Vector vhills= new Vector();
        int left=0, right=0, mid=0, index=0;
        while (index<size) {
            //1. find the first minimal
            for (; index<size; index++) {
                if (analysis[index]==LOCALMINIMAL) {left=index;break;}
                if (analysis[index]==LOCALMAXIMA) {left=0;break;}
            }
            if (index>=size) break;

            //2. find the maxima in between two minimal
            for (; index<size; index++) {
                // if meet another minimal, replace the first one
                if (analysis[index]==LOCALMINIMAL) left=index;
                if (analysis[index]==LOCALMAXIMA) { mid= index; break;}
            }
            if (index>=size) break;

            //3. find the another minimal
            for (; index<size; index++) {
                // if meet another minimal, replace the first one
                if (analysis[index]==LOCALMINIMAL) {right=index;break;}
                if (analysis[index]==LOCALMAXIMA) { if (data[index]>data[mid]) mid= index;}
            }
            if (index>=size) right=size-1;
            vhills.addElement(new Hill(left,right,mid,data[left],data[right],data[mid]));
        }
        //pick the good hills from the candidates
        pickHills(vhills);
        //make up a Hill array based on the Hill Vector;
        if (vhills.size()==0) return null;
        Hill[] hills= new Hill[vhills.size()];
        for (int i=0;i<vhills.size();i++)
            hills[i]=(Hill)vhills.elementAt(i);
        return hills;
    }
    static final int HILLTHRESHOLD=40;      //THRESHOLD% to be considered as the main area of the hill
    //hill conditions
    static final int LOK=1;          //left is OK;
    static final int LX=2;           //left is not OK;
    static final int ROK=3;          //right is OK;
    static final int RX=4;           //right is not OK;


    void pickHills(Vector vhills) {
        int size=vhills.size();
        int[] conditions= new int[size];
        for (int i=0;i<size;i++) {
            //left slope
            if (i>0) {
                Hill left_hill= (Hill) vhills.elementAt(i-1);
                Hill cur_hill= (Hill) vhills.elementAt(i);
                int lmaxpos=left_hill.mid, lmaxval=left_hill.max, lminpos=left_hill.right, lminval=left_hill.rm;
                int threshold= cur_hill.max*HILLTHRESHOLD/100;
                if (lminval>threshold) {
                    if (lmaxval<=lminval)
                        System.err.println("lmaxval<=lminval");
                        //do something
                    int newleft= (lminpos-lmaxpos)*(lmaxval-threshold)/(lmaxval-lminval)+lmaxpos;
                    if (newleft>=cur_hill.mid)
                }

    }
*/
    class Hill {
        int left,right,mid;
        int lmin,rmin,max;
        public Hill(int l, int r, int m, int lm, int rm, int mm) {left=l;right=r;mid=m;lmin=lm;rmin=rm;max=mm;}
    }
}