package yi.or;

import java.awt.*;
import yi.iu.processor.*;
import yi.iu.processor.channel.*;
import yi.iu.ui.DisplayFrame;
import yi.util.ImageUtil;
import yi.iu.processor.symmetry.*;

public class OR_car extends ImageProcessor
{
    int HORIZONTHRESHOLD= 165;   //how big the response could be considered as a horizontal response
    int HORLINEWIDTHTHRSH= 10;   //how width a horizontal line can accepted
    double sigma=2.0;

    final int DESIREDWIDTH=150;
    final double  MAXTHRESHOLD= 0.5;   //discard some maxima
    static double WIDTHTHRESHOLD= 0.6;     //determine the width
    static int TRIANGLELEN= 181;
    static int MINCARLEN=16;
    static double SHRINK=0.72;

    static int BLACK=24;			// what's the shade
    static double BLACKPER=0.6;	// how much percentage of line should be BLACK
    static int BLACKMIN=8;			// how much of line should be BLACK
    static double EDGEPER=0.2;	// how much percentage of edge should be car


    //used in maxima analysis
    static double maximaSize=0.05;   // the size from which the local maxima be considered
    static double MAXIMATHRESHOLD=0.2;



    public int[] getProcessedPseudoColor(int source[], Dimension d)
    {
        int w=d.width, h=d.height;
        int[] result= getProcessed(source, d);
//        DisplayFrame f= new DisplayFrame("final result", result, w, h);
//        f.show();
        return result;
    }

/*
    public int[] getProcessedPseudoColor(int source[], Dimension d)
    {
        int[] rst= getProcessed(source,d);
        int[] result= (int[])source.clone();

        for (int i=0;i<rst.length;i++) {
                if (rst[i]==0) continue;
                for (int h=0;h<d.height;h++) {
                        result[h*d.width+i]=0xffff0000;
                        if (i-rst[i]>0)
                                result[h*d.width+i-rst[i]]=0xffffffff;
                        if (i+rst[i]<rst.length)
                                result[h*d.width+i+rst[i]]=0xffffffff;
                }
        }
        return result;
    }
*/

    public int[] getProcessed(int source[], Dimension d)
    {
        int w=d.width, h=d.height;

        int[][] horRsp_rst= (new HorizontalRspSymCal(HORIZONTHRESHOLD, HORLINEWIDTHTHRSH, sigma)).getProcessed_ex(source, d);
        int[] in= preProcess(source, d);

        int[] hrst= horRsp_rst[0];      //histogram of number of horizontal lines
        int[] ccl= horRsp_rst[1];   	//connected component labels
        int[] ccw= horRsp_rst[2];		//connected component width

        //smooth the histogram of rst
        int[] tri= triangularFilter(hrst,DESIREDWIDTH+1);
                //getProcessedPseudoColor2(source, hrst, d);
        //getProcessedPseudoColor2(source, tri, d);

        int[] max= getLocalMaxima(tri);
        //int[] maxW= getWidthOnMaxima(max,tri);
        int[] maxW= getWidthBasedOnLongest(max,tri,ccl,ccw,d);
//        outputResult(maxW,maxW.length,1);
        int[] bottomLoc= getBottomLocation(in, maxW, d);
        int[] verLoc= getVerLocFromBtm(bottomLoc, ccl, maxW, d);
        //int[] verLoc= getVerticalLocation(ccl, maxW, d);
        int[] result=getProcessedPseudoColor3(source, maxW, verLoc, bottomLoc, d);
        //outputResult(verLoc,verLoc.length,1);

        return result;
    }

    int[] preProcess(int[] source, Dimension d)
    {
        // make intensity image
        IntensityMaker im= new IntensityMaker();
        int[] in= im.getProcessed(source,d);
        return in;
    }

    int[] triangularFilter(int[] s, int len)
    {
        int[] rst= new int[s.length];
        int[] tri= new int[len];
        int half= len/2;
        for (int i=0;i<half;i++) {
            tri[i]=tri[tri.length-i-1]=i+1;
        }
        tri[half]=half+1;
        for (int i=0;i<s.length;i++) {
            int b= i-half;
            if (b<0) b=0;
            int e= i+half;
            if (e>=s.length) e=s.length-1;
            for (int j=b; j<=e; j++) {
                int index= j-i+half;
                rst[i]+= s[j]*tri[index];
            }
        }
        return rst;
    }

    int[] getLocalMaxima(int[] s)
    {
        int[] max= yi.dp.Histogram.getLocalMaxima(s,maximaSize,MAXIMATHRESHOLD);
        int maximum=0;
        for (int i=0;i<s.length;i++) {
                if (s[i]>maximum) maximum=s[i];
        }
        for (int i=0;i<max.length;i++) {
            if (max[i]==0) continue;
            if (s[i]<(int)(maximum*MAXTHRESHOLD+0.5)) max[i]=0;
            int len= DESIREDWIDTH/4;
            int b=i-len; if (b<0) b=0;
            int e=i+len; if (e>=s.length) e=s.length-1;
            for (;b<=e;b++) {
                if (s[b]>s[i]) max[i]=0;
                if (s[b]==s[i]&&b<i) max[i]=0;// eliminate the double maxima
            }
        }
        return max;
    }

