// Ashok Sivakumar        6.030  Final Project
// 12/04/99

package breakout;


import java.awt.event.*;
import java.awt.*;
import cs101.io.*;	


/*  This class is the main link between  all the other classes in the game.	
	The screenCanvas is in charge of the specific ball repository, brick repository,
	methods dispatched when key or mouse events occur, double buffering, repainting
	images on screen, keeping track of the players score, creating the initial network 
	connection, taking care of paddle movements, continously updating the locations of all 
	ghost balls, and finally repainting necessary elements on the screen when it is required.
		
	Thus, everytime events happen, the other classes "catch them" but 
	eventually call the methods of this class so that action can be
	taken on the screen.  Primarily, this class incorporates the use of :
	1. Brick data repository to delete those bricks already hit on the screen.        
	2. Ball repository to keep track of myBalls and GhostBalls and keep them moving correctly
	3. Mouse and Keyboard listeners for paddle movements
	4. Client connection to receive / send data across the network for 2-Player game
	5. Generic Java paint methods to continously display all objects on screen.
	
*/


public class ScreenCanvas extends Canvas
{	
	public BrickRepository brickrepos; // Field variables to hold respective repositories for game
	public BallRepository ballrepos;  
	
	public int xMin = 20, xMax=700, yMin = 10, yMax = 600; // dimensions of canvas
	public Point CanvasPaddleUL = new Point((xMin+xMax)/2-40,this.yMax-25); // coords of
	public Point CanvasPaddleLR = new Point((xMin+xMax)/2+40,this.yMax-15); // paddle
	
	
	public	  Dimension offDimension;    // Field variables needed for double-buffering of
	public    Image offImage;    		 // of screen canvas
	public    Graphics offGraphics; 

	public boolean pause = true;  		 // boolean to pause pause ball, mouse and key listeners
	public int myScore = 0;
	public int numberOfBrick100s = 0;
	public String myScoreString = ("YOUR SCORE =  ");  // variables needed to keep track of scores
	public int numberOfPlayers = 0;
	public int playerID = 0;			// default ID value - will change to 1 or 2 when game begins
	public int ghostX=0, ghostY=0; 		/* default center coordinates for ghost ball - these coordinates
										   are continously updated b/c of Updater class thread and thus ghost
										   ball is painted in new location using these coordinates (which are
										   read across the network 								*/
	
	
	/**
	 *Field variable to remember the name of the Client connection
	 */
	public BOClient connection;
	
	 
	 
	/* constructor to create network connection, obtain each player's ID,
	   set the frame size and color, add mouse and key listeners, create instances of 
	   repositories, call the specific methods to create the Bricks and Initial Ball,
	   set default location of ghost ball offset from where original ball will start   */	
	   
	   
	public ScreenCanvas() 
	{	
		
		this.connection = new BOClient();   //Creation of a new BOClient assigned to the field variable
		System.out.println("About to read from new connection.");
		System.out.println("Available bytes: " + this.connection.available());
		this.playerID = this.connection.read(); // first integer read is the player ID
		System.out.println("player id =  " + playerID);
			
			
		
		setSize(this.xMax,this.yMax-10);  // sets size of playing area
		setBackground(Color.black);
		
		// need to add listeners to this canvas so I can act on Keyboard and Mouse Events.
		KeyListener kl = new KeyboardListener(this);
		this.addKeyListener(kl);
		MouseMotionListener ml = new MouseTracker(this);
		this.addMouseMotionListener(ml);
		
		
		this.brickrepos  = new BrickRepository(this); 				// new instances of repositories and
		this.ballrepos = new BallRepository(this, this.brickrepos); // calls to methods to make everything
		this.brickrepos.makeBricks();
		this.ballrepos.makeInitialBall();
		
		int centerX = (this.xMin+this.xMax+ 30 ) / 2;		// start the ghost ball offset from where
		int centerY = this.yMax - 62;						// the initial ball starts so user can see
		this.ballrepos.makeGhostBall(centerX, centerY);		// both balls simultaneously
	}
	
	
	       
