package  edu.csusb.danby.util;

import java.text.NumberFormat;
import java.awt.geom.*;
import java.awt.*;
/**
* static methods for graph classes
*/
public class GraphMethods{
    final static double log10 = Math.log(10.0);
    //constants for layout
    public static final int HORIZONTAL=0;
    public static final int VERTICAL =1;
    public static final int LOW =0;
    public static final int HIGH =1;
/*
* try to determine a reasonable number of decimal places
* to show in labels
*/
    public static int setMaxFractionDigits( double max, double min){
        double spreadData, logSpreadData;
        int maxFractionDigits;
        long spreadPower;
        spreadData = max-min;

        logSpreadData = Math.log(spreadData)/log10;
        spreadPower = (long)Math.floor(logSpreadData);
        maxFractionDigits =(int) ( 2-1*spreadPower);
        return maxFractionDigits;
    }
   
   
   
    /**
    * Find good grid values for scales and grid lines
    * @lowValue is the lower endpoint of the scale
    * @highValue is the upper endpoint of the scale
    * @graphPixels is the dimension of the scale in pixel space
    * @return an array of floats to draw the grid at
    */
    public static float[] findGrid(double lowValue, double highValue, int graphPixels){
        int increment;
        int incrementFactor=1; // increase increment for small graphs
        if (graphPixels<180){ incrementFactor=2;}
    

        //first we scale so that there are between 10 and 100 integer values
        //in the graphing range
        int pow10 = (int)Math.floor(2-Math.log(highValue-lowValue)/log10);
        double scale10 = Math.exp(log10*pow10);
        int iScaledLow= (int)(lowValue*scale10);
        int iScaledHigh = (int)(highValue*scale10);

        //Now we reduce the lines, keeping multiples of 2, 5, or 10
        int numberLines = iScaledHigh - iScaledLow;

        if (numberLines>= 80){increment =10*incrementFactor;}
        else if (numberLines >= 40) {increment = 5*incrementFactor;}
        else if (numberLines >= 16 ) {increment =2*incrementFactor;}
        else {increment =1*incrementFactor;}
        
        
        //adjust the starting value to be a multiple of the increment
        //We want a positive remainder, so the grid point is in the range
        int iAdj = (iScaledLow <0) ?iScaledLow%increment+increment:iScaledLow%increment;
        int initPosition = (iAdj==0)?iScaledLow:iScaledLow-iAdj+increment;
        iAdj = (iScaledHigh<0)? iScaledHigh%increment:iScaledHigh%increment-increment;
        int finalPosition = iScaledHigh+iAdj;

        //recount the number of grid points
        numberLines = (finalPosition -initPosition)/increment+1;

        //now calculate the grid points
        int iPosition = initPosition;
        float[] grid = new float[numberLines];
        for (int i= 0; i< numberLines; i++){
            grid[i] = (float)(iPosition/scale10);
        iPosition += increment;
        }
        return grid;
    }
    
    
    /**
    * Before using the grid to paint a vertical grid, need to scale locally
    */
    public static void paintVerticalGrid(Graphics g, double[] scaledVGrid,
                                int offsetLeft, int graphWidth, Color gridColor ) {
        int vLength = scaledVGrid.length;
        Line2D.Double line=new Line2D.Double(); // to draw various lines for box plot
        Graphics2D g2 = (Graphics2D)g;
        g2.setColor(gridColor);
        //paint horizontal lines at vertical grid marks
        for (int i=0; i< vLength; i++){
                line.setLine(offsetLeft, scaledVGrid[i],offsetLeft+graphWidth,scaledVGrid[i]);
                g2.draw(line);
        }
    }	
    
    /**
    * static method to paint a reasonable scale on a graph
    * grid should be found be a call to findGrid above
    * @param g the Graphics object of the graph
    * @param gi GraphInfo object
    * @param direction one of VERTICAL or HORIZONTAL
    * @param labelAxes whether to label axes
    */
    public static void paintScale(Graphics g, GraphInfo gi,  int direction,
                boolean labelAxes) {
        int othogonalDirection = (direction == VERTICAL)?HORIZONTAL:VERTICAL;
        //FIND various quantities from the GraphInfo object
        //grid has float coordinates (in real space) of grid lines
        float[] grid = gi.getGrid(direction);
        int scalePosition = gi.getScalePosition(direction);
        double lowValue = gi.getMin(direction);
        double highValue = gi.getMax(direction);
        int offset = gi.getOffset(direction);
        int graphSize = gi.getGraphSize(direction);
        Color scaleColor = gi.getScaleColor(direction);
        Color fontColor = gi.getScaleFontColor(direction);
        Font f = gi.getScaleFont(direction);
        String axisLabel = gi.getScaleLabel(direction);
        //Component comp = gi.getComponent();
        
        int labelWidth;
        
        //iGrid has the actual pixel coordinates of grid lines		
        int[] iGrid= new int[grid.length];
        NumberFormat nf = NumberFormat.getNumberInstance();
        for (int i=0; i< grid.length; i++){
            iGrid[i] = scale(grid[i], lowValue, highValue, direction, offset, graphSize);
        }
        String scaleLabel;
        g.setFont(f);
        FontMetrics fm = gi.getComponent().getFontMetrics(f);
        int maxFractionDigits = 
            GraphMethods.setMaxFractionDigits(highValue,lowValue);
        nf.setMaximumFractionDigits(maxFractionDigits);
        g.setColor(scaleColor);
        if (direction == HORIZONTAL){
            g.drawLine(offset, scalePosition, 
                offset+graphSize,scalePosition);
            for (int i=0; i <iGrid.length; i++){
                g.drawLine( iGrid[i], scalePosition-5, 
                    iGrid[i], scalePosition+5);
                scaleLabel = nf.format(grid[i]);
                g.setColor(fontColor);
                g.drawString(scaleLabel,iGrid[i]-5,scalePosition+20); 		
            }
            if (labelAxes) {
                g.setColor(fontColor);
                labelWidth = fm.stringWidth(axisLabel);
                g.drawString(axisLabel, offset+graphSize-(labelWidth+10)
                                        , scalePosition-20);
            }
        }
        else {  // direction == VERTICAL
            g.drawLine(scalePosition, offset, 
                scalePosition, offset+graphSize);
            for (int i=0; i <iGrid.length; i++){
                g.drawLine(scalePosition-5, iGrid[i],
                     scalePosition+5, iGrid[i]);
                scaleLabel = nf.format(grid[i]);
                g.setColor(fontColor);
                g.drawString(scaleLabel,scalePosition-25,iGrid[i]+6); 
            }
            if (labelAxes){
                g.setColor(fontColor);
                //labelWidth = fm.stringWidth(axisLabel);
                g.drawString(axisLabel, scalePosition+5,offset+10);
            }
        }

    }

   
    //offset should be topOffset for vertical scaling, leftOffset for horizontal
    protected static int scale(double value, double lowValue, double highValue, int direction, int offset, int graphSize){
        int nValue;
        nValue = (int)((value-lowValue)
            *graphSize/(highValue-lowValue));
        if (direction==VERTICAL){nValue = graphSize-nValue+offset;}
        else {nValue = nValue+offset;}
        return nValue;
    }
}
    