    int[] getWidthOnMaxima(int[] max, int[] tri)
    {
        int len= max.length;
        int[] w= new int[len];
        for (int i=0;i<len;i++) {
                if (max[i]==0) continue;
                int threshold= (int)(tri[i]*WIDTHTHRESHOLD+0.5);
                int j=1;
                for (;j<DESIREDWIDTH*3/2; j++) {
                        if (i-j>0) {
                                if (tri[i-j]<threshold) break;
                        }
                        if (i+j<len) {
                                if (tri[i+j]<threshold) break;
                        }
                }
                w[i]=j;
        }
        return w;
    }

    int[] getWidthBasedOnLongest(int[] max, int[] tri, int[] ccl, int[] ccw, Dimension d)
    {
        int w= d.width, h=d.height;
        int[] len= new int[w];
        for (int i=0;i<w;i++) {
                if (max[i]==0) continue;
                // find the longest horizontal line
                int longest=0, index=0;
                for (int j=0;j<h;j++) {
                        if (ccw[ccl[j*w+i]]>longest) {
                                longest=ccw[ccl[j*w+i]];
                                index=ccl[j*w+i];
                        }
                }
                // find the begin x and end x of that line
                int b=w,e=0;
                for (int y=0;y<h;y++) {
                        for (int x=0;x<w;x++) {
                                if (ccl[y*w+x]!=index) continue;
                                if (x<b) b=x;
                                if (x>e) e=x;
                        }
                }
                /*
                // adjust the symmetric line
                int sym=(e+b)/2;
                len[sym]=(e-b)/2;

                // if the len go over a minmum, then stop going so far
                if (len[sym]>DESIREDWIDTH*3/2) len[sym]=DESIREDWIDTH*3/2;
                for (int j=Math.abs(i-sym);j<len[sym]; j++) {
                        if (sym-j-1>0) {
                                if (tri[sym-j]<tri[sym-j-1]) len[sym]=j;
                        }
                        if (sym+j+1<w) {
                                if (tri[sym+j]<tri[sym+j+1]) len[sym]=j;
                        }
                }
                if (i<sym) i=sym;
                */
                // if the len go over a minmum, then stop going so far
                len[i]=(e-b)/2;
                if (len[i]>DESIREDWIDTH*3/2) len[i]=DESIREDWIDTH*3/2;
                for (int j=1;j<len[i]; j++) {
                        if (i-j-1>0) {
                                if (tri[i-j]<tri[i-j-1]) len[i]=(int)(j*SHRINK);
                        }
                        if (i+j+1<w) {
                                if (tri[i+j]<tri[i+j+1]) len[i]=(int)(j*SHRINK);
                        }
                }
        }

        return len;
    }

    int[] getProcessedPseudoColor2(int source[], int rst[], Dimension d)
    {
        int w=d.width, h=d.height;
        int[] result= new int[w*h];
        for (int i=0;i<w*h;i++) result[i]=source[i];

        int nw=d.width, nh=d.height;
        d.width=w; d.height=h;
        int num=0;
        for (int i=0;i<nw;i++)  if (num<rst[i]) num=rst[i];
        for (int i=0;i<nw;i++) {
            int temp= rst[i]*(h)/num;
            //for (int t=0; t<temp;t++)
            for (int t=h-1; t>=h-temp;t--)
                for (int j=t*w+i*(w/nw);j<t*w+(i+1)*(w/nw);j++) {
                    int r=(result[j]>>16)&0xff;
                    int g=(result[j]>>8)&0xff;
                    int b=result[j]&0xff;
                    r= r/3;
                    g= g/3;
                    b= b/3;
                    result[j]=255<<24|r<<16|g<<8|b;
                }
            if (temp>0)
                for (int j=(h-temp)*w+i*(w/nw);j<(h-temp)*w+(i+1)*(w/nw);j++)
                    result[j]=0xffffffff;
        }

        int[] max= getLocalMaxima(rst);
        for (int i=0;i<nw;i++) {
            if (max[i]==0) continue;
            for (int j=0;j<h;j++) {
                result[j*w+i*(w/nw)+1]= result[j*w+i*(w/nw)+2]= 0xffff0000;
            }
        }

        return result;
        /*
        DisplayFrame f= new DisplayFrame(result, w, h);
        String st= getClass().getName();
        f.setTitle(st);
        f.show();
        */
    }

