/**
*	This applet displays a window.
*	In this window you play JavaBrick !! !
*	@author :sderhy@imaginet.fr
*
*/

import java.awt.*;
import java.awt.Event;
import java.applet.*;
import java.util.*;

//import java.applet.AudioClip;

public class JavaBrick extends Frame implements Runnable{


/**
*
*		Instance variables
*
*/

	AudioClip ClickSound;
	
private Image offScreenImage;
private Graphics offScreenGC;
protected final static int DIAM = 12; // le DIAM
protected final static int DIAM2 = DIAM*2; // le DIAM*2
protected final static int Largeur = DIAM*5; // la largeur de la raquette
protected final static int Epaisseur = DIAM; // la largeur de la raquette
protected final static int SensyMax = 15; // vitesse maximum en y
protected final static int SensxMax = 15; // vitesse maximum en y
protected final static int Yop = 100+SensyMax; // permet de s'occuper des briques
								// qu'au dessus de Yop = Yopposé
Label label;
int score = 0 ;
boolean pleaseRun = true ;											

// Concerne les Briques	:
	int xBrick, yBrick = 100;	// x et y d'un brique
	int numRow = 4;						// nombre de rang
	int row;
	final static int  kWBrick=20, kHBrick=10;// taille des briques
	final static int Gap = 5; // espacement entre les briques
	protected Vector	LesBriques =new Vector();		// tableau de briques
	boolean HasToBeUpdated = true ;// concerne la reconstruction des briques

// Concerne la raquette :
int xPos = 0,yPos = 105, xMax,yMax; // xPos,yPos : position de la balle
int sensx =4, sensy= 7;
int deltax;
int xRac , yRac , Lastx, Lasty ;
int numLives = 4 ;
boolean ClickInTheRacket = false;

Graphics g ; 
Thread runner;
//Color theColor = Color.blue;
// Image offscrImg;	//  On  crée une image Offscreen

/** Constructeur ,
 *	 L'argument est le titre sous la forme d'une chaîne
 *  de caractères
 */
public JavaBrick(String title ,AudioClip ClickSound) {
 		
 				super(title);
 				this.ClickSound = ClickSound;//son.au 
 				this.setLayout( new BorderLayout());
 				label = new Label("Score = " + score,Label.CENTER);
 				this.add("North",label);	
 				resize(250,400);
   			setResizable(false);
        		setBackground(Color.white);
        		this.show();
        		g = this.getGraphics();
        		Rectangle Rect = this.bounds();
  				xMax = Rect.width;
  				xRac= xMax/2;
  				
  				yMax = Rect.height ;
 				yRac= yMax-50;  
 				
 				initJavaBrick();

 	    		paint(g);
        		start();
        	
    }// fin du constructeur
    
    
/* **************************< démarrage du Thread  >***************** */
	public void start()
	{	if (runner == null)
		{	runner = new Thread( this); // creation du Thread
			
			runner.start() ;			// Démarrage du Thread
		}
	}// fin de start
/* **************************< arrêt du Thread  >********************* */	
		public void stop()
	{		if(runner != null)
		{	runner.stop();
			runner = null;
		}
	}// fin de stop
/* **************************< Corps du Thread  >*********** */		
	public void run ()
	{	while(pleaseRun){
			 dessine();
			 runner.setPriority(10);
			 try { runner.sleep(50);} catch(InterruptedException E) {};
			 }
	}// fin de run

/* ***************************< Chaine d'évenements>******** */ 
   public boolean handleEvent(Event e) {
        if (e.id == Event.WINDOW_DESTROY) 
        			{	this.stop();
        				this.hide();
               	this.dispose();
              	  	return  true;
            	} 
       	 else if ( e.id == Event.MOUSE_DOWN ){
        			ClickInTheRacket = handleMouseClick(e.x,e.y);

    				if( !pleaseRun )	// On test ici si la balle est en mouvement
    						looseOneLife();
    				return true;
    				}
       	else if (e.id == Event.MOUSE_DRAG){
       			handleMouseDrag(e.x,e.y);
					return true;
					}// fin de mouseDrag
       	else if (e.target == this && e.id == Event.KEY_ACTION) {
					Winball_KeyPress(e);
					return true;
					}
     
     		else return super.handleEvent(e);
    }// fin de handleEvent()

/* ****************< paint(g) >***************/    	
	public void paint(Graphics g)
   {  		
   	//if(yPos < Yop ) 
   	paintJavaBrick(g);
		paintBall(g);
   	paintRacket(g);

    }// fin de paint(g)
    
// /* *************** on redéfini  update  ***************************/
   public void update(Graphics g) {
		if (offScreenImage == null) {
			// offScreenImage not yet created; create it
			offScreenImage = createImage(size().width,
 				size().height);
 				
 			// get the off-screen graphics context
			offScreenGC = offScreenImage.getGraphics();
			}
			// paint the background on the 
			// off-screen graphics context
			offScreenGC.setColor(Color.pink);//getBackground());
			offScreenGC.fillRect(0, 0, size().width, size().height);
			offScreenGC.setColor(getForeground());

		
		
		// first, draw the current frame to the off-screen GC
		//g.clipRect(xPos-10,yPos-10,30,30);
		paint(offScreenGC);

		// draw the image to the on-screen Graphics object g
		g.drawImage(offScreenImage, 0, 0, null);
	}
// fin de update()
  	

 
    protected void dessine()
    {			
    				xPos += sensx;
    				yPos += sensy;
    				 repaint();  
    			//	try { Thread.sleep(30);} catch(InterruptedException E) {};

    			  
    			//					g.setColor(getBackground());
				//					g.fillRect(xPos,yPos,DIAM,DIAM);

/* Cas ou la raquette est touchee :								 */

					if ( ((yPos+DIAM) > yRac ) &&			//  balle au sud de raquette
							 ( (xPos+DIAM) > xRac ) &&		//  && balle droite de xRac
							 	 (xPos< (xRac+Largeur))		//  && balle dans la raquette
							 	&& (yPos< yRac)
							 	&&(sensy>0) 				//  on evite de dribbler !
						)	//fin d'if
							{	 
								sensy = -1*sensy+deltax;
								sensx = sensx +deltax;	
								if (ClickSound != null) ClickSound.play();// petit son
								if (sensy > 0 ) sensy = -1;
							}
						
/* Rebonds sur les parois de la fenêtre:	*/				
    		 		 if(	(xPos + DIAM) > xMax && (sensx>0) 	) sensx *=-1;// rebond à droite
    		 		 if(	(xPos) < 0 && (sensx<0) 	)sensx *=-1;// rebond à gauche
    		 		 
/**
*	Ici on est au sud, c'est ici que l'on perd !
*/    		 		 
 					 if(	(yPos + DIAM) > yMax && (sensy>0) 	) looseTheBall(); //sensy *=-1;// rebond en bas

/**
* Attention!!! on appelle ici playTheGame pour jouer lorsque yPos > Yop
*/ 					 
 					 if(   yPos < Yop) playTheGame();// on joue de l'autre côté !!!!
 					 
    		 		 

/* Régulation de SenyMax et SensxMax :  vitesse maxi de la balle en x et y:  */ 		 		 
    		 		 if(	sensy > SensyMax )	sensy= SensyMax;	
    		 		  if(	sensy < -SensyMax )	sensy= -SensyMax;
 //   		 		  if( sensy == 0) 	sensy = -1;	
 					 if(	sensx > SensxMax )	sensx= SensxMax;	
    		 		  if(	sensx < -SensxMax )	sensx= -SensxMax;
    //if( sensx == 0) 	sensy = -1;	// ici décommenté car sensx peut etre egal a 0
	//	if(sensx==0 && sensy == 0) sensy =-2;
	if(sensy == 0) sensy = -3 ;
	}// fin de dessine()
	
	
	protected void paintRacket(Graphics gr){
		gr.setColor(Color.gray);
  		gr.fillRect(xRac,yRac,Largeur,Epaisseur);
  		
    	}
    	
	
	protected void paintBall(Graphics gr){
		gr.setColor(Color.blue);
  		gr.fillOval(xPos,yPos,DIAM,DIAM);
  		
    	} 
    
