/*
 * Graph.java
 *	  A simple graphing program
 *
 * Ken Shirriff	 ken.shirriff@eng.sun.com
 *
 * Copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * Please refer to the file "license.txt"
 * for further important copyright and licensing information.
 */

import java.awt.*;
import java.util.StringTokenizer;
import java.awt.image.*;

class Graph extends Canvas {
	int r,c;
	int topmarg = 20;
	int rightmarg = 20;
	int xmarg = 50;
	int ymarg = 20;
	double data[][];
	int st=1; // Y label step size in units
	int nstp = 0; // Number of steps on Y axis
	int zerostp = 0; // Step number of X axis
	Color colors[] = null;
	int numcolors = 0;
	int xlabel=0;
	boolean log=false;
	boolean fixedRange = false;
	double fixedmin, fixedmax;
	Font font = null;
	int tickw = 3;
	boolean stack = false;

	public void setFont(Font f) {
		xmarg = getFontMetrics(f).stringWidth("999999")+tickw;
		font = f;
	}

	void setStack(boolean flag) {
		stack = flag;
		repaint();
	}

	void setRange(double min, double max) {
		fixedmin = min;
		fixedmax = max;
		fixedRange=true;
	}

	void unsetRange() {
		fixedRange = false;
	}

	void setLog(boolean v) {
		log = v;
		scaleData();
		repaint();
	}

	void setData(double indata[][], int xl, int inr, int inc) {
		r = inr;
		c = inc;
		xlabel = xl;
		data = new double[r][c];
		for (int i=0;i<inr;i++) {
			for (int j=0;j<inc;j++) {
				data[i][j] = indata[i][j];
			}
		}
		scaleData();
		repaint();
	}

	void setColors(Color incolors[], int innum) {
		numcolors = innum;
		colors = incolors;
	}

	// Given max n, we want a reasonable step size
	int step(int n) {
		int s;
		for (s=1;s<n && s<1000000000;s*=10) { }
		// S = power of 10 >= n
		s /= 10;
		if (s>10 && n/(s/10)<12) {
			// steps of 1
			s /= 10;
		} else if (s>5 && n/(s/5)<12) {
			// steps of 2
			s /= 5;
		} else if (s>2 && n/(s/2)<12) {
			// steps of 5
			s /=2;
		}
		if (s==0) {
			s=1;
		}
		return s;
	}

	double val(double d) {
		if (log) {
			if (d==0) {
				return 0;
			} else {
				return Math.log(d)/Math.log(10.);
			}
		} else {
			return d;
		}
	}


	void scaleData() {
		double ymin=99999999, ymax = -9999999999;
		double maxv=0;
		if (fixedRange) {
			ymin = fixedmin;
			ymax = fixedmax;
		} else if (stack) {
			double top;
			ymin =0;
			for (int i=0;i<r;i++) {
				top = 0;
				for (int j=0;j<c;j++) {
					top+=data[i][j];
				}
				if (val(top)>ymax) {
					ymax = val(top);
				}
			}
		} else {
			for (int i=0;i<r;i++) {
				for (int j=0;j<c;j++) {
					if (val(data[i][j])<ymin) {
						ymin = val(data[i][j]);
					}
					if (val(data[i][j])>ymax) {
						ymax = val(data[i][j]);
					}
				}
			}
		}
		if (ymin<0) {
			maxv = -ymin;
		}
		if (ymax>maxv) {
			maxv= ymax;
		}
		st = step((int)maxv);
		nstp=0;
		if (ymin<0) {
			nstp = (int)((-ymin-.01)/st)+1;
			if (nstp>10) {
				st = step((int)-ymin);
				nstp = (int)((-ymin-.01)/st)+1;
			}
			zerostp = nstp;
		} else {
			zerostp = 0;
		}
		nstp += (int)((ymax-.01)/st)+1;
	}

	public String ylabel(double d) {
		if (log) {
			return ""+((int)(Math.exp(d*Math.log(10.))+.1));
		} else {
			return ""+(int)d;
		}
	}

	public void paint(Graphics g) {
		setBackground(Color.lightGray);
		if (font!=null) {
			g.setFont(font);
		}
		int width = size().width;
		int height = size().height;
		int okwidth = width-xmarg-rightmarg;
		int okheight = height-ymarg-topmarg;
		double xsc = okwidth/(double)(r-1);
		double ysc = okheight/(double)(nstp*st);
		// data
		double tot[] = null;
		if (stack) {
			tot = new double[r];
			for (int i=0;i<r;i++) {
				tot[i]=0;
			}
		}
		if (stack) {
			// draw stacked layers
			for (int j=c-1;j>=0;j--) {
				int lastx=0,lasty=0, lasty2=0,x,y,y2=0;
				if (colors != null) {
					g.setColor(colors[j%numcolors]);
				}
				for (int i=0;i<r;i++) {
					x = (int)(xmarg+i*xsc);
					y = (int)(topmarg+okheight-(val(tot[i])+zerostp*st)*ysc);
					tot[i]+=data[i][j];
					y2 = (int)(topmarg+okheight-(val(tot[i])+zerostp*st)*ysc);
					if (i!=0) {
						int xp[] = new int[4];
						int yp[] = new int[4];
						xp[0] = lastx;
						yp[0] = lasty;
						xp[1] = x;
						yp[1] = y;
						xp[2] = xp[1];
						yp[2] = y2;
						xp[3] = xp[0];
						yp[3] = lasty2;
						g.fillPolygon(xp,yp,4);
					}
					lastx = x;
					lasty = y;
					lasty2 = y2;
				}
			}
		} else {
			// Draw normal lines
			for (int j=0;j<c;j++) {
				int lastx=0,lasty=0, x,y;
				if (colors != null) {
					g.setColor(colors[j%numcolors]);
				}
				for (int i=0;i<r;i++) {
					x = (int)(xmarg+i*xsc);
					y = (int)(topmarg+okheight-(val(data[i][j])+zerostp*st)*ysc);
					if (y<0) {
						y=0;
					}
					if (i!=0 && (y>0 || lasty>0)) {
						g.drawLine(lastx,lasty,x,y);
					}
					lastx = x;
					lasty = y;
				}
			}
		}

		// Y grid
		g.setColor(Color.gray);
		for (int j=0;j<=nstp;j++) {
			g.drawLine(xmarg,(int)(topmarg+okheight-ysc*j*st),xmarg+okwidth, (int)(topmarg+okheight-ysc*j*st));
		}
		g.setColor(Color.red);
		
		// X axis
		g.drawLine(xmarg,(int)(topmarg+okheight-ysc*zerostp*st),xmarg+okwidth,(int)(topmarg+okheight-ysc*zerostp*st));
		for (int i=0;i<r;i++) {
			g.drawLine((int)(xmarg+xsc*i),topmarg+okheight,(int)(xmarg+xsc*i),topmarg+okheight+tickw);
			g.drawString(""+(xlabel+i),(int)(xmarg+xsc*i)-5,topmarg+okheight+15);
		}
		// Y axis
		g.drawLine(xmarg,topmarg,xmarg,topmarg+okheight);
		for (int j=0;j<=nstp;j++) {
			g.drawLine(xmarg,(int)(topmarg+okheight-ysc*j*st),xmarg-tickw,
				(int)(topmarg+okheight-ysc*j*st));
			String ylab = ylabel((j-zerostp)*st);
			g.drawString(ylab,xmarg-tickw-getFontMetrics(font).stringWidth(ylab), (int)(topmarg+okheight-ysc*j*st));
		}

		
	}
}
