//William Brownlow   Final Exam Project
import java.awt.event.*;
import java.awt.*;
import java.applet.Applet;


public class tennis extends Applet implements Runnable, MouseListener, MouseMotionListener
{
   static final int statusBarHt=20;
   static final int minSleepTime=20;
   static final int openingScreen=1;
   static final int fadeOut=2;
   static final int gameOver=3;
   static final int limbo=4;
   static final int normalPlay=5;
   static final int ballDiameter=10;
   static final int maxBallVelocity=10;
   static final int paddleHt=50;
   static final int paddleWidth=7;
   static final int serveBall=10;
   static final int you=0;
   static final int pc=1;
   static final double lookForBallFreq=1;
   static final int X=0;
   static final int Y=1;
   static final int oldX=2;
   static final int oldY=3;
                
   static int gameMode=openingScreen;
   long     startTime;
   int      height,width;     // height & width of applet
   int      yStatusBar,topStatusBar;
   int      sleepTime;     // value used for sleep with thread
   int      fadeCt;        // counter for creating fadeOut
   int      yPaddle[],xPaddle[],yPaddleOld[],paddleScore[];
   int      ball[],ballsUsed,maxBalls;
   int      yPaddleMax,ballVelocity[];
   int      yPaddleVelocityPc=10;
   int      lastPoint=pc;
   int      timeToServe=0;
   int      xMidCourt;
   boolean  ballInPlay;
   Thread   myThread=null;
   Image    offScrImage;
   Graphics offScrGraphics;
   FontMetrics fontMetrics;
   Rectangle rBall;
   Rectangle paddle[]=new Rectangle[2];

   public void init()
   {
      String s;
      int i;

      width=600;
      height=400;
      xMidCourt=width/2;
      yStatusBar=height - (statusBarHt/4);
      topStatusBar=height-statusBarHt;

      sleepTime= 50;

      maxBalls= 46;

      ball=new int[4];
      ballVelocity=new int[2];
      yPaddleMax=topStatusBar-paddleHt;
      yPaddle=new int[2];
      xPaddle=new int[2];
      xPaddle[you]=15;
      xPaddle[pc]=width-paddleWidth-xPaddle[you];
      yPaddleOld=new int[2];
      paddleScore=new int[2];
               
      offScrImage = createImage(width, height);
      offScrGraphics = offScrImage.getGraphics();
      offScrGraphics.setFont(new Font("TimesRoman", Font.BOLD, 16));
      fontMetrics = offScrGraphics.getFontMetrics();
      addMouseListener(this);
      addMouseMotionListener(this);
   }

   public void createBall()
   {
      int    j=paddleHt/2;

      setBallVelocity();
      if (lastPoint==pc)
      {
         ballVelocity[X]*=-1;  
         ball[X]=ball[oldX]=xPaddle[pc]-ballDiameter;
         ball[Y]=ball[oldY]=yPaddle[pc]+j;                   
      }
      else
      {
         ball[X]=ball[oldX]=xPaddle[you]+paddleWidth;
         ball[Y]=ball[oldY]=yPaddle[you]+j;
      }
      ballInPlay=true;
      timeToServe=0;
   }


   public void setBallVelocity()
   {
      double i;
                
      i= Math.random()*1;
      if (i > .5) i=1;
      else i=-1;
      ballVelocity[X]=(int)((Math.random()*maxBallVelocity)+5);
      ballVelocity[Y]= (int)(i) * (int)((Math.random()*maxBallVelocity)+5);     
   }

   public void start()
   {
      startTime=System.currentTimeMillis();
      if (myThread==null)
      {
         myThread= new Thread(this);
         myThread.start();
      }
   }                                                                                       

   public void stop()
   {
      myThread.stop();
      myThread=null;
      gameMode=gameOver;
   }