     /* This is the method to update a frame of animation. 
     	The next three methods use the technique of double-buffering
     	so objects do not flicker on screen (because of repainting)    */   
     	 
     public void update(Graphics g) 
     {	
     	Dimension d = size();// Create the offscreen graphics context
		
		if ((this.offGraphics == null)	 
			|| (d.width != this.offDimension.width)
	 		|| (d.height != this.offDimension.height)) 
	 	{	    
 			this.offDimension = d;
    		this.offImage = createImage(d.width, d.height);
    		this.offGraphics = this.offImage.getGraphics();	
    	}	// Erase the previous image

		this.offGraphics.setColor(getBackground());
		this.offGraphics.fillRect(0, 0, d.width, d.height);
		this.offGraphics.setColor(Color.black);	// Paint the frame into the image
		paintFrame(this.offGraphics);	// Paint the image onto the screen
		
		paint(g);
		
	}    
	
	
	/* Paint the previous frame (if any). */
    public void paint(Graphics g) 
    {	
		if (this.offImage != null)
		{
			g.drawImage(this.offImage, 0, 0, null);    
		}
		else
		{
			update(g);
		}
		
	}    
	
	
	
	/* Paint a frame of animation. */
    public void paintFrame(Graphics g) 
    {	
    	
    	
    	this.brickrepos.paintBricks(g); // Simply paints all bricks in brick repos
    	this.paintPaddle(g);	
		this.paintScores(g);		
		this.ballrepos.paintBalls(g);
	}
	
	/* This method is used to paint the paddle using the field coordinates  */
	
	
	public void paintPaddle(Graphics g)
	{
		g.setColor(Color.green);
		g.drawRect(this.xMin-1, this.yMin-1, this.xMax-this.xMin+2, this.yMax-this.yMin+2);
		g.setColor(Color.blue);
		g.fillRect(this.CanvasPaddleUL.x, this.CanvasPaddleUL.y, 81, 10);
	}
	
	/* This method paints the score of the current player underneath the border of play. */
	
	public void paintScores(Graphics g)
	{
		g.setColor(Color.white.brighter());
		g.drawString((this.myScoreString + this.myScore),xMin, yMax+20);
		
	}
	
	/* This is the method called by BreakOutMain so that game view initially shows up on screen */
	
	public void showStuff()
	{
		this.repaint();
	}
	
	/*  This method checks to see if ball has hit paddle and makes it bounce off */
	
	public void checkHitPaddle(ClassicBall boball)
	{
		Point ballCent = new Point(boball.getCenter());
		double ballRad = boball.getRadius();
		
		
		//checks if ball has hit paddle (in range of paddle)
		if ( ((ballCent.x+ballRad) >= this.CanvasPaddleUL.x) 
					&& ((ballCent.x-ballRad) <= this.CanvasPaddleLR.x)
					&& ((ballCent.y+ballRad) >= this.CanvasPaddleUL.y)
					&& ((ballCent.y-ballRad) <= this.CanvasPaddleLR.y) )
			{

				if (ballCent.x+ballRad >= CanvasPaddleLR.x)  //if ball hit brick from right
				{
					boball.changeXvelocity(Math.abs(boball.xVel));
				}
				
				
				if (ballCent.x-ballRad <= CanvasPaddleUL.x)  //if ball hit from left
				{
					boball.changeXvelocity(-Math.abs(boball.xVel));
							
				}
				
				
							if (ballCent.y-ballRad <= CanvasPaddleUL.y)  //if ball hit from top
				{
					boball.changeYvelocity(-Math.abs(boball.yVel));
				}
			}				
	}
	
	
	/* This method is called as the mouse is dragged or moved - it simply postions the paddle's center
	   to the current location of the mouse   */
	
