/*
credits:
animation timer goes to the swing tutorial and how to make shapes with
the JLabel

PongDemo 1.0
*/

import javax.swing.*;
import java.util.Random;
import java.awt.*;
import java.awt.event.*;

public class Pong extends JFrame implements ActionListener,
                             				MouseMotionListener, 
                             				MouseListener,
                             				KeyListener {
    JLayeredPane playPanel;			//this is the playing field                         					
    
    JPanel   buttonPanel,			//holds pause, speedUp, speedDown, and reset buttons
    	      scorePanel;			//holds the score
    	      
    JLabel statIconLabel,			//holds the background of the statPanel
    	    p1ScoreLabel,			//shows the scores
       	    p2ScoreLabel,			//shows the scores

    	         bgLabel;			//holds the background
    
    JButton     upButton, 			//these two buttons are for changing variable speed
    		  downButton, 
    		 resetButton;			//this resets the ball
    		
    //initialize the menu objects
    JMenuBar     menuBar;			//this is the main menuBar
    JMenu     		file,			//these are the menus
    				help;			
    JMenuItem 		game, 			//these go within the menus
    				exit, 
    			   about;	

    
	ImageIcon statIcon;				//this is the background of the statPanel
	
	BoxLabel   ballLabel,				//the ball and paddle instances of the BoxLabelClass
			 paddleLabel,
		   aiPaddleLabel;
	
	
	char     p1Up = 'w',			//these are the keyboard movement presets
		   p1Down = 's';
	
    Timer timer;					//make the timer
	boolean frozen = false,			//used for the timer to see if frozen
			     ai = true,			//ai?
			newGame = true;			//this is used to reset the paddle positions
	int fps = 60;					//how fast does the animation go
    int delay = (fps > 0) ? (1000 / fps) : 100;
	
	//these hold the palyer's scores
	int p1Score = 0, 
	    p2Score = 0;
	
	//these are variables used with ball movement
	//these are constants to more easily set and determine 
	//the direction of the ball	
	final int up = 1, down = 0, left = 2, right = 3;
	
	int speed = 5,		//this is the initial speed 
		     yDir, 		//this is to hold the X direction of the ball
		     xDir,		//this is to hold the Y direction of the ball
			 oldX,	 	//the "old" vars get the old direction
			 oldY, 
			 newX,	 	//the "new" vars add or subtract with the "old"
			 newY,
	 oldP1PaddleY,		//these hold the old location of the paddle
	 oldP2PaddleY,		//and are used for keyboard control
	 newP1PaddleY,		//these are the new coordinates
	 newP2PaddleY;
	
	int frameCount = 0;	//just holds the number of frames since 
						//start (also the score)
						
	Random rand = new Random();		//used for any random stuff needed 
	
    public Pong()    {
    	//This is the constructor where the frame and its panels are created
    	//this names the frame by calling the constructor of JFrame
    	//which this program extends.  By doing this you eliminate lots of
    	//extraneous code
    	super("JPong by Alex Perez");
    	
    	//this just makes the program have a nice look and feel
    	setDefaultLookAndFeelDecorated(true);
    	
    	//border layout for simplicity (field in center and score in east)
		getContentPane().setLayout(new BorderLayout());
		
    	//Set up a timer that calls this object's actionPerformed handler.
        timer = new Timer(delay, this);
        timer.setInitialDelay(0);
        timer.setCoalesce(true);

		//This sets up the playPanel where the playing field is
		playPanel = new JLayeredPane();
		playPanel.setOpaque(true);
		playPanel.setBackground(Color.BLACK);
		playPanel.setPreferredSize(new Dimension(400, 275));
		
		//these let the program read the mouse movement and clicks
		playPanel.addMouseListener(this);
		playPanel.addMouseMotionListener(this);
		playPanel.addKeyListener(this);
				
        //make the scorePanel and make it a 
        //GridLayout to align the scoreLabels
        //and make it black
        scorePanel = new JPanel();
        scorePanel.setLayout(new GridLayout(1,2));
        scorePanel.setBackground(Color.BLACK);
                        
        //this function always builds the seperate objects in the panels
		buildUI();

		//this function builds the menus and adds them to the frame
		buildMenus();
		
        //add all the JPanels and the layeredPane
        getContentPane().add(playPanel, BorderLayout.WEST);
        getContentPane().add(scorePanel, BorderLayout.SOUTH);
        
        //Create and set up the window.
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
        //Display the window.
        pack();
        setVisible(true);
    }
    
    void buildUI(){
    	//make the scorelabels
    	p1ScoreLabel = new JLabel("Player 1: "+p1Score);
    	p2ScoreLabel = new JLabel("Player 2: "+p2Score);
    	
    	//make the text white
    	p1ScoreLabel.setForeground(Color.WHITE);
    	p2ScoreLabel.setForeground(Color.WHITE);
    	
    	//make the paddles and the ball
		paddleLabel = new BoxLabel(4, 54, Color.WHITE);
		aiPaddleLabel = new BoxLabel(4, 54, Color.WHITE);
		ballLabel = new BoxLabel(5, 5, Color.GREEN);
	
		//draw the tennis court
		buildCourt();
		
		//add the paddle and ball labels to the layered pane
		//and make the paddles above the ball and court
		playPanel.add(ballLabel, new Integer(100), 0);
		playPanel.add(paddleLabel, new Integer(101), 0);
		playPanel.add(aiPaddleLabel, new Integer(102), 0);
		
		//add scoreLabel to scorePanel
		scorePanel.add(p1ScoreLabel);
		scorePanel.add(p2ScoreLabel);
	}
	
	void buildMenus(){
   		//create the menu bar
 		menuBar = new JMenuBar();
   	   	
 	  	//make the menus
 	  	file = new JMenu("File");
	   	help = new JMenu("Help");
   	
  	 	//add the menus to the menu bar
 	  	menuBar.add(file);
 	  	menuBar.add(help);
   	
 	  	//create the menu items
 	  	game = new JMenuItem("Game...");
 	  	game.addActionListener(this);
 	  	exit = new JMenuItem("Exit");
 	  	exit.addActionListener(this);
  	 	about = new JMenuItem("About");
		about.addActionListener(this);
	
  	 	//add the menuitems to their respective menu
  	 	file.add(game);
  	 	file.add(exit);
  	 	help.add(about);
  	 	
  	 	//add the menuBar
		setJMenuBar(menuBar);
    }
 	
 	//draw line to make the playPanel look like a tennis court
	void buildCourt(){
		BoxLabel topLine = new BoxLabel(400, 5, Color.WHITE);
		BoxLabel bottomLine = new BoxLabel(400, 5, Color.WHITE);
		BoxLabel centerLine = new BoxLabel(6, 275, Color.WHITE);
		
		playPanel.add(topLine);
		playPanel.add(bottomLine);
		playPanel.add(centerLine);
		
		//move the lines to their appropriate locations
		topLine.setLocation(0, 0);
		bottomLine.setLocation(0, 270);
		centerLine.setLocation(400 / 2 - 3, 0);
	}
	
	void moveP1Paddle(int dir){
		//get the old X location of the paddle
		oldP1PaddleY = paddleLabel.getY();
		
		//only move the
		if(!(oldP1PaddleY + 54 > playPanel.getHeight())){
			if(dir == up)
				newP1PaddleY = oldP1PaddleY--;
			if(dir == down)
				newP1PaddleY = oldP1PaddleY++;
		}
		paddleLabel.setLocation(10,newP1PaddleY); 
	}
		

 	//perfect ai
	void moveAI(){
		//if it is a new game set the paddle in the middle of the field on the right side
		if(newGame){
			aiPaddleLabel.setLocation(playPanel.getWidth() - 20, playPanel.getHeight() / 2);
			newGame = false;
		}
		//only move the paddle within the y bounds
		if(ballLabel.getY() > 0 && ballLabel.getY() < playPanel.getHeight() )
			//match the paddle with the x of the ball
			aiPaddleLabel.setLocation(playPanel.getWidth() - 20, ballLabel.getY() - 26);
	}
	
	//use this with true and it resets the ball
	void moveBall(boolean restart){
		
		//if the restart flag is true then set the ball back to
		//the top
		if(restart == true){
			ballLabel.setLocation(playPanel.getWidth() / 2, 0);
			setRandomDirection();
			yDir = rand.nextInt(2);
			restart = false;
		}
		
		//get the previous locations to work with
		oldX = ballLabel.getX();
		oldY = ballLabel.getY();
		
		//set the speed and direction of the ball
		switch(xDir){
			case right: newX = oldX + speed; break;
			case left:  newX = oldX - speed; break;
		}
		switch(yDir){
			case up:   newY = oldY - speed; break;
			case down: newY = oldY + speed; break;
		}
		
		//move the ball
		ballLabel.setLocation(newX, newY);
	}
	
	//set a random direction
	void setRandomDirection(){
		int go = 0;
		//just loop until the random # work is either 2 or 3, which is
		//left or right
		while(go == 0){
			int work = rand.nextInt(4);
			if(work == 2){
				xDir = left;
				go = 1;
			} else if(work == 3){
				xDir = right;
				go = 1;
			}
		} 
	}

	void ballHitTest(){
		int ballY = ballLabel.getY(),
			aiPaddleY = aiPaddleLabel.getY();
		
			//ball hit your paddle
			//the ball has the same y as the paddle
			if(ballLabel.getX() <= paddleLabel.getX() + 6 && ballLabel.getX() >= paddleLabel.getX() - 2){
				//the ball is within the paddle's bounds
				//since the ball hit the paddle, find which side it hit
				//and set the direction accordingly
				//Note:  adding 10 to the ballLabel x in the if lets the collision be 
				//true since it tests the top left pixel instead of the top right
				//Test for the first left 3rd of the paddle = 0 to 24 pixels
				if(ballLabel.getY() + 4 >= paddleLabel.getY() && ballLabel.getY() <= paddleLabel.getY() + 18){
					xDir = right;
					yDir = up;
					p1Score++;
					updateScores();
				}
				if(ballLabel.getY() + 4 >= paddleLabel.getY() + 18 && ballLabel.getY() <= paddleLabel.getY() + 36){
					xDir = right;
					yDir = right;
					p1Score++;
					updateScores();
				}
				if(ballLabel.getY() + 4 >= paddleLabel.getY() + 36 && ballLabel.getY() <= paddleLabel.getY() + 54){
					xDir = right;
					yDir = down;
					p1Score++;
					updateScores();
				}
				oldX = 25;

			}
				
			//just set a random direction for the ai paddle
			if(ballLabel.getX() == 380 || (ballLabel.getX() >= 375 && ballLabel.getX() <= 380)){
				System.out.println("first part works");
				//Test for the first left 3rd of the paddle = 0 to 24 pixels
				if(ballLabel.getY() + 4 >= aiPaddleLabel.getY() && ballLabel.getY() <= aiPaddleLabel.getY() + 54){
					System.out.println("ball hit the paddle");
					xDir = left; 
					yDir = rand.nextInt(2);
					p2Score++;
					updateScores();
				}
			}
			
			//ball hit the bottom of the screen
			if(ballLabel.getY() >= playPanel.getHeight()){
				yDir = up;
			}
			
			//ball hit the top of the screen
			if(ballLabel.getY() <= 0){
				yDir = down;
			}
			
			//ball hit the left side bounds
			if(ballLabel.getX() <= 0){
				stopAnimation();
				moveBall(true);
			}
			
			//ball hit the right side bounds
			if(ballLabel.getX() >= playPanel.getWidth()){
				stopAnimation();
				moveBall(true);
			}				
	}
	
	void updateScores(){
		p1ScoreLabel.setText("Player 1: "+p1Score);
		p2ScoreLabel.setText("Player 2: "+p2Score);
	}

    void aboutDialog(){}
    void gameDialog(){}

    //make the paddle follow the cursor.
    public void mouseMoved(MouseEvent e) {
		paddleLabel.setLocation(10, e.getY() - 30);
    }
 	
    public void mouseEntered(MouseEvent e) {
    	//start the animation if the mouse entered the window
    	startAnimation();
    }

    public void mouseExited(MouseEvent e) {
    	//stop the animation when the mouse exits the window
    	stopAnimation();
    }
    public void mousePressed(MouseEvent e) {}
    public void mouseClicked(MouseEvent e) {}
    public void mouseDragged(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    
    //key events
	public void keyTyped(KeyEvent e) {}
    public void keyPressed(KeyEvent e) {
    	//read the player one keys
    	if(e.getKeyChar()==p1Up)
    		moveP1Paddle(up);
    	if(e.getKeyChar()==p1Down)
    		moveP1Paddle(down);
    		
    }
    public void keyReleased(KeyEvent e) {}

	//here is the main game loop
    public void actionPerformed(ActionEvent e) {
    	//move the ball and see if it hit any of the walls or paddle
    	//before and after movements for good measure
    	ballHitTest();
       	moveBall(false);
       	//check which kind of player is player 2
       	if(ai)
       		moveAI();
    	ballHitTest();
    	
		//this piece of code sees if any of the menu options have
		//been pressed and does their respective function
		if(e.getSource() == game)
			gameDialog();
		if(e.getSource() == about)
			aboutDialog();
		if(e.getSource() == exit)
			System.exit(0);
		
		//debug code
    	System.out.println("ballLabel "+ballLabel.getLocation().toString());
    	System.out.println("paddleLabel "+paddleLabel.getLocation().toString());
    	System.out.println("aiPaddleLabel "+aiPaddleLabel.getLocation().toString());
    	System.out.println("newX: " + newX+" oldX: "+oldX+" speed: "+speed);
    	frameCount++;
    }
    
    //timer code
    public void startAnimation() {
        if (frozen) {
			//do nothing
        } else {
            //Start animating!
            if (!timer.isRunning()) {
                timer.start();
            }
        }
    }

    //Can be invoked by any thread (since timer is thread-safe).
    public void stopAnimation() {
        //Stop the animating thread.
        if (timer.isRunning()) {
            timer.stop();
        }
    }
    
    public static void main(String[] args) {
    	//load up the program and go
    	Pong PongDemo = new Pong();
    	//I call moveBall before the timer goes to set it on its
    	//initial direction for the beginning of the game
    	PongDemo.moveBall(true); 
    }
}