import java.awt.*;
import java.applet.Applet;

public class otto extends Applet implements Runnable {

/* Dimensioni previste dell'applet */
private final int MAXX = 240;
private final int MAXY = 240;
/* Dimensioni della pallina */
private final int DIMX = 24;
private final int DIMY = 24;

private final int STEP = 10;  /* Singolo movimento in pixel pallina verde*/
private final int RSTEP = 20; /* Singolo movimento in pixel pallina rossa*/

/* Costanti per la direzione del movimento */
private final int E = 0;
private final int S = 1;
private final int N = 2;
private final int W = 3;

/* Coordinate correnti della pallina verde */
private int x = MAXX / 3;
private int y = 0;
/* Variabile di stato: direzione corrente pallina verde */
private int dir = E;

/* Coordinate correnti della pallina rossa */
private int xr = 0;
private int yr = MAXY / 2;
/* Variabile di stato: direzione corrente pallina rossa */
private int dirr = E;

/* Array delle velocita' delle due palline */
private int speed [] = { STEP, RSTEP};

private Image dot = null;               /* La pallina verde */
private Image rdot = null;              /* La pallina rossa */
private volatile boolean sem1 = false;  /* Semaforo di sinistra */
private volatile boolean sem2 = false;  /* Semaforo di desrtra */

Thread avanza = null;                   /* Thread della pallina verde */
Thread ravanza = null;                  /* Thread della pallina rossa */

  /* Init deve solamente lanciare il caricamento delle palline */
  public void init () {
    dot = getImage(getCodeBase(), "lista.gif");
    rdot = getImage(getCodeBase(), "listarossa.gif");
  }
	
  /* Deve far partire l'animazione */	
  public void start() {
    /* Thread della pallina verde */
    if (avanza == null) {                       // Se il thread non esiste ancora
      avanza = new Thread(this, "lista");       // Lo crea 
      avanza.start();                           // E lo fa partire
    }
    /* Thread della pallina rossa */
    if (ravanza == null) {                      // Se il thread non esiste ancora
      ravanza = new Thread(this, "listarossa"); // Lo crea 
      ravanza.start();                          // E lo fa partire
    }
  }

  /* Movimento pallina verde */        
  private synchronized void ballMove () {
    switch (dir) {                 // A seconda dllo stato (la direzione corrente)
      case E :                     // Verso Est
        x += STEP;                 // Aumenta la X
        if (x >= (2 * MAXX) / 3) { // Se arriva al bordo destro
          dir = S;                 // Cambia direzione (va a Sud)
        }
        break;
      case S :             // Verso Sud
        y += STEP;         // Aumenta la Y

        /* Gestione dell'incorcio destro e del semaforo destro */
        if (y >= (MAXY / 2) - DIMY && 
          y < (MAXY / 2) - DIMY + STEP) {  // Entra nella zona di scontro
          if (sem2) {                      // Se il semaforo destro e' rosso
            try {
              wait ();                     // (attende sull'applett) Attende il verde
            } catch (Exception e) {
            }
          }
          sem2 = true;                     // Accende il semaforo destro
        } else if (y >= (MAXY / 2) + DIMY &&
          y < (MAXY / 2) + DIMY + STEP) {  // Esce dalla zona di scontro
          sem2 = false;                    // Spegne il semaforo destro
          notifyAll ();                    // Sblocca i thread in attesa su quel semaforo
        }

        if (y >= (2 * MAXY) / 3) { // Se arriva al bordo inferiore
          dir = W;                 // Cambia direzione (va a Ovest)
        }
        break;
      case W :               // Verso Ovest
        x -= STEP;           // Cala la X
        if (x <= MAXX / 3) { // Se arriva al bordo sinistro
          dir = N;           // Cambia direzione (va a Nord)
        }
        break;
      case N :             // Verso Nord
        y -= STEP;         // Cala la Y

        /* Gestione dell'incorcio sinistro e del semaforo sinistro */
        if (y >= (MAXY / 2) + DIMY &&
          y < (MAXY / 2) + DIMY + STEP) {  // Entra nella zona di scontro
          if (sem1) {                      // Se il semaforo sinistro e' rosso
            try {
              wait ();                     // (attende sull'applett) Attende il verde
            } catch (Exception e) {
            }
          }
          sem1 = true;                     // Accende il semaforo sinistro
        } else if (y >= (MAXY / 2) - DIMY && 
          y < (MAXY / 2) - DIMY + STEP) {  // Esce dalla zona di scontro
          sem1 = false;                    // Spegne il semaforo sinistro
          notifyAll ();                    // Sblocca i thread in attesa su quel semaforo
        }

        if (y <= 0) {      // Se arriva al bordo superiore
          dir = E;         // Cambia direzione (va di nuovo a Est)
        }
        break;
    };
  }
    
