import java.awt.Font;
import java.awt.Color;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.LinkedList;
import javax.imageio.ImageIO;

public class Graph extends Canvas {
	
	private float[][] graphData;
	private float graphWidth, graphHeight, graphX, graphY;
	private int pixelWidth=640,pixelHeight=480;
	private boolean badAssError = false;
	
	
	/** Constructor for Graph, specify inputdata
	 *  User must call save() or paint() after constructing.
	 *  @param data The input data(float[][]) first column are the x-values/time
	 *  @see save();
	 */  
	Graph(float[][] data)
	{
		graphData = data;
		removeErrorColumns();
		if(data.length>1)
		{
			graphData = data;
			calcDimensions();
		}
		else
		{
			System.out.println("Graph Error: Graph needs at least two columns");
		}
	}
	/** Constructor for Graph, specify inputdata
	 *  User must call save() or paint() after constructing.
	 *  @param data The input data(BeatSet) first column are the x-values/time
	 *  @see save();
	 *
	 */  
	Graph(BeatSet data)
	{
		graphData = data.getBeatsDataInArrayForm();
		removeErrorColumns();
		calcDimensions();
	}	
	/** Private method to remove invalid Columns 
	 */
	private void removeErrorColumns()
	{
		for(int i=0;i<graphData.length;i++)
		{
			try
			{
				float test = (float) graphData[i][0];
					  test = (float) graphData[i][graphData[i].length-1];
			}
			catch(Exception e)
			{	
				if(i==0)
				{
					System.out.println("Graph Error: First Column(containing the time/x values) is invalid...No graph wil be drawed");
					badAssError = true;
					return;
				}
				else
				{
					removeColumn(i);
					System.out.println("Graph Error: Invalid data in column nr. " + i + " ...Column removed");
				}
				
			}
		}
	}
	
	/** Private method to calculate the x,y,heights,widths values of the graph
	 */	
	private void calcDimensions()
	{
			if(badAssError)
				return;
			float widthMinMax[]  = getMinMax(graphData[0]);
			float heightMinMax[] = getMinMax(graphData[1]);
			
			for(int i=2;i<graphData.length;i++)
			{
				float current[] = getMinMax(graphData[i]);
				if(heightMinMax[0]>current[0])
					heightMinMax[0] = current[0];
				if(heightMinMax[1]<current[1])
					heightMinMax[1] = current[1];
			}
			
			graphWidth  = widthMinMax[1]  - widthMinMax[0];
			graphHeight = heightMinMax[1] - heightMinMax[0];
			graphX      = widthMinMax[0]  + (graphWidth/2);
			graphY      = heightMinMax[0] + (graphHeight/2);
		
			pixelWidth  = this.getWidth();
			pixelHeight = this.getHeight();
			
			if(pixelWidth<=0)
				pixelWidth = 640;	
			if(pixelHeight<=0)
				pixelHeight = 480;
	}
	
	/** Public method to add a column(graph line) to the graph
	 *	@param data The inpute data(float[]) for the column, must be of same length as the other columns
	 */
	public void addColumn(float[] data)
	{
		if(data.length == graphData[0].length)
		{
			float[][] tempAr = new float[graphData.length+1][graphData[0].length];
			for(int i=0;i<graphData.length;i++)
			{
				tempAr[i] = graphData[i];
			}
			tempAr[tempAr.length-1]=data;
			graphData = tempAr;	
		}
		else
		{
			System.out.println("Graph Error: The new column must have a length of: " + graphData[0].length);
		}
		removeErrorColumns();
		calcDimensions();		
	}
	
	/** Public method wich returns the length of the columns
	 *	
	 */
	public int getColumnLength()
	{
		if(graphData.length>=1)
			return graphData[0].length;
		else 
			return 0;
	}
	
	/** Public method to remove a column(graph line) from the graph
	 *	@param col The column number(int) to remove
	 */
	
	public void removeColumn(int col)
	{
		float[][] tempAr = new float[graphData.length-1][graphData[0].length];
		for(int i=0;i<tempAr.length && i< col;i++)
		{
			tempAr[i] = graphData[i];
		}
		for(int k=col;k<tempAr.length;k++)
		{
			tempAr[k] = graphData[k+1];
		}
		graphData = tempAr;			
	}
	
	
	/** Public method wich returns the data Array
	 *	
	 */
	
	public float[][] getArray()
	{
		return graphData;
	}

