import java.applet.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;

public class PVADrawing extends JPanel
{
	Dimension windowSize = new Dimension(600,600);
	Dimension shapeSize = new Dimension(10,10);
	
	Point2D.Double startPoint = new Point2D.Double(0.0,0.0);
	Point2D.Double startVelocity = new Point2D.Double(0.0,0.0);
	Point2D.Double startAcceleration = new Point2D.Double(0.0,0.0);
	
	Color foregroundColor = new Color(0,0,255);
	Color backgroundColor = new Color(255,255,255);
	Stroke stroke = new BasicStroke(1.5f);

	int currentFrameRate = 30; // Frames per second
	javax.swing.Timer frameTimer;
	
	PVAAnimatedRectangle rect = new PVAAnimatedRectangle(startPoint, shapeSize, startVelocity, startAcceleration);

	public PVADrawing()
	{
		setSize(windowSize);

		addMouseListener(new MouseAdapter()
		{
			public void mousePressed(MouseEvent e)
			{				
				rect.setAcceleration(rect.unitVector(new Point2D.Double(e.getX(),e.getY()),new Point2D.Double(rect.getX(),rect.getY())));
			}
			
			public void mouseReleased(MouseEvent e)
			{
				rect.setAcceleration(new Point2D.Double(0.0,0.0));
			}
		});

		frameTimer = new javax.swing.Timer(1000/currentFrameRate, new
		        ActionListener()
		        {
					public void actionPerformed(ActionEvent Event)
					{
						repaint();
					}
		        });
		
		frameTimer.start();
	}
	
	// Main entry point when running standalone
	public static void main(String[] args) 
	{
		PVADrawing drawing = new PVADrawing();
		JFrame frame = new JFrame();
		frame.getContentPane().add(drawing, BorderLayout.CENTER);
		frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		frame.setTitle("PVADrawing");
		frame.setSize(600,600);
		frame.setBackground(new Color(255,255,255));
		frame.setVisible(true);
	}

	public void paint(Graphics myGraphics)
	{
		rect.incrementTime();

		if ((rect.getX() > windowSize.getWidth() && rect.getVelocity().getX() > 0) || (rect.getX() < 0 && rect.getVelocity().getX() < 0))
		{
			rect.reverseXVelocity();
		}			
		
		if ((rect.getY() > windowSize.getHeight() && rect.getVelocity().getY() > 0) || (rect.getY() < 0  && rect.getVelocity().getY() < 0))
		{
			rect.reverseYVelocity();
		}
		
		/* Oscillations
		if ((rect.getX() > windowSize.getWidth()/2 && rect.getAccelerationVector().getX() > 0) || (rect.getX() < windowSize.getWidth()/2 && rect.getAcceleration().getX() < 0))
		{
			rect.reverseXAcceleration();
		}			
		
		if ((rect.getY() > windowSize.getHeight()/2 && rect.getAcceleration().getY() > 0) || (rect.getY() < windowSize.getHeight()/2 && rect.getAcceleration().getY() < 0))
		{
			rect.reverseYAcceleration();
		}
		*/
		
		Graphics2D bufferGraphics = (Graphics2D) myGraphics;
		bufferGraphics.clearRect(0,0,(int)windowSize.getWidth(),(int)windowSize.getHeight());
		bufferGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		bufferGraphics.setStroke(stroke);
		bufferGraphics.setColor(foregroundColor);
		bufferGraphics.fill(rect);
	}

	class PVAAnimatedRectangle extends Rectangle2D.Double
	{
		Point2D.Double velocityVector;
		Point2D.Double accelerationVector;
		
		public PVAAnimatedRectangle(Point2D.Double location, Dimension size, Point2D.Double velocity, Point2D.Double acceleration)
		{
			super(location.getX(),location.getY(), size.getWidth(),size.getHeight());
			
			velocityVector = velocity;
			accelerationVector = acceleration;
		}

		public void setAcceleration(Point2D.Double acceleration)
		{
			accelerationVector = acceleration;
		}
		
		public Point2D.Double getAccelerationVector()
		{
			return accelerationVector;
		}
		
		public Point2D.Double getAcceleration()
		{
			return getAccelerationVector();
		}
		
