//
// Space.java -- provides a visual space for the Particle simulation
// -- essentially, lets you draw lines between two points in 2d space
// A.B. Maccabe
// February 2005
// The University of New Mexico
//

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.lang.*;
import java.util.*;


class Space extends Object {


    // Drawable things know how to draw themselves onto a graphics
    // context when asked to do so.
    private abstract class Drawable {
	abstract void draw( Graphics g, double scale, int border,
			    double xOff, double yOff );
    }

    // a Line is a simple drawable 
    private class Line extends Drawable {
	public double x1, y1, x2, y2;
	public Line( double xx1, double yy1, double xx2, double yy2 ) {
	    x1 = xx1;
	    y1 = yy1;
	    x2 = xx2;
	    y2 = yy2;
	}
	public void draw( Graphics g, double scale, int border,
			  double xOff, double yOff ) {
	    int x1Pos = (int)( (x1+xOff) * scale) + border;
	    int y1Pos = (int)( (y1+yOff) * scale) + border;
	    int x2Pos = (int)( (x2+xOff) * scale) + border;
	    int y2Pos = (int)( (y2+yOff) * scale) + border;
	
	    g.drawLine( x1Pos, y1Pos, x2Pos, y2Pos );
	}
    }

    // a Cirlce is a simple drawable 
    private class Circle extends Drawable {
	public double x, y, radius;
	public Circle( double xx, double yy, double rad ) {
	    x = xx;
	    y = yy;
	    radius = rad;
	}

	public void draw( Graphics g, double scale, int border,
			  double xOff, double yOff ) {
	    int xPos = (int)( (x+xOff) * scale) + border;
	    int yPos = (int)( (y+yOff) * scale) + border;
	    int rad =  (int) (radius*scale);

	    g.fillOval( xPos-rad, yPos-rad, rad*2, rad*2 );
	}
    }

    // a DrawSpace provides the canvas for drawing upon
    private class DrawSpace extends JPanel {
	private double maxX, maxY;	// maximum (visible) x and y values
	private Vector objs;	// a list of drawable objects (we
				// re-draw these from time to time)

	private final int SPOT=2;	// radius of a spot
	private final int FUDGE=5;	// slop on edges of panel

	public DrawSpace( double x, double y ) {
	    objs = new Vector();
	    maxX = x;
	    maxY = y;
	}

	// when we add a drawable object to the canvas, we simply add
	// it to the list of objects and re-draw the canvas
	public synchronized void addObj( Drawable obj ) { 
	    objs.add( obj );
	    repaint(); 
	}

	// paintComponent gets called (magically) by the window system
	// whenever it thinks that the window needs to be drawn or
	// when we call "repaint" (as in addObj above)
	public synchronized void paintComponent( Graphics g ) {
	    // the only tricky thing is to map into the display coordinate system
	    // -- the origin is at the center of  g  
	    // -- the scaling is set so that everything should be visible
	    int width = this.getSize().width;
	    int height = this.getSize().height;

	    double scale = width / ((maxX+FUDGE)*2);
	    if( scale > height / ((maxY+FUDGE)*2) )
		scale = height / ((maxY+FUDGE)*2);

	    // redraw everything in the frame....
	    g.setColor( Color.white );
	    g.fillRect( 0, 0, width, height );
	    g.setColor( Color.black );
	    // setBackground( Color.black );

	    Iterator iter = objs.iterator();
	    while( iter.hasNext() ) {
		Drawable x = (Drawable) iter.next();
		x.draw( g, scale, FUDGE, maxX, maxY );
	    }
	}
    }

    // it's a JFrame with a Jpanel for drawing on
    private JFrame f;		// this is the outer frame (the window)
    private DrawSpace dsp;	// this is the canvas in the window

    private final int INITPIX = 400; // maxium initial pixels in x or y direction

    // constructor
    public Space( double maxX, double maxY ) { 

	maxX = Math.abs( maxX ); // make the value positive
	maxY = Math.abs( maxY ); 

	f = new JFrame( "Particle Space" );

	// give the frame an initial width and height
	double scale;
	if( maxX > maxY ) scale = INITPIX / (maxX*2);
	else scale = INITPIX / (maxY*2);
	int width = (int) (scale * maxX * 2);
	int height = (int) (scale * maxY * 2);
	f.setSize( width, height );

	// create a drawing space and add it to our frame
	dsp = new DrawSpace(maxX, maxY);
	f.getContentPane().add( dsp );
	f.addWindowListener( new WindowAdapter() {
		public void windowClosing( WindowEvent e ) {
		    System.exit(0);
		}
	    });

	// make the frame visible on the screen
	f.setVisible( true );
    }

    // draw a line......
    public void drawLine( double x1, double y1, double x2, double y2 ) {
	dsp.addObj( new Line( x1, -y1, x2, -y2 ) );
    }

    public void drawCircle( double x, double y, double rad ) {
	dsp.addObj( new Circle( x, -y, rad ) );
    }
}