	/** Public method wich updates the graph
	 **	@param g The graphics object
	 **/
	
	public void update(Graphics g)
    {   
 		paint(g);
	}
	
	/** Public method wich paints the graph on a Graphics object
	 *	@param g The graphics object
	 */
	public void paint(Graphics g)
	{	
		BufferedImage image = drawGraph();
		g.drawImage(image,0,0,null);
	}
	
	
	/** Public method to save the graph-image to the harddrive as a *.png file
	 *	@param path The path to save the file
	 */
	public void save(String path)
	{
		BufferedImage image = drawGraph();
		try
		{
			ImageIO.write(image, "png", new File(path));
		}
		catch(Exception e)
		{
			System.out.println("Graph Error: Error saving Graph at: " + path);
			return;
		}
		System.out.println("Graph Saved at: " + path);
	}
	
	/** Public method to zoom the graph
	 *	@param x The graph-x coordinate 
	 *	@param y The graph-y coordinate 
	 *	@param width The graph-width of the graph
	 *	@param height The graph-height of the graph
	 */
	public void zoom(float x, float y, float width, float height)
	{
		graphWidth  = width;
		graphHeight = height;
		graphX		= x;
		graphY		= y;
	}
	/** Public method to zoom the graph
	 *	@param x The graph-x coordinate 
	 *	@param y The graph-y coordinate 
	 *	@param factor The zoom factor
	 */
	public void zoom(float x, float y, float factor)
	{
		graphX		 = x;
		graphY		 = y;
		graphWidth  /= factor;
		graphHeight /= factor;
	}
	
	/** Public method to zoom(window) the graph
	 *	@param xMin The minimum x coordinate 
	 *	@param yMin The minimum y coordinate 
	 *	@param xMax The maximum x coordinate
	 *	@param yMax The maximum y coordinate
	 */	
	public void zoomWindow(float xMin, float xMax, float yMin, float yMax)
	{
		graphWidth   = xMax - xMin;
		graphHeight  = yMax - yMin;
		graphX		 = xMin + (graphWidth/2);
		graphY		 = yMin + (graphHeight/2);
	}	
	
	/** Public method wich returns the graph value 
	 *	@param pixelX The pixel-x coordinate
	 */	
	public float getGraphX(int pixelX)
	{	
		float factor 	= (graphWidth/pixelWidth);
		int nulX 		= getPixelX(0);
		return ((pixelX - nulX)*factor);	
	}
	
	/** Public method wich returns the graph value 
	 *	@param pixelY The pixel-y coordinate
	 */	
	public float getGraphY(int pixelY)
	{
		float factor 	= (graphHeight/pixelHeight);
		int nulY 		= getPixelY(0);
		return (( nulY - pixelY)*factor);	
	}
	
	/** Public method wich returns the pixel value 
	 *	@param graphX The graph-x coordinate
	 */	
	public int getPixelX(float grX)
	{
		float leftX   = graphX-(graphWidth/2);
		float factorX = pixelWidth/graphWidth;
		return Math.round((factorX * grX)-(leftX*factorX));
	}
	
	/** Public method wich returns the pixel value 
	 *	@param graphY The graph-y coordinate
	 */	
	public int getPixelY(float grY)
	{
		float topY    = graphY-(graphHeight/2);
		float factorY = pixelHeight/graphHeight;
		return (pixelHeight - Math.round((factorY * grY)-(topY*factorY))); 	
	}
	
