package plots;

import java.awt.Graphics;
import java.awt.Canvas;
import java.awt.Image;
import java.applet.*;
import java.awt.Component;
import java.awt.Color;
import java.awt.Polygon;
import java.awt.Dimension;
import java.util.Stack;

/* Abstract because the method `draw' has to be defined.  It is
	responsible for drawing the picture on the off-screen image. */

public abstract class PlotCanvas extends Canvas {
	public Image img;
	public Graphics gfx;
	public Matrix2d matrix;
	
	public PlotCanvas(int w, int h) {
		matrix = new Matrix2d(w, h);
		setBackground(Color.white);
	}
	
	public Dimension preferredSize() {
		return(new Dimension(matrix.w, matrix.h));
	}
	
	public Dimension minimumSize() {
		return(new Dimension(matrix.w, matrix.h));
	}
	
	public void addNotify() {
		super.addNotify();
		mkImage();
	}

	public void mkImage() {
		img = createImage(matrix.w, matrix.h);
		gfx = img.getGraphics();		
	}

	public synchronized void render() {
		// System.err.println("rendering");
		getGraphics().drawImage(img, 0, 0, null);
	}
	
	public synchronized void render(Graphics g) {
		// System.err.println("rendering");
		g.drawImage(img, 0, 0, null);
	}
	
	/** Reverses the normal ordering of paint() and 
		update() for flicker-free performance. */
	public void paint(Graphics g) {
		draw();
		render(g);
	}
	
	public void update() {
		draw();
		render(getGraphics());
	}
	
	public void update(Graphics g) {
		render(g);
	}

	// --------------------------------------------------

	// an array of lines, in pairs of points
	public int[][] grid(float llx, float lly, float urx, float ury, float dx, float dy) {
		Stack s = new Stack();
		int N = (int) (llx/dx);
		if (N*dx < llx) N++;
		float x = N*dx;
		while (x < urx) {
			int[] p;
			p = matrix.transform(x, lly);
			s.push(p);
			p = matrix.transform(x, ury);
			s.push(p);
			x += dx;
		}	 
		 N = (int) (lly/dy);
		if (N*dy < lly) N++;
		float y = N*dy;
		while (y < ury) {
			int[] p;
			p = matrix.transform(llx, y);
			s.push(p);
			p = matrix.transform(urx, y);
			s.push(p);
			y += dy;
		}
		int [][] S = new int[s.size()][2];
		for (int i=s.size();i>0;i--) {
			S[i-1] = (int[]) s.pop();
		}
		return(S);		
	}

	public void strokeLines(Graphics gfx, int[][] lines) {
		for (int i=0;i<lines.length;i+=2) {
			gfx.drawLine(lines[i][0], lines[i][1], lines[i+1][0], lines[i+1][1]);
		}
	}
	
	public void strokePath(Graphics gfx, int[][] path) {
		for (int i=1;i<path.length;i++) {
			gfx.drawLine(path[i-1][0], path[i-1][1], path[i][0], path[i][1]);
		}
	}

	// --------------------------------------------------
	
	public abstract void draw();
	
	// --------------------------------------------------

	// - drawing ----------------------------------------

	public void drawEllipticalArc(float x, float y, float a, float b, double A, double B) {
		int alpha = (int)(A*180/Math.PI);
		int beta = (int)(B*180/Math.PI);
		int[] corner = matrix.transform(x-a, y+b);
		int[] diameter = matrix.transform(x+a, y-b);
		gfx.drawArc(corner[0], corner[1], diameter[0]-corner[0], diameter[1]-corner[1], alpha, beta);
	}

	public void fillEllipticalArc(float x, float y, float a, float b, double A, double B) {
		int alpha = (int)(A*180/Math.PI);
		int beta = (int)(B*180/Math.PI);
		int[] corner = matrix.transform(x-a, y+b);
		int[] diameter = matrix.transform(x+a, y-b);
		gfx.fillArc(corner[0], corner[1], diameter[0]-corner[0], diameter[1]-corner[1], alpha, beta);
	}

	public void clear(Graphics gfx) {
		gfx.setColor(getBackground());
		gfx.fillRect(0, 0, matrix.w, matrix.h);
		gfx.setColor(getForeground());
	}
	
	public void drawLine(Graphics gfx, float x0, float y0, 
			float x1, float y1, Color c) {
		int p[] = matrix.transform(x0, y0);
		int q[] = matrix.transform(x1, y1);
		gfx.setColor(c);
		gfx.drawLine(p[0], p[1], q[0], q[1]);
	}
	
	public void drawPolygon(Graphics gfx, Polygon p, Color c) {
		gfx.setColor(c);
		int x[] = p.xpoints, y[] = p.ypoints;
		for (int i=1;i<p.npoints;i++) {
			gfx.drawLine(x[i-1], y[i-1], x[i], y[i]);
		}
	}
	
	public void fillPolygon(Graphics gfx, Polygon p, Color c) {
		gfx.setColor(c);
		gfx.fillPolygon(p);
	}
	
	public void drawEllipse(float x, float y, float a, float b) {
		int[] corner = matrix.transform(x-a, y+b);
		int[] diameter = matrix.transform(x+a, y-b);
		gfx.drawOval(corner[0], corner[1], diameter[0]-corner[0], diameter[1]-corner[1]);
	}

	public void fillEllipse(float x, float y, float a, float b) {
		int[] corner = matrix.transform(x-a, y-b);
		int[] diameter = matrix.transform(x+a, y+b);
		gfx.fillOval(corner[0], corner[1], diameter[0]-corner[0]+1, diameter[1]-corner[1]+1);
	}

	public void drawEllipticalArc(float x, float y, float a, float b, int A, int B) {
		int[] corner = matrix.transform(x-a, y+b);
		int[] diameter = matrix.transform(x+a, y-b);
		gfx.drawArc(corner[0], corner[1], diameter[0]-corner[0], diameter[1]-corner[1], A, B);
	}

	public void fillEllipticalArc(float x, float y, float a, float b, int A, int B) {
		int[] corner = matrix.transform(x-a, y+b);
		int[] diameter = matrix.transform(x+a, y-b);
		gfx.fillArc(corner[0], corner[1], diameter[0]-corner[0], diameter[1]-corner[1], A, B);
	}

	public void clear() {
		gfx.setColor(getBackground());
		gfx.fillRect(0, 0, matrix.w, matrix.h);
		gfx.setColor(getForeground());
	}
	
	public void drawLine(float x0, float y0, float x1, float y1, Color c) {
		int p[] = matrix.transform(x0, y0);
		int q[] = matrix.transform(x1, y1);
		gfx.setColor(c);
		gfx.drawLine(p[0], p[1], q[0], q[1]);
	}
	
	public void drawPolygon(Polygon p, Color c) {
		gfx.setColor(c);
		int x[] = p.xpoints, y[] = p.ypoints;
		for (int i=1;i<p.npoints;i++) {
			gfx.drawLine(x[i-1], y[i-1], x[i], y[i]);
		}
	}
	
	public void fillPolygon(Polygon p, Color c) {
		gfx.setColor(c);
		gfx.fillPolygon(p);
	}
}
