package edu.csusb.danby.graph;

import java.awt.geom.*;
import java.awt.*;
import java.util.*;
/**
* class PdfPlot will plot the pdf of a distribution
* given data from pdfPlotable.
* @author Charles Stanton
* @version June 20, 2001
*/
public class PdfPlot extends GraphPanel{

    // array of Points -- floats
    Point2D.Float[] plotPoints; 
    int numberPoints; //length of array
    // ***********************************************************************************
    // pLow=plotPoints[0]                                       pHigh=plotPoints[numberPoints-1]
    //                     pLowInterval       pHighInterval     
    // pLow0               pLowInterval0      pHighInterval0    pHigh0
    // ************************************************************************************
    // pLow, pHigh are endpoints (Plot2D.Float) of curve to be drawn (viewport)
    //pLow0, pHigh0 are corresponding points dropped to the x-axis
    // pLowInterval, pHighInterval are curve endpoints for interval of probability
    // pLowInterval0, pHighInterval0 are dropped to x-axis
    Point2D.Float pLow, pHigh, pLow0, pHigh0; 
    Point2D.Float pLowInterval, pLowInterval0, pHighInterval, pHighInterval0;
    // We need things in arrays to apply transformation
    Point2D.Float[] pInterval; // {pLowInterval0, pLowInterval, pHighInterval0, pHighInterval}
    float lowX, highX, lowY, highY;// For setting viewport
    float x0,x1; // x coordinates pLowInterval and pHighInterval
    //transformation variables
    float a; //horizontal scale factor
    float b; //vertical scale factor * -1
    float c; // horizontal translation
    float d; // vertical translation
    boolean inverseSelected; // for coloring choice
    float deltaX;
    Color fill1, fill2, fill3;
    Color lightBlue = new Color(240, 240, 255);
    //Color fill2 = Color.blue;
    //Color fill3 = new Color(240, 240, 255);;
    BasicStroke curveStroke, thinStroke;// for plot of pdf curve
    // pdfPlotData provides the values for the graph
    PdfPlotable pdfPlotData;
    
    /**
    * constructs <code> PdfPlot</code> from supplied aPdfPlotData
    */
    public PdfPlot(PdfPlotable aPdfPlotData){
    graphSize = new int[2];
        if (aPdfPlotData.isInverseSelected()){
            int tailChoice = aPdfPlotData.getTailChoice();
            switch (tailChoice){
                case PdfPlotable.LEFT_TAIL : 
                    fill1 = Color.red;
                    fill2 = Color.red;
                    fill3 = lightBlue;
                    break;
                case PdfPlotable.RIGHT_TAIL: 
                    fill1 = lightBlue;
                    fill2 = Color.red;
                    fill3 = Color.red;
                    break;
                case PdfPlotable.BOTH_TAILS:
                    fill1 = Color.red;
                    fill2 = lightBlue;
                    fill3 = Color.red;
                    break;
                }
            }
        else{
            fill1 = lightBlue;
            fill2 = Color.blue;
            fill3 = lightBlue;
        }
        curveStroke = new BasicStroke(4, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND);
        thinStroke = new BasicStroke(0.01f);
        pdfPlotData = aPdfPlotData;
        update(pdfPlotData);
    }

    public void update( PdfPlotable pdfPlotData){
        plotPoints = pdfPlotData.getPlotPoints();
        numberPoints = plotPoints.length;
        lowX=(float)plotPoints[0].getX();
        highX=(float)plotPoints[numberPoints-1].getX();
        lowY=pdfPlotData.getLowY();
        highY=pdfPlotData.getHighY();
        title = pdfPlotData.getTitle();
        
        deltaX = (highX-lowX)/numberPoints;
        pLow0 = new Point2D.Float(lowX, lowY);
        pHigh0 = new Point2D.Float(highX, lowY);
        pLowInterval = pdfPlotData.getLowXInterval();
        x0 = (float)pLowInterval.getX();
        if (x0 < lowX){ 
            pLowInterval = plotPoints[0];//SHOULD BE pLow 
            x0 = lowX;
            }
        if (x0 > highX){
            pLowInterval = plotPoints[numberPoints-1];//SHOULD BE pHigh
            x0 = highX;
            }
        pLowInterval0 = new Point2D.Float(x0, lowY);
        pHighInterval = pdfPlotData.getHighXInterval();
        x1 = (float)pHighInterval.getX();
        if (x1 < lowX){ 
            pHighInterval = pLow0;//SHOULD BE pLow 
            x1 = lowX;
            }
        if (x1 > highX){
            pHighInterval = pHigh0;//SHOULD BE pLow
            x1 = highX;
            }
        pHighInterval0 = new Point2D.Float(x1, lowY);
        // we put these points in an array to use the transform
        pInterval = new Point2D.Float[4];
        pInterval[0] = pLowInterval0;
        pInterval[1] = pLowInterval;
        pInterval[2] = pHighInterval0;
        pInterval[3] = pHighInterval;
    }