	public void mouseMovePaddle(int xPos, int yPos)
	{
	
		if (!this.pause)  // the paddle will only move if the game is not paused
		{
			this.CanvasPaddleUL.x = (xPos - 39);
			this.CanvasPaddleLR.x = (xPos + 39);
			this.repaint();
		}
	}
	
	/* This method is called when the user presses the #4 key on the key Pad and when the NumLocks key
	   is depressed - it's effect is to move the entire paddle to the left  */
	
	public void updateLeft()
	{
	
	
		if (!this.pause)  // the paddle will only move if the game is not paused
		
		{
		
			if(this.CanvasPaddleUL.x >= this.xMin + 10) /* this is the general case where the paddle is not
				near the left edge of the screen  - in this case the paddle moves by 10 pixels to the left  */
			{
				this.CanvasPaddleUL.x -= 10;
				this.CanvasPaddleLR.x -= 10;
				this.repaint();
			}
		 	else // special case when paddle is near edge - necessary so paddle doesn't overstep game boundries
		 	{
		 		int difference = this.CanvasPaddleUL.x - this.xMin;
		 		this.CanvasPaddleUL.x -= difference;
				this.CanvasPaddleLR.x -= difference;
				this.repaint();
			} 
		  
		  
		}
		  
	}
	
	/* This method is called when the user presses the #6 key on the key Pad and when the NumLocks key
	   is depressed - it's effect is to move the entire paddle to the right  */
	
	
	public void updateRight()
	{
	
		if (!this.pause)   // the paddle will only move if the game is not paused
	
	  	{
	
			if(this.CanvasPaddleLR.x <= this.xMax -10)    /* this is the general case where the paddle is not
				near the right edge of the screen  - in this case the paddle moves by 10 pixels to the  right  */
			{
				this.CanvasPaddleUL.x += 10;
				this.CanvasPaddleLR.x += 10;
				this.repaint();
			}
	 		else // special case when paddle is near edge - necessary so paddle doesn't overstep game boundries
	 		{
	 			int difference = this.xMax - this.CanvasPaddleLR.x;
	 			this.CanvasPaddleUL.x += difference-2;
				this.CanvasPaddleLR.x += difference-2;
				this.repaint();
			} 
	
	   }
	}
	
	
	
	// The next 5 methods are simply return methods for other classes to use to obtain certain field 
	// variable values from this class
	
	public boolean getPause()
	{
		return this.pause;
	}
	
		
	public int getMaxX()
	{
		return this.xMax;
	}
	public int getMinX()
	{
		return this.xMin;
	}
	public int getMaxY()
	{
		return this.yMax;
	}
	public int getMinY()
	{
		return this.yMin;
	}
	
	
	/**
	 *	updatePlayers() reads the x and y coordinates of the ghost ball
	 *	read by the client, then it calls ScreenCanvas's updatingGhostBall
	 * 	method, giving it the x and y coordinates the client read.
	 */
	public void updatePlayers()  
	{
		this.ghostX = this.connection.read();  // reads centerx of ghost
		this.ghostY = this.connection.read();  // reads centery of ghost	
		this.updatingGhostBall(this.ghostX, this.ghostY);
	}	
		
	
	/*  This method repaints the ghost ball in its most recently read coordinate loction*/
	
	public void updatingGhostBall(int x, int y)
	{
		int vectSize = this.ballrepos.ballvect.size();
		int i; 
		BreakOutBall ball;
		
		this.ghostX = x;
		this.ghostY = y;
		
		for (i=0; i<vectSize; i++)  // checks each ball in the repository
		{
			ball = (BreakOutBall)(this.ballrepos.ballvect.elementAt(i));
			
			if (ball instanceof GhostBall)  // checks the instance to see if it is a ghost ball
			{
				((GhostBall)ball).changeCenter(x, y); // calls GhostBall Class's method to update ball's center
			}
		}
			
	}
	
	
}	

