import java.applet.* ;
import java.awt.* ;
import java.net.* ;
import java.io.* ;
import java.util.Date ;

public class tcgraph extends Applet implements Runnable {
	// First we write everything to bufferimage.  After that, that buffered image is written to the screen.  We can use bufferg to write to this image.
	private Image bufferimage ;
	private Graphics bufferg ;

	private Dimension d ;		// Size of the applet
	private Color backgroundcolor = new Color (255,255,255) ; //  background color of the graph

	private int fontSize = 12 ;
	private Font font ;
	private FontMetrics fontMetrics ;
	private int leftSpace ;		// The width of the white area on the left of the chart (calculated as the width of 7 characters)

	private int speed = 3 ;		// in seconds, time between updates

	private Color[] lineColor  ;	// Array with colors
	private long[][] Data  ;	// Matrix with fetched counters
	private int[][] height  ;	// Matrix with calculated heights (the heights of the lines in the graph)
	private long[] preData ;	// Array with previous fetched counter
	private long[] newData ;	// Array with new fetched counter
	private long[] calData ;	// Array with calculated counter (pre - new)
	private int numCounters = 0 ;	// Number of counters we fetched
	private long newTime ;		// Time in milli seconds when we fetched the previous counter
	private long preTime ;		// Time in milli seconds when we fetched the current counter

	private int index = 0 ;		// place in the array that we are fetching

	private int type = 0 ;		// Type of graph we are plotting

	private int gridSize = 10 ;	// width in pixels for the grid
	private int counterGrid = 10 ;	// Number of counters we place between two grid lines
	private int step ;		// distance in pixels between two counters (calculated as gridSize / counterGrid )
	private int maxDataSize ; 	// Number of counter we can place on the graoh (graphWidth / gridSize * counterGrid)

	private int rightBorder = 0 ;	// # pixels between right side of the applet and the chart

	private int LegendBottomHeight ;// Calculated as twice the heigth of the font
	private int graphXoff ;		// # pixels between top of applet and the chart
	private int graphYoff ;		// # pixels between left side of applet and the chart
	private int graphWidth ;	// width of the graph
	private int graphHeight ;	// height of the graph
	private long chartMax ;		// Maximum of the data
	private long chartMin ;		// Minimum of the data
	private int i ;
	private int j ;

	private String dataSource = "http://scne/cgi-bin/rand.pl" ;

	public Thread graphthread = null ;

	public void init () {
		String param = getParameter("dataSource") ;
		if (param != null)
			dataSource = param ;

		param = getParameter("gridSize") ;
		if (param != null)
			gridSize = Integer.parseInt(param) ;

		param = getParameter("counterGrid") ;
		if (param != null)
			counterGrid = Integer.parseInt(param) ;

		param = getParameter("type") ;
		if (param != null)
			type = Integer.parseInt(param) ;

		param = getParameter("speed") ;
		if (param != null)
			speed = Integer.parseInt(param) ;

		// Calculate the width of the white area on the left of the chart
		// We take the size of 7 characters
		font = new Font("Arial", Font.BOLD, fontSize) ;
		setFont(font) ;
		fontMetrics = getFontMetrics(font) ;
		leftSpace = fontMetrics.stringWidth("1234567") ;

		// Calculate the # data values per grid line
		// We want gridSize as a multiple of counterGrid
		step = gridSize / counterGrid ;
		gridSize = counterGrid * step ;

		// Get the size of the applet 
		d = getSize () ;

		// How many space do we have at the bottom of the chart ?
		LegendBottomHeight = fontSize * 2 ;

		// Calculate the size of the graph
		graphXoff = leftSpace ;
		graphYoff = fontSize / 2 ;  // Space we left open at the top of the chart
		graphWidth = d.width - leftSpace - rightBorder ;
		graphHeight = d.height - LegendBottomHeight ;

		graphWidth = graphWidth / gridSize * gridSize ;	// Make graphWidth q multiple of gridSize
		maxDataSize = graphWidth / step ;			// How many data points can we store on the graph

		// initialize the array with counters
		preData = new long[10];
		newData = new long[10];
		calData = new long[10];
		Data    = new long[10][maxDataSize];
		height  = new  int[10][maxDataSize];

		lineColor = new Color[10] ;
		lineColor[0] = Color.green  ;
		lineColor[1] = Color.yellow ;
		lineColor[2] = Color.blue   ;
		lineColor[3] = Color.green  ;
		lineColor[4] = Color.yellow ;
		lineColor[5] = Color.blue   ;
		lineColor[6] = Color.green  ;
		lineColor[7] = Color.yellow ;
		lineColor[8] = Color.blue   ;
		lineColor[9] = Color.green  ;

		// initializee the buffered image
		bufferimage = createImage (d.width,d.height) ;
		bufferg = bufferimage.getGraphics() ;
	}