    /**
    * paintMethod for <code>PdfPlot</Code>
    */
    public void paint(Graphics g){
        //We must set the scale factors here because the panel width & height
        // may have changed
        panelWidth = getWidth(); 
        panelHeight = getHeight();
        graphWidth = panelWidth -(offset[LEFT]+offset[RIGHT]);
        graphHeight = panelHeight -(offset[TOP]+offset[BOTTOM]);
        graphSize[HORIZONTAL]=graphWidth;
        graphSize[VERTICAL]=graphHeight;
        a = graphWidth/(highX-lowX);// a is horizontal scale factor
        b =-graphHeight/(highY-lowY);
        c = offset[LEFT] - a*lowX;
        d = offset[TOP] +graphHeight;
        paintBackground(g);//paint background and such
        paintCurves(g);
    }



    protected void paintBackground(Graphics g){
        setBackground(backgroundColor);
        super.paint(g); 
        if (drawHorizontalAxis) drawHorizontalScale(g);
        if (drawVerticalAxis) drawVerticalScale(g);
    //	if (drawAxes) drawScales(g);
        if (drawTitle) drawTitle(g);
    }
    
    

    protected void paintCurves(Graphics g) {
        int i=0;
        Graphics2D g2 = (Graphics2D)g;
        AffineTransform tx; // to transfer real coord to screen coord
        Point2D.Float p;
        GeneralPath path1, path2, path3; 
        // enlarge array for pLow, pHigh 
        Point2D.Float[] ePlotPoints = new Point2D.Float[numberPoints+2];
        ePlotPoints[0] = pLow0;
        ePlotPoints[numberPoints+1] = pHigh0;
        System.arraycopy(plotPoints, 0, ePlotPoints, 1, numberPoints);
        
        tx = new AffineTransform(a,0,0,b,c,d);
        Point2D.Float[] transformedExtendedPlotPoints = new Point2D.Float[numberPoints+2];
        Point2D.Float[] transformedPInterval = new Point2D.Float[4];
        
        tx.transform(ePlotPoints,0,transformedExtendedPlotPoints,0, numberPoints+2);
        tx.transform( pInterval, 0,transformedPInterval, 0, 4);
        path1 = new GeneralPath();
        
        g2.setStroke(curveStroke);
        p = transformedExtendedPlotPoints[0];
        if (x0 >= lowX) {
            path1.moveTo((float)p.getX(), (float)p.getY());
            while ((i< numberPoints) && (plotPoints[i].getX() <= x0)  ){
                p = transformedExtendedPlotPoints[i+1];
                path1.lineTo((float)p.getX(), (float)p.getY());
                i++;
            }
            p = transformedPInterval[1];
            path1.lineTo((float)p.getX(), (float)p.getY());
            p = transformedPInterval[0];
            g2.setStroke(thinStroke);
            path1.lineTo((float)p.getX(), (float)p.getY());
            path1.closePath();
        }
        path2 = new GeneralPath();
        if ( (x1 > lowX) && (x0 < highX)){
            //path2 = new GeneralPath();
            //p still = transformedPAxis[0];
            path2.moveTo((float)p.getX(), (float)p.getY());
            p = transformedPInterval[1];
            path2.lineTo((float)p.getX(), (float)p.getY());
            g2.setStroke(curveStroke);
                while ((plotPoints[i].getX() < x1) && (i < numberPoints)){
                p = transformedExtendedPlotPoints[i+1];
                path2.lineTo((float)p.getX(), (float)p.getY());
                i++;
            }
            g2.setStroke(thinStroke);
            p= transformedPInterval[3];
            path2.lineTo((float)p.getX(), (float)p.getY());
            p= transformedPInterval[2];
            path2.lineTo((float)p.getX(), (float)p.getY());
            path2.closePath();
        }
            path3 = new GeneralPath();
            //p still = transformedPAxis[2];
            path3.moveTo((float)p.getX(), (float)p.getY());
            p = transformedPInterval[3];
            path3.lineTo((float)p.getX(), (float)p.getY());
            g2.setStroke(curveStroke);
            while (i < numberPoints+2){
                p = transformedExtendedPlotPoints[i];
                path3.lineTo((float)p.getX(), (float)p.getY());
                i++;
            }
        path3.closePath();
        g2.setPaint(graphColor);
        g2.draw(path1);	
        g2.draw(path2);
        g2.draw(path3);
        g2.setPaint(fill1);
        g2.fill(path1);
        g2.setPaint(fill2);
        g2.fill(path2);
        g2.setPaint(fill3);
        g2.fill(path3);
    }
    void drawVerticalScale(Graphics g){;} //NO OP OPERATION FOR NOW!!!!!!!!!!!!!

    void drawHorizontalScale(Graphics g) {	
        float[] hGrid = GraphMethods.setGrid(lowX, highX);
        //GraphMethods.paintScale(g, panelHeight-offset[BOTTOM]/2,hGrid,HORIZONTAL);
        GraphMethods.paintScale( g, panelHeight-offset[BOTTOM]/2-8, hGrid, HORIZONTAL,
                lowX, highX, offset[LEFT],  graphWidth,
                 scaleColor,   scaleFont,  fontColor,
                 labelAxes,  hAxisLabel);
    }


    public void setFillColor1(Color fillColor){ fill1 = fillColor;}
    public void setFillColor2(Color fillColor){ fill2 = fillColor;}
    public void setFillColor3(Color fillColor){ fill3 = fillColor;}
}