  protected boolean handleMouseClick(int x, int y)
   { 
   		
   			if (
   				(x > xRac  &&  x< xRac+Largeur) &&
   				(y > yRac &&   y< yRac+Epaisseur)
   				)		
   						{ Lastx=x; Lasty =y ;return true ;}
   					else return false;
   		 
   }// FIN DE HandleMouseClick()  
      	
    protected void handleMouseDrag(int x, int y)
   {
   	if ( ClickInTheRacket )// déplacement de la raquette :
   	{		
   			effaceRac();
			 	deltax = x - Lastx; // Ici on ne s'occupe pas de y
			 	xRac +=deltax;
   			if( (xRac+Largeur) > xMax)	{xRac = xMax-Largeur;Lastx=xMax;}
   			if( xRac < 0) 					{ xRac = 0; Lastx=0;}
   			Lastx= x ;
   			
   				
   	}// fin de if( ClickInthe Racket )
   	return;
   }  
     		
   	void Winball_KeyPress(Event e) {
			int flags = e.modifiers;
	
			if( e.key == Event.RIGHT)
		   		{	
		   			effaceRac();
		   			xRac = xRac + Largeur/2;
		   			if( xRac > xMax) xRac= xMax;
		   		
		   		}
		   if( e.key == Event.LEFT)
						{
						effaceRac();
						xRac = xRac - Largeur/2	;
						if(xRac < 0) xRac = 0;
						}
	}
protected void playTheGame()
	{
		// redéfinir ici le jeux  de l'autre côté 
		// override here this method to play the game 
		// when yPos is > to Yop
		Rectangle BallRect = new Rectangle(xPos,yPos,DIAM,DIAM);
					// rectangle correspondant à la balle
		BrickRect LaBrique ;
		
		for( int i = 0 ; i < LesBriques.size() ; i++ ){
			LaBrique = (BrickRect) LesBriques.elementAt(i);
			
			if(BallRect.intersects(LaBrique)){	
						if (LaBrique.isVisible){
								sensy *=-1;
								if(sensy > 0 && sensy < 3) sensy = 5;
								//sensx *=-1;
								LaBrique.isVisible= false;
								if (ClickSound != null) ClickSound.play();// petit son
								LesBriques.insertElementAt(LaBrique,i);
								score++;
								label.setText("Score = "+ String.valueOf(score));
								HasToBeUpdated = true;
								
						}
						else HasToBeUpdated = false ;
						break;
			}// fin de if intersects...intersects(Rectangle r); 
		}// fin de for...
		
		
		
		
		
		if(	(yPos) < 0 && (sensy<0) 	)sensy *=-1;// rebond au Sud
	
	}
protected void effaceRac()
	{
		/*g.setColor(getBackground());
   	g.fillRect(xRac,yRac,Largeur,Epaisseur);
   */
   }
   
   
   