	// synchronized : maks sure only 1 thread can paint the applet
	public synchronized void paint (Graphics g) {
		if ( bufferg != null ) { // make sure the buffer image is initialized
			// Fill the image with a color
			bufferg.setColor (backgroundcolor) ;
			bufferg.fillRect (0,0,d.width,d.height) ;

			// Get a new data value
			try {
				URL theURL = new URL(dataSource) ;
				String tempString ;
				// The first time we fetch a counter, we have to fetch a second one so we can calculate the difference
				if ( index < 1 ) {
					numCounters = 0 ;
					Date dtemp = new Date() ;
					preTime = dtemp.getTime () ;
					BufferedReader input = new BufferedReader (new InputStreamReader(theURL.openStream())) ;
					while ((tempString = input.readLine()) != null) {
						preData[numCounters] = Long.parseLong(tempString) ;
						numCounters ++ ;
					}

					input.close();
					try { Thread.sleep(speed * 1000) ;
					} catch (InterruptedException e) {
						System.out.println("Exception: " + e) ;
					}
				}

				Date d = new Date() ;
				newTime = d.getTime () ;
				BufferedReader input = new BufferedReader (new InputStreamReader(theURL.openStream())) ;
				for ( i=0; i<numCounters; i++ ) {
					if ((tempString = input.readLine()) != null) {
						newData[i] = Long.parseLong(tempString) ;
					}
				}

				int timediff = Integer.parseInt(Long.toString(newTime-preTime)) ;
				for ( i=0; i<numCounters; i++ ) {
					calData[i] = ( (newData[i] - preData[i] )  * 1000 / timediff ) ;
					preData[i] = newData[i] ;
				}
				preTime = newTime ;
				input.close();

			} catch (MalformedURLException e) {
				System.out.println("Exception: " + e);
			} catch (IOException e) {
				System.out.println("Exception: " + e);
			}

			// Update the array
			for ( i=0; i<numCounters; i++ ) {
				if ( calData[i] < 0 ) 
					calData[i] = - calData[i] ;
				if ( index == maxDataSize-1 ) {
					// shift the array to the left
					for ( j=0; j<index; j++ ) {
						Data[i][j] = Data[i][j+1] ;
					}
				}
				Data[i][index] = calData[i] ;
			}
			if ( index < maxDataSize-1 ) {
				index ++ ;
			}

			// Draw the left axis
			chartMax = findMax () ;
			chartMin = findMin () ;
			bufferg.setColor(Color.black);
			if ( chartMax == chartMin ) { 
				bufferg.drawString (""+chartMin, leftSpace - fontMetrics.stringWidth(""+chartMin), graphHeight/2) ;
			} else {
				bufferg.drawString (""+chartMax, leftSpace - fontMetrics.stringWidth(""+chartMax), fontSize) ;
				long temp = ((chartMax-chartMin)/2+chartMin) ; // find the middle of the graph
				bufferg.drawString (""+temp, leftSpace - fontMetrics.stringWidth(""+temp), graphHeight/2+fontSize) ; // and draw the number we found on the left axis
				bufferg.drawString (""+chartMin, leftSpace - fontMetrics.stringWidth(""+chartMin), graphHeight+fontSize) ;
			}

			// Draw the grid
			bufferg.setColor(Color.lightGray) ;
			bufferg.drawLine (graphXoff, graphYoff, graphXoff+graphWidth, graphYoff) ;
			bufferg.drawLine (graphXoff, graphYoff+graphHeight/2, graphXoff+graphWidth, graphYoff+graphHeight/2) ;
			bufferg.drawLine (graphXoff, graphYoff+graphHeight, graphXoff+graphWidth, graphYoff+graphHeight) ;
			for (i=0; i<=graphWidth; i+=gridSize ) 
				bufferg.drawLine (graphXoff+i,graphYoff,graphXoff+i,graphYoff+graphHeight) ;

			// Draw the data
			if ( type == 0 ) {
				for ( i=0; i<numCounters; i++ ) {
					bufferg.setColor(lineColor[i]);
					for ( j=1; j<index; j++ ) {
						if ( chartMax == chartMin ) { 
							height[i][j] = 0 ;
						} else {
							height[i][j] = Integer.parseInt(Long.toString ( ( Data[i][j] - chartMin ) * graphHeight / ( chartMax - chartMin ) ) ) ;
						}
						bufferg.fillRect (graphXoff+((j-1)*step),graphYoff+graphHeight-height[i][j],step,height[i][j]) ;
					}
				}
			} else {
				int height1 = 0 ;
				int height2 = 0 ;
				for ( i=0; i<numCounters; i++ ) {
					bufferg.setColor(lineColor[i]);
					for ( j=1; j<index; j++ ) {
						if ( chartMax != chartMin ) { 
							height1 = Integer.parseInt(Long.toString ( ( Data[i][j-1] - chartMin ) * graphHeight / ( chartMax - chartMin ) ) ) ;
							height2 = Integer.parseInt(Long.toString ( ( Data[i][j]   - chartMin ) * graphHeight / ( chartMax - chartMin ) ) ) ;
						} else {
							height1 = graphHeight / 2 ;
							height2 = graphHeight / 2 ;
						}
						bufferg.drawLine (leftSpace+((j-1)*step),graphYoff+graphHeight-height1,leftSpace+(j*step),graphYoff+graphHeight-height2) ;
					}
				}
			}

			// Draw the current value in red
			bufferg.setColor(Color.red) ;

			// Create the text we want to draw
			String text = "" ;
			for ( i=0; i<numCounters; i++ ) {
				text += calData[i] + " " ;
			}

			int textWidth ; // the width of the text we want to draw
			// Calculate the start of the legend so the legend fits on the screen
			if ( leftSpace + index*step - fontMetrics.stringWidth(text)/2 > graphWidth ) { // The text reaches the right border of the chart
				textWidth = leftSpace + index*step - fontMetrics.stringWidth(text)  ;
			} else {
				if ( leftSpace + index*step - fontMetrics.stringWidth(text)/2 < 0 ) { // The text reaches the left border of the chart
					textWidth = 0 ;
				} else {
					textWidth = leftSpace + index*step - (fontMetrics.stringWidth(text)/2) ; // Center the text
				}
			}
			bufferg.drawString (text,textWidth,graphHeight + LegendBottomHeight - fontSize / 2 ) ; // Add the text
			bufferg.drawLine (leftSpace+((index-1)*step),graphYoff,leftSpace+((index-1)*step),graphYoff+graphHeight) ; // Add a red horizontal line

			g.drawImage (bufferimage,0,0,this) ;  // Draw the buffered image on the screen
		}
	}