   public void run()
   {
      while (myThread !=null)
      {
         repaint();
         try     {
            myThread.sleep(Math.max(startTime-System.currentTimeMillis(),minSleepTime));
         } 
         catch (InterruptedException e) {
         }
         startTime=System.currentTimeMillis()+sleepTime;
       }
   }

   public void showNormalPlay()
   {
      int i,j=0;

      if (ballInPlay)
      {

                        // erase paddles
         offScrGraphics.setColor(Color.green);
         for (i=0; i<2; i++)
         {
                                // do not erase top of scoreBar
            offScrGraphics.fillRect(xPaddle[i],yPaddleOld[i],paddleWidth,paddleHt);         
         }
        
                        // erase ball
         offScrGraphics.fillRect(ball[oldX],ball[oldY],ballDiameter,ballDiameter);
                                
                        // set new paddle[pc] position
                        // first check if ball is above or below PC paddle
         if (ballVelocity[X] > 0 && ball[X] > xMidCourt && Math.random() <= lookForBallFreq)
         {
            if (ballVelocity[Y] > 0 && ball[Y] + ballDiameter > yPaddle[pc])
               j=Math.abs(yPaddleVelocityPc);
            else if (ballVelocity[Y] < 0 && ball[Y] < yPaddle[pc]+paddleHt)
               j=-1 * (Math.abs(yPaddleVelocityPc));
         }
         if (yPaddle[pc]+j <= 0 || yPaddle[pc]+j >= topStatusBar-paddleHt)
            j*=-1;                          
         yPaddle[pc]+=j;
                        
                        // draw paddles
         offScrGraphics.setColor(Color.blue);
         for (i=0; i<2; i++)
         {
            offScrGraphics.fillRect(xPaddle[i],yPaddle[i],paddleWidth,paddleHt);            
            paddle[i]=new Rectangle(xPaddle[i],yPaddle[i],paddleWidth,paddleHt);
            yPaddleOld[i]=yPaddle[i];
         }
                        
                        // set new ball position
                        // PC wins point
         if (ball[X]+ballVelocity[X] < 0)
         {
            ballsUsed++;
            
            paddleScore[pc]+=15;
            lastPoint=pc;
            ballInPlay=false;
            showNewScore();
         }
                        // I win point
         else if (ball[X]+ballVelocity[X] > width-ballDiameter)
         {
            ballsUsed++;
            paddleScore[you]+=15;
            lastPoint=you;
            ballInPlay=false;
            showNewScore();
         }
         else
         {
            if (ball[Y]+ballVelocity[Y] < 0 || ball[Y]+ballDiameter+ballVelocity[Y] > topStatusBar-ballDiameter)
               ballVelocity[Y]*=-1;                              
        
            rBall=new Rectangle(ball[X]+ballVelocity[X],ball[Y]+ballVelocity[Y],ballDiameter,ballDiameter);                     
                
            if (rBall.intersects(paddle[you]))  setBallVelocity();                           
            else if (rBall.intersects(paddle[pc]))
            {
               setBallVelocity();
               ballVelocity[X]*=-1;
            }
                                
            ball[X]+=ballVelocity[X];
            ball[Y]+=ballVelocity[Y]; 
            ball[oldX]=ball[X];
            ball[oldY]=ball[Y];

            offScrGraphics.setColor(Color.white);
            offScrGraphics.fillOval(ball[X],ball[Y],ballDiameter,ballDiameter);
         }
      }
      else    timeToServe++;

      if (paddleScore[you] >=maxBalls || paddleScore[pc] >= maxBalls)
      {
         gameMode=fadeOut;
         fadeCt=0;
      }
      if (timeToServe == serveBall)  createBall();
   }

   public void showNewScore()
   {
      offScrGraphics.setColor(Color.magenta);
      offScrGraphics.fillRect(0,height-statusBarHt,width,statusBarHt);        

      offScrGraphics.setColor(Color.cyan);
      offScrGraphics.drawString("You: "+paddleScore[you],10,yStatusBar);
      offScrGraphics.drawString("PC: "+paddleScore[pc],width-60,yStatusBar);
   }