	protected void initJavaBrick(){ // dessin des briques 
			for (row = 0; row < numRow; row++){
				for(xBrick= 2; xBrick < xMax; xBrick += kWBrick+ Gap){
					BrickRect LaBrique = new BrickRect( xBrick, yBrick-row, kWBrick, 
											kHBrick, true );
					LesBriques.addElement(LaBrique);	// on remplit le vecteur
					}
				yBrick -= (Gap + kHBrick); // petit ecartement entre les rangs
			}
		}

		protected void paintJavaBrick(Graphics gr){
			//if(HasToBeUpdated)
			{
				BrickRect LaBrique ;
					for( int i=0 ; i < LesBriques.size(); i++){
						LaBrique	=(BrickRect)LesBriques.elementAt(i);
						LaBrique.drawBrick(gr);
					}
				}
		}// fin de paintJavaBrick
		


	protected void looseTheBall(){ // cas ou yPos > yMax
	//stop();
	pleaseRun =false;
	numLives --;	// on perd une vie !
	//	sensy *=-1; // instruction minimale.
   }
   
   
   protected void looseOneLife() {
   		
   		
   if(numLives > 0){
    	
    						pleaseRun = true ;
    						runner = null;
    						xPos = 125 ; yPos= 400; sensy= -6 ; sensx = 0;
    						start();
    						
    		
    				}// fin de if(numLives) 
   else gameOver();
   }
   
   
   protected void gameOver(){
   Graphics gr = this.getGraphics();
   gr.drawString(" Game over !!! ", 100,200);
   stop(); 
    }//fin de GameOver
   
   
 }// fin de Winball
 
 
 	
/* 
 
*/

			


/**	**********************************************************************
* 	Classe BrickRect : comportement d'une brique
*/	

class BrickRect extends Rectangle{
	
	boolean isVisible;				// pas encore détruite !
	
	// BrickRect.BrickRect : the constructor :

		public BrickRect(int x, int y, int w, int h, boolean Visible){
			super(x,y,w,h);
			this.isVisible = Visible;
			}
			
	// BrickRect.destroy
		public void destroy(Graphics g){
				this.isVisible = false ;
				this.drawBrick(g);
			}

	// BrickRect.drawBrick	:  the method : implique que le fond est blanc.
		public void drawBrick( Graphics g){
			if(isVisible){
				g.setColor(Color.magenta);
				g.fillRect(this.x,this.y,this.width,this.height);
				g.setColor(Color.black);
				g.drawRect( this.x,this.y,this.width,this.height);
			}
			else{
				g.setColor(Color.pink);		// pas sûr que getBackground() marche !
				g.fillRect ( this.x,this.y,this.width+2,this.height+2); // essayer clearRect()
			}
		
		}
}// fin de BrickRect