    int[] getProcessedPseudoColor3(int source[], int[] len, int[] top, int[] v, Dimension d)
    {
        int w=d.width, h=d.height;
        int[] result= new int[w*h];
        for (int i=0;i<w*h;i++) result[i]=source[i];

        for (int i=0; i<w; i++) {
                if (len[i]==0) continue;
                int l=i-len[i]; if (l<0) l=0;
                int r=i+len[i]; if (r>=w) r=w-1;
                int t=top[i]; if (t<0) t=0;
                int b=v[i]; if (b>=h) b=h-1;
                for (int j=l; j<=r; j++) {
                        result[t*w+j]=0xff00ff00;
                        result[b*w+j]=0xff00ff00;
                        //internal frame
                        result[(t+1)*w+j]=0xff00ff00;
                        result[(b-1)*w+j]=0xff00ff00;
                }
                for (int j=t; j<=b; j++) {
                        result[j*w+l]=0xff00ff00;
                        result[j*w+r]=0xff00ff00;
                        //internal frame
                        result[j*w+l+1]=0xff00ff00;
                        result[j*w+r-1]=0xff00ff00;
                }
        }
        return result;
    }


    int[] getVerticalLocation(int[] edge, int[] maxW, Dimension d)
    {
        int w=d.width, h=d.height;
        int[] ver= new int[w];
        for (int i=0;i<w;i++) {
                if (maxW[i]==0) continue;
                int len= maxW[i]*2; if (len>h) len=h; if (len>w) len=w;
                int[] edgesum= new int[h];
                for (int j=0; j<h; j++) {
                        for (int k=i-len/2; k<=i+len/2; k++) {
                                if (k<0||k>=w) continue;
                                if (edge[j*w+k]!=0) edgesum[j]++;
                        }
                }
                int max=0;
                int cur=0;
                int index=0;
                for (int j=0; j<len ; j++)
                        max+= edgesum[j];
                cur=max;
                for (int j=1; j<h-len; j++) {
                        cur-= edgesum[j-1];
                        cur+= edgesum[j+len-1];
                        if (cur>max) { max=cur; index=j;}
                }
                ver[i]= index;
        }
        return ver;
    }


    int[] getVerLocFromBtm(int[] bottom, int[] edge, int[] maxW, Dimension d)
    {
        int w=d.width, h=d.height;
        int[] ver= new int[w];
        for (int i=0;i<w;i++) {
                if (maxW[i]==0) continue;
                ver[i]=bottom[i]-maxW[i]*2;
                if (ver[i]<0) {
                        ver[i]=0;
                        continue;
                }
                int len= maxW[i]; if (len*2>h) len=h/2; if (len*2>w) len=w/2;
                for (int j=ver[i]; j<=ver[i]+h; j++) {
                        int edgeN=0;
                        int total=0;
                        for (int k=i-len; k<=i+len; k++) {
                                if (k<0||k>=w) continue;
                                if (edge[j*w+k]!=0)
                                        edgeN++;
                                total++;
                        }
                        if (edgeN>total*EDGEPER) {
                                ver[i]=j;
                                break;
                        }
                }
        }

/*    	for (int i=0;i<w;i++) {
                if (maxW[i]==0) continue;
                int len= maxW[i]*2; if (len>h) len=h; if (len>w) len=w;
                int[] edgesum= new int[h];
                for (int j=0; j<h; j++) {
                        for (int k=i-len; k<=i+len; k++) {
                                if (k<0||k>=w) continue;
                                if (edge[j*w+k]!=0) edgesum[j]++;
                        }
                }
                int max=0;
                int cur=0;
                int index=0;
                for (int j=0; j<len ; j++)
                        max+= edgesum[j];
                cur=max;
                for (int j=1; j<h-len; j++) {
                        cur-= edgesum[j-1];
                        cur+= edgesum[j+len-1];
                        if (cur>max) { max=cur; index=j;}
                }
                ver[i]= index;
        }
        */
        return ver;
    }

    int[] getBottomLocation(int[] source, int[] maxW, Dimension d)
    {
        int w=d.width, h=d.height;
        int[] ver= new int[w];
        for (int i=0;i<w;i++) {
                if (maxW[i]==0) continue;
                int len= maxW[i]; if (len*2>h) len=h/2; if (len*2>w) len=w/2;
                for (int j=h-1; j>=0; j--) {
                        int blackN=0;
                        int total=0;
                        for (int k=i-len; k<=i+len; k++) {
                                if (k<0||k>=w) continue;
                                if (source[j*w+k]<=BLACK)
                                        blackN++;
                                total++;
                        }
                        if (blackN>total*BLACKPER&&blackN>BLACKMIN) {
                                ver[i]=j;
                                break;
                        }
                }
                // find where the shade end.
                for (int j=ver[i]; j>=0; j--) {
                        int blackN=0;
                        int total=0;
                        for (int k=i-len; k<=i+len; k++) {
                                if (k<0||k>=w) continue;
                                if (source[j*w+k]<=BLACK)
                                        blackN++;
                                total++;
                        }
                        if (blackN<total*BLACKPER||blackN<BLACKMIN) {
                                ver[i]=j;
                                break;
                        }
                }
                // if no black area found, then eliminate this candidate
                if (ver[i]==0) maxW[i]=0;

        }
        return ver;
    }

}