/*
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
		   	  pauseLabel;

    
    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
    				game,
    				help;			
    JMenuItem 		exit, 			//these go within the menus
    			   about; 		
    			
    JCheckBoxMenuItem aiItem;		//this is a special menuItem that has a checkBox 
    								//next to it
    
	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 = false,			//ai?
			newGame = true,			//this is used to reset the paddle positions
			checked = false;
	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)
		  hitCount = 0; //used for counting the # of hits and to increase speed				
	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 pauseLabel (comes up when animation Stopped)
    	pauseLabel = new JLabel("Paused");
    	
    	//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);
    	pauseLabel.setForeground(Color.WHITE);
    	
    	//make the paddles and the ball
		paddleLabel = new BoxLabel(4, 54, Color.WHITE);
		aiPaddleLabel = new BoxLabel(400 - 20, 275 / 2 - 36, 4, 54, Color.WHITE);
		ballLabel = new BoxLabel(4, 4, 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(pauseLabel, new Integer(1000), 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);
		
		pauseLabel.setLocation(400 / 2, 275 / 2);
	}
	
	void buildMenus(){
   		//create the menu bar
 		menuBar = new JMenuBar();
   	   	
 	  	//make the menus
 	  	file = new JMenu("File");
	   	help = new JMenu("Help");
	   	game = new JMenu("Game");
   	
  	 	//add the menus to the menu bar
 	  	menuBar.add(file);
   		menuBar.add(game);
 	  	menuBar.add(help);

 	  	//create the menu items
 	  	exit = new JMenuItem("Exit");
 	  	exit.addActionListener(this);
  	 	about = new JMenuItem("About");
		about.addActionListener(this);
		aiItem = new JCheckBoxMenuItem("AI");
		aiItem.addActionListener(new ActionListener() {
 			public void actionPerformed(ActionEvent e) {
        		if(aiItem.isSelected()){	
        			ai = true;
        			moveBall(true);
        			stopAnimation();
        		} else {
        			ai = false;
        			moveBall(true);
        			stopAnimation();
        		}
        	}	
		});

	
  	 	//add the menuitems to their respective menu
  	 	//file.add(game);
		game.add(aiItem);
  	 	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);
	}

 	//perfect ai
	void moveAI(){
		newP2PaddleY = aiPaddleLabel.getY();
		int dist = java.lang.Math.abs(ballLabel.getY() - aiPaddleLabel.getY());
			if (xDir == left) {
				if (aiPaddleLabel.getY() < (ballLabel.getY())) newP2PaddleY = (newP2PaddleY + dist/speed);
				else if (aiPaddleLabel.getY() > (ballLabel.getY() + 3)) newP2PaddleY = (newP2PaddleY - dist/speed);
			}
			else if(xDir == right){
				if (aiPaddleLabel.getY() + 27 < ballLabel.getY()) newP2PaddleY += speed - 1;
				else if (aiPaddleLabel.getY() + 27 > ballLabel.getY()) newP2PaddleY -= speed - 1;
			}
	 aiPaddleLabel.setLocation(400-20, newP2PaddleY);
	}	
	
	//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;
					hitCount++;
					updateSpeed(false);
				}
				if(ballLabel.getY() + 4 >= paddleLabel.getY() + 18 && ballLabel.getY() <= paddleLabel.getY() + 36){
					xDir = right;
					yDir = right;
					hitCount++;
					updateSpeed(false);
				}
				if(ballLabel.getY() + 4 >= paddleLabel.getY() + 36 && ballLabel.getY() <= paddleLabel.getY() + 54){
					xDir = right;
					yDir = down;
					hitCount++;
					updateSpeed(false);
				}
			}
				
			//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);
					updateSpeed(false);
				}
			}
			
			//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);
				p2Score++;
				updateScores();
 				updateSpeed(true);
			}
			
			//ball hit the right side bounds
			if(ballLabel.getX() >= playPanel.getWidth()){
				stopAnimation();
				moveBall(true);
				p1Score++;
				updateScores();
 				updateSpeed(true);
			}				
	}
	void updateSpeed(boolean reset){
		//reset speed
		if(reset)
			speed = 5;
			
		speed += hitCount / 50;
	}
	
	void updateScores(){
		p1ScoreLabel.setText("Player 1: "+p1Score);
		p2ScoreLabel.setText("Player 2: "+p2Score);
	}

	void aboutDialog(){
		JFrame dialogFrame = new JFrame();
		JOptionPane.showMessageDialog(dialogFrame,
    			"JPong 1.0\nMade by Alex Perez",
    			"About",
    			JOptionPane.PLAIN_MESSAGE);
    }
    void gameDialog(){}

    //make the paddle follow the cursor.
    public void mouseMoved(MouseEvent e) {
		paddleLabel.setLocation(10, e.getY() - 30);
    }
    public void mouseExited(MouseEvent e) {
    	//stop the animation when the mouse exits the window
    	stopAnimation();
    }
    public void mouseClicked(MouseEvent e) {
    	//start the animation when the mouse exits the window
       	startAnimation();
	}
    public void mouseDragged(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mousePressed(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    
    //key events
	public void keyTyped(KeyEvent e) {}
    public void keyPressed(KeyEvent e) {}
    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) {
			pauseLabel.setVisible(true);
        } else {
            //Start animating!
            if (!timer.isRunning()) {
                timer.start();
                pauseLabel.setVisible(false);
            }
        }
    }

    //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); 
    }
}

 class ball{}