	/** Private method wich draws the  graph on an BufferedImage 
	 */		
	private BufferedImage drawGraph()
	{
		if(badAssError)
		{
			return new BufferedImage(1, 1 , BufferedImage.TYPE_INT_RGB);
		}
		pixelWidth  = this.getWidth();
		pixelHeight = this.getHeight();
		
		if(pixelWidth<=0)
			pixelWidth = 640;	
		if(pixelHeight<=0)
			pixelHeight = 480;
		
		BufferedImage image = new BufferedImage(pixelWidth, pixelHeight , BufferedImage.TYPE_INT_RGB);
		Graphics gr = image.getGraphics(); 
		gr.setColor(new Color(255,255,255));
		gr.fillRect(0,0,pixelWidth,pixelHeight);
		
		for(int k=1;k<graphData.length;k++)
		{
			gr.setColor(new Color(50000/(k+1)*100));
			int lastX = getPixelX(graphData[0][0]);
			int lastY = getPixelY(graphData[k][0]);
			
			for(int i=0;i<graphData[0].length;i++)
			{
				int curX = getPixelX(graphData[0][i]);
				int curY = getPixelY(graphData[k][i]);
				gr.drawLine(lastX,lastY,curX,curY);
				//System.out.println("lijn:" + lastX + "," + lastY + "," + curX+ "," +curY);
				lastX = curX;
				lastY = curY;
			}
		}
		gr.setColor(new Color(0,0,0));
		
		int xValue = getPixelX(0);
		int yValue = getPixelY(0);
		
		gr.drawLine(xValue,0,xValue,pixelHeight);
		gr.drawLine(0,yValue,pixelWidth,yValue);
		
		if (xValue<0 || xValue>pixelWidth-10)
			xValue = 0;
		if (yValue<0 || yValue>pixelHeight-10)
			yValue = 0;
	
		//x as:
		gr.setFont(new Font("arial",0, 9));
		int difference=0;
		float tel=(float)0.5;
		while(difference<35)
		{
			difference = Math.abs(getPixelX(0)-getPixelX(tel));
			tel+=0.5;	
		}
		
		float step  = getGraphX(getPixelX(0)+difference);
		float begin = getGraphX(getPixelX(0)%difference);
		
		//System.out.println("dif: "+difference+" nul:" + getGraphX(0) + "  " + getGraphX(pixelHeight) + " step:" + step + " begin:" + begin);
				
		for(float i=begin;i<=graphWidth+step;i+=step)
		{
			gr.drawLine(getPixelX(i),yValue-1,getPixelX(i),yValue+1);
			gr.drawString(Math.round(i)+"",getPixelX(i),yValue+10);	
		}
		
		//y as
		difference=0;
		tel=(float)0.5;
		while(difference<35)
		{
			difference = Math.abs(getPixelY(0)-getPixelY(tel));
			tel+=0.5;	
		}
		step  = getGraphY(getPixelY(0)+difference);
		begin = getGraphY(pixelHeight)-(getGraphY(pixelHeight)%step);
		
		//System.out.println("dif: "+difference+" nul:" + getGraphY(0) + "  " + getGraphY(pixelHeight) + " step:" + step + " begin:" + begin);
		
		for(float i=begin;i<=getGraphY(0);i-=step)
		{
			gr.drawLine(xValue-1,getPixelY(i),xValue+1,getPixelY(i));
			gr.drawString(Math.round(i)+"",xValue+5,getPixelY(i));	
		}
		
		return image;
	}
	
	/** Public method wich returns an array with the min[0] and max[1] value of an array
	 *	@param data The input array
	 */	
	public float[] getMinMax(float data[])
	{
		float result[] = new float[2];
		float first = (float) data[1];
		
		result[0] = first;     //[0] is laagste waarde en [1] is hoogste waarde
		result[1] = first;
		 
		for(int i=1;i<data.length;i++)
		{
			float current = data[i];
			//System.out.println("**:" + i + " -" + current);	
			if(result[0]>current)
				result[0]=current;
			if(result[1]<current)
				result[1]=current;
		} 
		return result;
	}	
}
/*
	Graph(BeatSet data)
	{
		LinkedList beats = data.getBeats();
		graphData = new float[15][beats.size()];
		Iterator it = beats.iterator();
		int i = 0;
		while(it.hasNext())
		{
			Beat curBeat     = (Beat) it.next();
			graphData[0] [i] = curBeat.T;
			graphData[1] [i] = curBeat.Syst;
			graphData[2] [i] = curBeat.Dias;
			graphData[3] [i] = curBeat.Mean;
			graphData[4] [i] = curBeat.Rat;
			graphData[5] [i] = curBeat.IBI;
			graphData[6] [i] = curBeat.Vol;
			graphData[7] [i] = curBeat.CO;
			graphData[8] [i] = curBeat.EJT;
			graphData[9] [i] = curBeat.Tpr;
			graphData[10][i] = curBeat.Artifact;
			graphData[11][i] = curBeat.Z0;
			graphData[12][i] = curBeat.Cw;
			graphData[13][i] = curBeat.Puls;
			graphData[14][i] = curBeat.Hgt;
			i++;
		}
		removeErrorColumns();
		//System.out.println("efrs"+graphData.length);
		if(graphData.length>1)
			calcDimensions();
		else
			System.out.println("Graph Error: Graph needs at least two columns");
	}
	*/