   public void showOpeningScreen()
   {
      String buf;
      int i;
      int j=height/2;                 

      offScrGraphics.setColor(Color.white);
      offScrGraphics.fillRect(0,0,width,height);
                
      offScrGraphics.setColor(Color.blue);
      buf="Play tennis against the machine!";
      offScrGraphics.drawString(buf,(width-fontMetrics.stringWidth(buf))/2,j-50);

      offScrGraphics.setColor(Color.magenta);
      buf="Click on mouse to begin game.";
      offScrGraphics.drawString(buf,(width-fontMetrics.stringWidth(buf))/2,j);

      buf="Move mouse to control paddle.";
      offScrGraphics.drawString(buf,(width-fontMetrics.stringWidth(buf))/2,j+25);

      gameMode=limbo;
      ballInPlay=false;
      ballsUsed=0;
      timeToServe=0;
      return;
   }


   public void showScoreBoard()
   {
      String buf;
      int i,k;
      int j=height/2;                 

      offScrGraphics.setColor(Color.white);
      offScrGraphics.fillRect(0,0,width,height);
                
      buf="You - PC";
      k=(width-fontMetrics.stringWidth(buf))/2;       
      offScrGraphics.setColor(Color.blue);
      offScrGraphics.drawString(buf, k,j - 65);
          
      offScrGraphics.setColor(Color.magenta);
      buf="Click on mouse to begin game.";
      offScrGraphics.drawString(buf,(width-fontMetrics.stringWidth(buf))/2,j+50);

      gameMode=limbo;
      paddleScore[you]=paddleScore[pc]=ballsUsed=0;
      timeToServe=0;
      return;
   }

   public void showFadeOut()
   {
      int i;
      int j=topStatusBar/2;                   

      fadeCt+=4;
      offScrGraphics.setColor(Color.black);
      if (fadeCt <= j)
      {
         offScrGraphics.fillRect(0,j-fadeCt  ,width,4);
         offScrGraphics.fillRect(0,j+fadeCt-4,width,4);
      }
      else
      {
         offScrGraphics.setColor(Color.green);
         offScrGraphics.fillRect(0,0,width,topStatusBar);
         if (ballsUsed >= maxBalls)  gameMode=gameOver;
         else    gameMode=normalPlay;
         showNewScore();
      }
      return;
   }

/*   public boolean handleEvent(Event e)
   {
      if (e.id == Event.MOUSE_DOWN)
      {
         if (gameMode==limbo)
         {
            gameMode=fadeOut;
            fadeCt=0;
         }
      }
      return super.handleEvent(e);
   }
  */      
        
   public void mouseMoved( MouseEvent e )
   {
      if (e.getY() < 0) 
         yPaddle[you]=0;
      else if (e.getY() > yPaddleMax) yPaddle[you]=yPaddleMax;
      else    yPaddle[you]=e.getY();
      
   }

   public void mouseExited( MouseEvent e ) {}
   public void mouseDragged( MouseEvent e ) {}
   public void mousePressed( MouseEvent e ) {}
   public void mouseReleased( MouseEvent e ) {}
   public void mouseEntered( MouseEvent e ) {
      if (gameMode==limbo)
      {
         gameMode=fadeOut;
         fadeCt=0;
      }
   }
   public void mouseClicked( MouseEvent e )
   {
      
   }

             
                
   public void update (Graphics g)
   {
      if (gameMode != limbo)
      {
         if (gameMode==fadeOut)   showFadeOut();
         else if (gameMode==gameOver)   showScoreBoard();
         else if (gameMode==openingScreen)   showOpeningScreen();
         else    showNormalPlay();               
         paint(g);
      }
   }

   public void paint(Graphics g)
   {
      g.drawImage(offScrImage,0,0,this);
   }       
}