  /* Movimento pallina verde */        
  private synchronized void rballMove () {
    switch (dirr) {
      case E :                  // Verso Est
        xr += RSTEP;            // Aumenta la X
        	    	
        /* Gestione dell'incorcio sinistro e del semaforo sinistro */
        if (xr >= (MAXX / 3) - DIMX &&
          xr < (MAXX / 3) - DIMX + RSTEP) { // Entra nella zona di scontro
          if (sem1) {                       // Se il semaforo sinistro e' rosso
            try {
              wait ();                      // (attende sull'applett) Attende il verde
            } catch (Exception e) {
            }
          }
          sem1 = true;                      // Accende il semaforo sinistro
        } else if (xr >= (MAXX / 3) + DIMX &&
          xr < (MAXX / 3) + DIMX + RSTEP) { // Esce dalla zona di scontro
          sem1 = false;                     // Spegne il semaforo sinistro
          notifyAll ();                     // Sblocca i thread in attesa su quel semaforo
        }
        	    	       	    	
        /* Gestione dell'incorcio destro e del semaforo destro */
        if (xr >= (2 * MAXX / 3) - DIMX &&
          xr < (2 * MAXX / 3) - DIMX + RSTEP) { // Entra nella zona di scontro
          if (sem2) {                           // Se il semaforo destro e' rosso
            try {
              wait ();                          // (attende sull'applett) Attende il verde
            } catch (Exception e) {
            }
          }
          sem2 = true;                          // Accende il semaforo destro
        } else if (xr >= (2 * MAXX / 3) + DIMX &&
          xr < (2 * MAXX / 3) + DIMX + RSTEP) { // Esce dalla zona di scontro
          sem2 = false;                         // Spegne il semaforo destro
          notifyAll ();                         // Sblocca i thread in attesa su quel semaforo
        }

        if (xr >= MAXX) {     // Se arriva al bordo inferiore
          dirr = S;           // Cambia direzione (va a Sud)
        }
        break;
      case S :                // Verso Sud
        yr += RSTEP;          // Aumenta la Y
        if (yr >= MAXY) {     // Se arriva al bordo inferiore
          dirr = W;           // Cambia direzione (va a Ovest)
        }
        break;
      case W :                // Verso Ovest
        xr -= RSTEP;          // Cala la X
        if (xr <= 0) {        // Se arriva al bordo sinistro
          dirr = N;           // Cambia direzione (va a Nord)
        }
        break;
      case N :                // Verso Nord
        yr -= RSTEP;          // Cala la Y
        if (yr <= MAXY / 2) { // Se arriva al bordo superiore
          dirr = E;           // Cambia direzione (va di nuovo a Est)
        }
        break;
    };
  }

  /* Metodo che esegue il 'lavoro duro' - azione */
  public void run() {
    // Questo metodo viene invocato per ogni thread dell'Applet, quindi deve
    // eseguire il lavoro solo nel caso il thread sia quello dell'animazione,
    // uno o l'altro
    if (Thread.currentThread() == avanza) {  // Thread pallina verde
      // Il loop termina quando l'attributo avanza viene messo a null in stop()
      while (avanza!= null) {
        ballMove ();                         // Movimento pallina verde - gestisce semaforo
        repaint();                           // Ridisegna il cmapo
        try {
            Thread.sleep(100);               // Attende 100 mS
          } catch (InterruptedException e){  // Nessuna azione
        }
      }
    } else if (Thread.currentThread() == ravanza) {
      // Il loop termina quando l'attributo ravanza viene messo a null in stop()
      while (ravanza!= null) {
        rballMove ();                         // Movimento pallina rossa - gestisce semaforo
        repaint();                            // Ridisegna il cmapo
        try {
          Thread.sleep(100);                  // Attende 100 mS
        } catch (InterruptedException e){     // Nessuna azione
        }
      }
    }
  }

  /* Se usciamo dalla pagina, elimina il thread */
  public void stop() {
    avanza = null;  // se 'avanza' diventa 'null', il metodo 'run' (istanza pallina verde) termina
    ravanza = null; // se 'avanza' diventa 'null', il metodo 'run' (istanza pallina rossa) termina
  }
	
  /* Funzione di disegno che semplicemente disegna le palline alle coordinate fissate ed i semafori */	
  synchronized public void paint(Graphics g) {
    g.drawImage (dot, x, y, this);                             // Pallina verde
    g.drawImage (rdot, xr, yr, this);                          // Pallina rossa
    g.setColor ((sem1) ? Color.red : Color.green);             // Colore semaforo sinistro
    g.fillRect (MAXX/3 - 20, MAXY / 2 - 20 , 10 , 10);         // Semaforo sinistro
    g.setColor ((sem2) ? Color.red : Color.green);             // Colore semaforo destro
    g.fillRect (2*MAXX/3 + 10 + DIMX, MAXY / 2 - 20 , 10 , 10);// Semaforo destro
  }
}