package graphicsDomain;

import java.awt.*;
import java.lang.*;
/** Performs conversions between world and pixel coordinates */
public class GraphMetrics
{
    private GraphDataInGraphics graphData;
    private GraphOptionsInGraphics graphOptions;
    private double minXData,maxXData,minYData,maxYData;
    private int borderSize,xSize,ySize,currX=0,currY=0;
    private String message=""; // possible text message to draw
    private Point[] scaledPoints;
    private boolean isGraphData,isDataError;

    public GraphMetrics()
    {
	graphData = null;
	graphOptions = null;
	isGraphData = false;
	isDataError = false;
    }

    public GraphMetrics(GraphDataInGraphics gD,GraphOptionsInGraphics gO)
    {
	graphData = gD;
	graphOptions = gO;
	update();
    }

    public void update()
    {
	if (graphData!=null)
	{
            minXData=graphData.getMinX();
	    maxXData=graphData.getMaxX();
	    minYData=graphData.getMinY();
	    maxYData=graphData.getMaxY();
	    isGraphData = graphData.getIsGraphData();
	    isDataError = graphData.getIsDataError();
            if (maxXData <= minXData)
                minXData = maxXData - 1E-20; //Guard against divide by zero
	}
    }

    public void updateData(GraphDataInGraphics gD)
    {
	graphData=gD;
	update();
    }

    public void updateOptions(GraphOptionsInGraphics gO)
    {
	graphOptions = gO;
    }

    /** World to pixel in X*/
    public int getXPixel(double xVal)
    {
        return (int) ( (xVal-minXData)/(maxXData-minXData)
                      * (xSize-2*borderSize) + borderSize );
    }

    /** World to pixel in Y*/
    public int getYPixel(double yVal)
    {
        return (int) ( (yVal-minYData)/(maxYData-minYData)
		       * (2*borderSize-ySize) + ySize-borderSize );
    }

    /** Pixel to world in X*/
    public double getXValue(double xPixel)
    {
        return ( (xPixel-borderSize)/(xSize-2*borderSize)
                 * (maxXData-minXData) + minXData );
    }

    /** Pixel to world in Y*/
    public double getYValue(double yPixel)
    {
        return ( (yPixel-ySize + borderSize)/(2*borderSize - ySize)
             * (maxYData-minYData) + minYData );
    }

    /** Returns an array of (possibly filtered) pixel Point objects */
    public Point[] getPixelArray(double []xVal, double[]yVal)
    {
      int actDim = -1;
      int currXVal, currYVal, prevXVal = 0;
      for (int i = 0; i < yVal.length; i++) { //Find how many points can be filtered
        currXVal = (int) ( (xVal[i] - minXData) / (maxXData - minXData)
                          * (xSize - 2 * borderSize) + borderSize);
        if (actDim == -1 || currXVal != prevXVal) {
	    actDim++; // increment counter if x pixel value is different
            prevXVal = currXVal;
        }
      }

      if (actDim > yVal.length / 2) { //Not enough points to bother filtering
        scaledPoints = new Point[yVal.length];
        for (int i = 0; i < yVal.length; i++) {
          scaledPoints[i].x = (int) ( (xVal[i] - minXData) / (maxXData - minXData)
                                     * (xSize - 2 * borderSize) + borderSize);
          scaledPoints[i].y = (int) ( (yVal[i] - minYData) / (maxYData - minYData)
                                     * (2 * borderSize - ySize) + ySize -
                                     borderSize);
        }
        return scaledPoints;

      }
      else { //It makes sense to filter. Adjacent filtered points store max/min Y vals
        scaledPoints = new Point[2 * (actDim + 1)];
        actDim = -1;
        for (int i = 0; i < yVal.length; i++) {
          currXVal = (int) ( (xVal[i] - minXData) / (maxXData - minXData)
                            * (xSize - 2 * borderSize) + borderSize);
          currYVal = (int) ( (yVal[i] - minYData) / (maxYData - minYData)
                            * (2 * borderSize - ySize) + ySize - borderSize);
          if (actDim == -1 || currXVal != prevXVal) {
            actDim++;
            prevXVal = currXVal;
            scaledPoints[2 * actDim] = new Point(currXVal, currYVal);
            scaledPoints[2 * actDim + 1] = new Point(currXVal, currYVal);
          }
          else { // store max/min y vals
            if (currYVal < scaledPoints[2 * actDim].y)
              scaledPoints[2 * actDim].y = currYVal;
            if (currYVal > scaledPoints[2 * actDim + 1].y)
              scaledPoints[2 * actDim + 1].y = currYVal;

          }
        }
        return scaledPoints;
      }
    }

    public void setXSize(int x) {xSize=x;}
    public void setYSize(int y) {ySize=y;}
    public void setBorderSize(int b){borderSize=b;}

    public double getMinXData() { return minXData;}
    public double getMaxXData() { return maxXData;}
    public double getMinYData() { return minYData;}
    public double getMaxYData() { return maxYData;}
    public int getXSize() {return xSize;}
    public int getYSize() {return ySize;}
    public int getBorderSize(){return borderSize;}

    public void setIsGraphData(boolean isGraphData) {this.isGraphData = isGraphData;}
    public boolean getIsGraphData() {return isGraphData;}
    public boolean getIsDataError() {return isDataError;}

    public GraphOptionsInGraphics getGraphOptions() {return graphOptions;}
    public GraphDataInGraphics getGraphData() {return graphData;}

    /** Message string for writing on graph */
    public String getMessage() { return message;}
    public void setMessage(String _message) { message = _message;}

    /** Current XY positions of cursor */
    public void setCurrX(int xPixel) { currX = xPixel;}
    public int getCurrX() { return currX;}
    public void setCurrY(int yPixel) { currY = yPixel;}
    public int getCurrY() { return currY;}

}