		public void setVelocity(Point2D.Double velocity)
		{
			velocityVector = velocity;
		}
		
		public Point2D.Double getVelocityVector()
		{
			return velocityVector;
		}

		public Point2D.Double getVelocity()
		{
			return getVelocityVector();
		}
		
		public void incrementTime()
		{
			Point2D.Double newLocation;
			velocityVector = addVectors(velocityVector,accelerationVector);
			newLocation = addVectors(new Point2D.Double(getX(),getY()),velocityVector);
			x = newLocation.getX();
			y = newLocation.getY();
		}
		
		public void reverseAcceleration()
		{
			Point2D.Double newAcceleration = new Point2D.Double(accelerationVector.getX()-accelerationVector.getX()*2,accelerationVector.getY()-accelerationVector.getY()*2);
			setAcceleration(newAcceleration);						
		}

		public void reverseYAcceleration()
		{
			Point2D.Double newAcceleration = new Point2D.Double(accelerationVector.getX(),accelerationVector.getY()-accelerationVector.getY()*2);
			setAcceleration(newAcceleration);				
		}
		
		public void reverseXAcceleration()
		{
			Point2D.Double newAcceleration = new Point2D.Double(accelerationVector.getX()-accelerationVector.getX()*2,accelerationVector.getY());
			setAcceleration(newAcceleration);
		}

		public void reverseVelocity()
		{
			Point2D.Double newVelocity = new Point2D.Double(velocityVector.getX()-velocityVector.getX()*2,velocityVector.getY()-velocityVector.getY()*2);
			setVelocity(newVelocity);								
		}		
		
		public void reverseXVelocity()
		{
			Point2D.Double newVelocity = new Point2D.Double(velocityVector.getX()-velocityVector.getX()*2,velocityVector.getY());
			setVelocity(newVelocity);		
		}		
										
		public void reverseYVelocity()
		{
			Point2D.Double newVelocity = new Point2D.Double(velocityVector.getX(),velocityVector.getY()-velocityVector.getY()*2);
			setVelocity(newVelocity);						
		}		

		public Point2D.Double addVectors(Point2D.Double vectorOne, Point2D.Double vectorTwo)
		{
			double x, y;
			x = vectorOne.getX() + vectorTwo.getX();
			y = vectorOne.getY() + vectorTwo.getY();
			return new Point2D.Double(x,y);			 
		}
		
		public Point2D.Double multiplyVectors(Point2D.Double vectorOne, Point2D.Double vectorTwo)
		{
			double x, y;
			x = vectorOne.getX()*vectorTwo.getX();
			y = vectorOne.getY()*vectorTwo.getY();
			return new Point2D.Double(x,y);
		}
		
		public Point2D.Double vectorDifference(Point2D.Double vectorOne, Point2D.Double vectorTwo)
		{
			double x, y;
			x = vectorOne.getX() - vectorTwo.getX();
			y = vectorOne.getY() - vectorTwo.getY();
			return new Point2D.Double(x,y);			 		
		}
		
		public Point2D.Double unitVectorOther(Point2D.Double vectorOne, Point2D.Double vectorTwo)
		{
			Point2D.Double difference;
			double distance;
			Point2D.Double unitVector;
			
			difference = vectorDifference(vectorOne, vectorTwo);
			distance = vectorOne.distance(vectorTwo);
			unitVector = new Point2D.Double((difference.getX()/distance), (difference.getY()/distance));
			
			return unitVector;
		}
		
		public Point2D.Double unitVector(Point2D.Double vectorOne, Point2D.Double vectorTwo)
		{
			/*
			1: Find difference (5,-7)
			2: Use Pythagorean theorm to find actual distance a^2 + b^2 = c^2    c = sqrt(a^2+b^2)
			3: Unit vector for x = x/distance, for y = y/distance. or (x/distance,y/distance)
			*/
			
			Point2D.Double difference;
			double distance;
			Point2D.Double unitVector;
						
			difference = vectorDifference(vectorOne, vectorTwo);
			distance = Math.sqrt(difference.getX() * difference.getX()  + difference.getY() * difference.getY());
			unitVector = new Point2D.Double((difference.getX()/distance), (difference.getY()/distance));
			
			return unitVector;
		}
	}
}