/*
 * Applet : Pavages d'ordre 5
 */

// Bibilothèques utilisées 

/*
 * Gestion de l'interface utlisateur : Swing ou AWT ?
 * Je déconseille Swing pour l'instant.
 */

import java.util.*;
import java.awt.*;
import java.awt.event.*;

/*
 * Classe principale (tout est dedans)
 */

public class Puzzle 
    extends Base
       // J'ai écrit la classe Base qui regroupe des fonctionnalités
       // communes à mes applets, voir description.txt
    implements 
	// ActionListener,
	MouseListener,
	MouseMotionListener,
	KeyListener
       // signifie qu'il réagit aux actions suivantes 
       // ActionListener   : actionPerformed(...)      -> bouton
       // MouseListener    : ... -> souris
       // MouseMotionListener : ... -> souris aussi
       // KeyListener      : clavier
{
    /*
     *                             Champs
     */

    /*
     * Je mets ici les constantes utilisées dans le placement des 
     * zones de l'applet
     */
    public static int
	XOFFSET=20, // position de l'aire de jeu (Upper Left corner)
	YOFFSET=20, // idem
	PIECES_AREA_WIDTH=100;

    public static double 
	PIECES_AREA_SCALE=50;

    public Rectangle bounds; // mémorise la taille de la zone d'affichage

    public int x1,x2,y1,y2;  // mémorise les coords de la zone de pose
                             // des pièces

    /*
     * les deux figures de base
     */
    /*
    OLD :
    public static double[][][]
	POLS={{{0,0},{1,0},{1.309016994374947,.9510565162951535}
                       ,{0.309016994374947,.9510565162951535}}
	     ,{{0,0},{1,0},{1.809016994374947,.4370160244488210}
	               ,{0.809016994374947,.4370160244488210}}
	     };
    */
    public static double[][][]
	POLS={{{-.6545084971874738,-.4755282581475768}
	      ,{0.3454915028125262,-.4755282581475768}
              ,{0.6545084971874738,.4755282581475768}
              ,{-.3454915028125262,.4755282581475768}
              }
	     ,{{-.9045084971874738,-.2938926261462365}
	      ,{0.0954915028125262,-.2938926261462365}
              ,{0.9045084971874738,.2938926261462365}
              ,{-.0954915028125262,.2938926261462365}
              }
	     };
    //.9045084971874738, .2938926261462365

    /*
     * Pièce tenue
     */
    public int holdedX;
    public int holdedY;
    public int holdedType;
    public int holdedAngle;

    /*
     * Les deux polygônes du panier (coordonnées écran)
     */
    public Polygon P0, P1;

    /*
     * Table des cos et sin des multiples de Pi/10
     */
    public double[] COS;
    public double[] SIN;

    // ...

    public boolean holdingPiece;

    public Vector droppedPieces;

    /*
     *                          Sous-classes
     */

    /*
     * Pièces posées
     */
    class Tile 
    {
	double x,y;
	int angle;
	int type;
    }

    /*
     *                           Méthodes
     */

    /*
     * Crée un polygône à coords entière pour affichage à l'écran
     * on donne l'offset, l'échelle, la rotation en tour/10 et le
     * numéro du polygone dans la liste POLS
     */
    public Polygon rotateAndScale(double xoff, double yoff, double scale,
				  int angle, int index)
    {
	Polygon p=new Polygon();
	double u,v;
	for(int i=0;i<4;i++) {
	    u=COS[angle]*POLS[index][i][0] - SIN[angle]*POLS[index][i][1];
	    v=SIN[angle]*POLS[index][i][0] + COS[angle]*POLS[index][i][1];
	    // Remarquer le signe '-' dans la formule suivante
	    p.addPoint((int)(xoff+scale*u),(int)(yoff-scale*v));
	}
	return p;
    }

    /*
     * Création des deux polygônes du panier
     * Appelé à chaque Paint()
     */
    public void createBasketPolygons() {
	storeBounds();
	P0=rotateAndScale(x2+(XOFFSET+PIECES_AREA_WIDTH)/2,80,PIECES_AREA_SCALE,0,0);
	P1=rotateAndScale(x2+(XOFFSET+PIECES_AREA_WIDTH)/2,150,PIECES_AREA_SCALE,0,1);
    }

    /*
     * Mémorise la taille de la zone d'affichage et calcule quelques constantes
     */
    public void storeBounds() {
	bounds=getBounds(); // prendre la taille de la zone
	                              // d'affichage de la fenêtre
	
	x1=XOFFSET;
	x2=bounds.width-XOFFSET-PIECES_AREA_WIDTH;
	y1=YOFFSET;
	y2=bounds.height-YOFFSET;
    }

    /*
     * Constructeur de la classe principale
     */
    public void init() {

	// Toujours appeler le constructeur de la classe mère

	super.init();

	// Message d'aide

        StringBuffer auxString=new StringBuffer(
  "Mode d'emploi de l'applet\n"
+ "\n" 
+ "Affichage :\n"
+ "\n"
+ "  -  Jeu de pavages d'ordre 5, \n"
+ "     type \"Puzzle\".\n"
+ "\n"
);
	auxString.append(
  "\n"
+ "Commandes :\n"
+ "\n"
+ "  -  Cliquer sur une pièce dans la réserve pour la prendre.\n"
+ "  -  La faire éventuellement tourner avec le clavier.\n"
+ "  -  La déposer dans la zone blanche avec un nouveau click.\n"
+ "  -  La touche '?' ou un click droit ouvrent cette fenêtre d'information.\n"
+ "\n" 
+ "Puzzle, ©2003 Arnaud Chéritat\n"
);

	// MyHelpApplet prendra en charge la gestion du message
	// d'aide, dont le contenu est défini par setHelpText("contenu");

        setHelpText(auxString.toString());

	// Création du contenu et affectation de celui-ci à l'applet

	//        makeContent();

	addKeyListener(this);
	addMouseListener(this);
	addMouseMotionListener(this);

	/*
	 * Initialisation des variables utilisateurs
	 */

	// Au début, on ne tient aucune pièce
	holdingPiece=false;

	// Aucune pièce posée
	droppedPieces=new Vector();

	/*
	 * Initialisation mathématique
	 */

	// Remplissage du tableau des cos, sin de k*Pi/10
	COS=new double[10];
	SIN=new double[10];
	for(int k=0;k<10;k++) {
	    COS[k]=Math.cos(((double)k)*Math.PI/10.0);
	    SIN[k]=Math.sin(((double)k)*Math.PI/10.0);
	}

	// APRES remplissage des cos,sin : créér les polygônes du panier
	createBasketPolygons(); // Bien placé ???
    }

    /* Pour gérer l'affichage de la class Base,
     * il faut modifier la méthode offPaint
     */
    public void offPaint(Graphics g) {
  
	// Rectangle mathématique en blanc

	storeBounds();

	// Remplissage en blanc
	g.setColor(Color.white);
	g.fillRect(x1, y1, x2-x1, y2-y1);
	// Tracé du bord en noir : remarquer les "-1" en plus
	g.setColor(Color.black);
	g.drawRect(x1, y1, x2-x1-1, y2-y1-1);

	// Les deux pièces

	createBasketPolygons();

	// Remplissage en vert
	g.setColor(Color.green);
	g.fillPolygon(P0);
	// Tracé du bord en noir
	g.setColor(Color.black);
	g.drawPolygon(P0);

	// Remplissage en bleu
	g.setColor(Color.blue);
	g.fillPolygon(P1);
	// Tracé du bord en noir
	g.setColor(Color.black);
	g.drawPolygon(P1);

	Shape savedClip=g.getClip();
	g.setClip(x1+1,y1+1,x2-x1-2,y2-y1-2);
	Tile tile;
	Polygon tp;
	int s=droppedPieces.size();
	for(int i=0 ; i<s ; i++) {
	    tile=(Tile) droppedPieces.elementAt(i);
	    tp=rotateAndScale(tile.x,tile.y,PIECES_AREA_SCALE,tile.angle,tile.type);
	    // Remplissage
	    g.setColor(tile.type==0 ? Color.green : Color.blue);
	    g.fillPolygon(tp);
	    // Tracé du bord en noir
	    g.setColor(Color.black);
	    g.drawPolygon(tp);	    
	}
	g.setClip(savedClip);

	if(holdingPiece) {
	    Polygon hp=rotateAndScale(holdedX,holdedY,
		      PIECES_AREA_SCALE,holdedAngle,holdedType);
	    // Remplissage
	    g.setColor(holdedType==0 ? Color.green : Color.blue);
	    g.fillPolygon(hp);
	    // Tracé du bord en noir
	    g.setColor(Color.black);
	    g.drawPolygon(hp);
	}

	/*	
	// Texte
	g.setColor(new Color(0.25f,0.5f,0.75f));
	g.setFont(myFont2);
	g.drawString("Vitesse : ",TEXTX,YOFFSET+SIZE+TEXTY);
	*/
    }

    // Interface MouseListener

    public void mouseClicked(MouseEvent e) {
	if((e.getModifiers() & InputEvent.BUTTON1_MASK)!=0) {
	    int x=e.getX();
	    int y=e.getY();
	    if(holdingPiece) {
		holdingPiece=false;
		if(x>x1 && x<x2 && y>y1 && y<y2) {
		    Tile tile = new Tile();
		    //		    tile.x=(x-xoff)/scale;
		    //		    tile.y=(y-yoff)/scale;
		    tile.x=x;
		    tile.y=y;
		    tile.type=holdedType;
		    tile.angle=holdedAngle;
		    droppedPieces.addElement(tile);
		}
		repaint();
	    }
	    else {
		if(P0.contains(x,y)) {
		    holdingPiece=true;
		    holdedType=0;
		};
		if(P1.contains(x,y)) {
		    holdingPiece=true;
		    holdedType=1;
		};
		if(holdingPiece) {
		    holdedAngle=0;
		    repaint();
		    holdedX=x;
		    holdedY=y;
		}
	    }
	}
    }
    public void mouseEntered(MouseEvent e) {
	// Do nothing
    }
    public void mouseExited(MouseEvent e) {
	// Do nothing
    }
    public void mousePressed(MouseEvent e) {
	// Do nothing
    }
    public void mouseReleased(MouseEvent e) {
	// Do nothing
    }

    // Interface MouseMotionListener

    public void mouseDragged(MouseEvent e) {
	// Do nothing
    }
    public void mouseMoved(MouseEvent e) {
	if(holdingPiece) {
	    int x=e.getX();
	    int y=e.getY();
	    holdedX=x;
	    holdedY=y;
	    // Ordonnons le redessin de la fenêtre
	    repaint();
	    // ETC...
	}
    }

    // Interface KeyListener

    public void keyPressed(KeyEvent e) {
	int left=-1;
	int center=0;
	int right=1;

	int turn=center;

	if(holdingPiece) {


	    switch(e.getKeyChar()) {
	    case 'a' : case 'A' : case 'q' : case 'Q' : case 'l' :
	    case 'L' : turn=left; break;
	    case 'z' : case 'Z' : case 'w' : case 'W' : case 'r' :
            case 'R' : turn=right; break;
	    }
	    if(e.getKeyCode()==KeyEvent.VK_LEFT) { turn=left; }
	    //	    if(e.getKeyCode()==KeyEvent.VK_KP_LEFT) { turn=left; }
	    if(e.getKeyCode()==KeyEvent.VK_RIGHT) { turn=right; }
	    //	    if(e.getKeyCode()==KeyEvent.VK_KP_RIGHT) { turn=right; }

	    if(turn==left) {
		holdedAngle+= (holdedType==0 ? 2 : 1);
		holdedAngle= (10+holdedAngle) % 10;
		repaint();
	    }
	    if(turn==right) {
		holdedAngle-= (holdedType==0 ? 2 : 1);
		holdedAngle= (10+holdedAngle) % 10;
		repaint();
	    }
	}

    }
    public void keyReleased(KeyEvent e) {
	// Do nothing
    }
    public void keyTyped(KeyEvent e) {
	// Do nothing
    }
 


    // Autres

    /*
     * Méthode "abstract" de la classe Base dont dérive cet applet.
     * Gère le Placement des composants en "absolute positioning"
     * Y mettre le code les positionnant --au premier affichage--
     */
    public void position() {
	// Rien pour l'instant
    }
    
    /* Cette fonction parle d'elle-même
     * elle n'est pas utilisée par les navigateurs actuels
     */
    public String getAppletInfo() {
         return "Puzzle, ©2003 Arnaud Chéritat";
    }
}