	// // // // After this line is nothing usefull anymore

	public void start () {
		if ( graphthread == null ) {
			graphthread = new Thread (this,"graphthread") ;
			graphthread.start () ;
		}
	}

	public void stop  () {
		if (( graphthread != null) && graphthread.isAlive() )
			graphthread.stop () ;
		graphthread = null ;
	}

	public void run () {
		while (true) {
			repaint () ;
			try { Thread.sleep (speed*1000) ; }
			catch (InterruptedException e) { }
		}
	}

	// overwrite the update method so the applet is not blanked with grey
	// synchronized : make sure only 1 thread can update the applet
	public synchronized void update (Graphics g) {
		paint (g) ;
	}

	// method to read a parameter as integer
	public int getIntegerParameter (String name) {
		String value  = getParameter (name) ;
		int intvalue ;
		try { intvalue = Integer.parseInt (value); }
		catch (NumberFormatException e) { return 5; }
		return intvalue ;
	}

	private long findMax() {
		long max = Long.MIN_VALUE ;
		for ( i=0; i<numCounters; i++ ) { for ( j=0; j<index; j++ ) {
			if ( Data[i][j] > max ) {
				max = Data[i][j] ;
			} }
		}
		return max ;
	}

	private long findMin() {
		long min = Long.MAX_VALUE ;
		for ( i=0; i<numCounters; i++ ) { for ( j=0; j<index; j++ ) {
			if ( Data[i][j] < min ) {
				min = Data[i][j] ;
			} }
		}
		return min ;
	}
}
