import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import Cercle;

/** Applet de démonstration des threads et d'animation en double buffer. Il reste encore à corriger le
    calcul de la zone d'évolution des cercles en fonctiond es bordures de l'applet (insets) */
    
public class AnimCircle extends Applet implements Runnable,ActionListener {
    Thread threadDeApplet;
    Vector listeCercles = new Vector();
    // Pour le double buffer
    Image offScrImage;
    Graphics offScrGC;
    int largeur, hauteur;
    
    boolean use_double_buffer = false;

    // Un constructeur dans une Applet ? Très pratique pour gérer les événements !
    public AnimCircle() {

        // Classe anonyme interne pour gérer l'événement resize()
        ComponentListener resizeCallback = new ComponentAdapter() {
            public void componentResized(ComponentEvent e) {
                // Définition de la zone d'évolution des cercles. Comme l'applet est un 
                // Container, la taille de la zone où on dessine est égale à
                // la taille de l'applet - la taille des bordures (insets)
                largeur = getSize().width - (getInsets().left + getInsets().right);
                hauteur = getSize().height - (getInsets().top + getInsets().bottom);
                
                Cercle.setZoneEvolution(largeur, hauteur);

                // Le double buffer = une image mémoire de la taille de la zone d'évolution
                // des cercles
                offScrImage = createImage(largeur, hauteur);
                                          
                // Récupération d'un contexte graphique pour dessiner dans le double buffer.
                offScrGC = offScrImage.getGraphics();
                offScrGC.setColor(Color.red);

                System.out.println("Applet retaillée !");
            }
        }; // Fin de la classe anonyme

        // On indique qu'en cas de retaillage de l'Applet, c'est l'objet resizeCallback
        // qui va traiter l'événement
        addComponentListener(resizeCallback);
    }


    public void init() {

        // Creation de cercles animés
        listeCercles.addElement(new Cercle(100, 100, 10));
        listeCercles.addElement(new Cercle(100, 100, 20));
        listeCercles.addElement(new Cercle(100, 100, 30));

        // bouton pour suspendre l'animation des cercles
        Button b1 = new Button("Suspendre l'animation");
        add(b1);
        b1.addActionListener(this);

        // bouton pour reprendre l'animation des cercles
        Button b2 = new Button("Reprendre l'animation");
        add(b2);
        b2.addActionListener(this);

        // bouton pour ajouter un nouveau cercle
        Button b3 = new Button("Ajouter un cercle");
        add(b3);
        b3.addActionListener(this);
        
        // bouton pour switcher le double buffer on/off
        Button b4 = new Button("db on/off");
        add(b4);
        b4.addActionListener(this);
}

    public void start(){
        // Si le thread de l'applet vaut null
        if(threadDeApplet == null) {
             // On crée d'un Thread à partir de l'applet et on le lance
             threadDeApplet = new Thread(this);
             threadDeApplet.start();
        }
        else {
            // Le thread existe déjà, on le réveille
            threadDeApplet.resume();
        }

        // On réveille les cercles...
        Cercle.resume();
    }

    public void stop() {
        // On suspend tous les threads : celui de l'applet et les cercles
        threadDeApplet.suspend();
        Cercle.suspend();
    }

    /** Tache de fond executee par le Thread. Effectue l'animation du cercle en
        modifiant ses coordonnees et en invoquant repaint(), ce qui provoque l'execution
        de paint(). Notez que run() doit impérativement contenir une boucle afin de ne pas
        rendre la main tout de suite.
    */
    public void run() {

        while(true) {
            // On demande à redessiner le cercle
            repaint();

            // On "dort" 10 millisecondes
            try {
                threadDeApplet.sleep(10);
            } catch(InterruptedException e) {
                System.out.println("Erreur dans le sleep(10);");
                e.printStackTrace();
            }
        }
    }

    /** Appelé lors d'un click sur un des boutons de l'applet */
    public void actionPerformed(ActionEvent evt) {
        String arg = evt.getActionCommand();

        if(arg.equals("Suspendre l'animation")) {
            threadDeApplet.suspend();
            Cercle.suspend();
        } else if(arg.equals("Reprendre l'animation")) {
            threadDeApplet.resume();
            Cercle.resume();
        } else if(arg.equals("Ajouter un cercle")) {
            listeCercles.addElement(new Cercle(100, 100, (int) (10 + Math.rint(Math.random() * 20))));
        } else if (arg.equals("db on/off")) {
            use_double_buffer = !use_double_buffer;
        }
    }


    /** On surcharge update()pour le cas où on fait du double buffer. On ne veut pas 
        effacer à la fois la fenêtre graphque de l'applet (ce que fait par défaut update()
        Et effacer en plus le double buffer dans paint() */
    public void update(Graphics g) {
        
        // Si on ne fait pas de double buffering, il faut effacer la fenêtre graphique
        if(!use_double_buffer)
            g.clearRect(0, 0,largeur, hauteur); 
            
        paint(g);
    }
    
    public void paint(Graphics gc) {
        // appelé automatiquement par l'AWT, ou sur demande par repaint()
        Enumeration liste = listeCercles.elements();
        Cercle cercle;

        if(use_double_buffer) {
            // On efface le double buffer
            offScrGC.clearRect(0, 0, largeur, hauteur);
        
            while(liste.hasMoreElements()) {
                cercle = (Cercle) liste.nextElement();

            
                // On dessine un cercle dans le double buffer
                offScrGC.drawOval(cercle.getX() - cercle.getRayon(),
                        cercle.getY() - cercle.getRayon(),
                        2 * cercle.getRayon(),
                        2 * cercle.getRayon());
            }
        
            // On recopie le double buffer dans l'Applet
            gc.drawImage(offScrImage, 0, 0, this);
       } else {
           while(liste.hasMoreElements()) {
               cercle = (Cercle) liste.nextElement();

            
               // On dessine un cercle dans le double buffer
               gc.drawOval(cercle.getX() - cercle.getRayon(),
                        cercle.getY() - cercle.getRayon(),
                        2 * cercle.getRayon(),
                        2 * cercle.getRayon());
           }
       }
   }
}
