import java.awt.*;
import java.applet.*;
import java.awt.event.*;
/** Un applet per visualizzare e manipolare automi cellulari lineari
 * @author Giuseppe Zito
 * @author http://www.ba.infn.it/%7ezito/
 * @version 1.0
*/
public class Automa extends Applet
 implements Runnable,ActionListener {
 Thread animazione;
 Image o;
 Graphics og;
 AC cell1;
 int j =0;
 Button b1,b2,b3;
 Panel p1,p2;
 ACanvas disegno;
 TextField nvalori,nsiti,regola;
 int psize = 1;
 int wtime = 10;
 boolean scroll = true;
 boolean interfaccia = true;
 String s1="0110";
 int k=2;
 int r=1;
 int pixelCentrale = -1;
 
 
public void init(){
  readParameter();
  setLayout(new BorderLayout());
  disegno = new ACanvas(this);
  add("Center",disegno);
  disegno.Xscreen = getSize().width;
  disegno.Yscreen = getSize().height;
  disegno.npixel=(int)(disegno.Xscreen/psize);
  disegno.Xscreen=disegno.npixel*psize;
  System.out.println("init "+disegno.Xscreen+" "+disegno.Yscreen);
  o = createImage(disegno.Xscreen,disegno.Yscreen);
  og = o.getGraphics();
  cell1 = new AC(k,r,s1,this);
  if(interfaccia){
  b1 = new Button("Stop");
  b2 = new Button("Cambia prima riga");
  b3 = new Button("Passo");
  p1 = new Panel();
  p1.add(b1);p1.add(b2);p1.add(b3);
  add("North",p1);
  b1.addActionListener(this);
  b2.addActionListener(this);
  b3.addActionListener(this);
  nvalori = new TextField(2);
  nsiti = new TextField(3);
  regola = new TextField(10);
  p2 = new Panel();
  p2.add(nvalori); p2.add(nsiti);
  p2.add(regola);
  add("South",p2);
  nvalori.setText(Integer.toString(cell1.K));
  nsiti.setText(Integer.toString(cell1.IR));
  regola.setText(cell1.rule());
  if(pixelCentrale > -1){
  if(pixelCentrale < k)cell1.setPixel(pixelCentrale);
    else cell1.setPixelRandom();
  }

  }
  }


 void readParameter(){
  String s;
  s = getParameter("k");
  if (s != null) k = Integer.parseInt(s);
  s = getParameter("r");
  if (s != null) r = Integer.parseInt(s);
  s = getParameter("regola");
  if( s!=null)s1=s;
  s = getParameter("attesa");
  if(s != null)wtime = Integer.parseInt(s);
  s = getParameter("scroll");
  if(s != null) scroll = Boolean.valueOf(s).booleanValue();
  s = getParameter("interfaccia");
  if(s != null) interfaccia = Boolean.valueOf(s).booleanValue();
  s = getParameter("pixelcentrale");
  if (s != null) pixelCentrale = Integer.parseInt(s);

}
public void actionPerformed(ActionEvent e){
if(e.getSource()==b1){
if(e.getActionCommand()=="Stop"){
 b1.setLabel("Start");
 stop(); 
} else {
 b1.setLabel("Stop");
 start();
  }
  }
 if(e.getSource()==b2) {
  stop(); 
  j = 0;
  cell1.setWorld();
  start();
   }
 if(e.getSource()==b3) {
   stop();  
   aggiorna();
  disegno.repaint();
 }

 } 

/** Disegna un nuovo automa scelto a caso
*/
public void startrandom(){
stop();
   j = 0;
   cell1 = new AC(this);
   nvalori.setText(Integer.toString(cell1.K));
 nsiti.setText(Integer.toString(cell1.IR));
 regola.setText(cell1.rule());
   start();
}

/** Disegna l'automa indicato
 * @param K1  numero di colori 
 * @param IR1  dimensioni del vicinato 
 * @param s1  regola dell'automa  
*/
public void startautoma(int K1, int IR1, String s1){
stop();
   j = 0;
   cell1 = new AC(K1,IR1,s1,this);
   nvalori.setText(Integer.toString(cell1.K));
 nsiti.setText(Integer.toString(cell1.IR));
 regola.setText(cell1.rule());
   start();
}

/**Ridisegna l'automa ponendo il pixel centrale uguale al valore indicato e gli altri a 0
 * @param val colore del pixel centrale
*/
public void setPixel(int val){
 stop();
 j=0;
 cell1.setPixel(val);
 start();
 }

/**Ridisegna l'automa ponendo il pixel centrale uguale a un valore a caso e gli altri a 0
*/
public void setPixelRandom(){
 stop();
 j=0;
 cell1.setPixelRandom();
 start();
 }


/** Disegna un automa a caso col numero di colori e dimensioni del vicinato indicati 
 * @param K1  numero di colori 
 * @param IR1  dimensioni del vicinato 
*/
public void startautomar(int K1, int IR1){
stop();
   j = 0;
   cell1 = new AC(K1,IR1,this);
   nvalori.setText(Integer.toString(cell1.K));
 nsiti.setText(Integer.toString(cell1.IR));
 regola.setText(cell1.rule());
   start();
}

/** Disegna un automa a caso col numero di colori  indicato 
 * @param K1  numero di colori 
*/
public void startautomac(int K1){
   stop();
   j = 0;
   cell1 = new AC(K1,this);
   nvalori.setText(Integer.toString(cell1.K));
 nsiti.setText(Integer.toString(cell1.IR));
 regola.setText(cell1.rule());
   start();
}
/** Ridisegna l'automa con i quadratini della dimensione indicata
 * @param psize1 dimensioni in pixel del quadratino che rappresenta una cella
dell'automa
*/
public void setpsize(int psize1){
    stop();
    j = 0;
    psize = psize1;
    start();
}
/** Richiedi il disegno continuo dell'automa
  * @param scroll1 se vero indica che si deve avere un disegno continuo
*/
public void setscroll(boolean scroll1){
     scroll = scroll1;    
}

public void start(){
  if(animazione==null){
    animazione = new Thread(this);
    animazione.start();
    }
 }


public void stop(){
  if(animazione !=null)animazione=null;
  }


public void run(){
 while(animazione!=null){
  aggiorna();
  if(j >= (int)(disegno.Yscreen/psize) && !scroll)break;
  disegno.repaint();
  try {Thread.sleep(wtime);}
  catch(InterruptedException e){} 
 }
}


synchronized void aggiorna(){
  if(j < (int)(disegno.Yscreen/psize)){
    for(int i=0;i<disegno.npixel;i++){
      int nv = cell1.itestru(i); 
      og.setColor(cell1.c[nv]);
      og.fillRect(i*psize,j*psize,psize,psize);
     } 
     for(int i=0;i<disegno.npixel;i++)
      cell1.ioldv[i]=cell1.newv[i];   
   }
   else {
  og.copyArea(0,psize,disegno.Xscreen,
  disegno.Yscreen-psize,0,-psize);
  for(int i=0;i<disegno.npixel;i++){
      int nv = cell1.itestru(i); 
      og.setColor(cell1.c[nv]);
      og.fillRect(i*psize,disegno.Yscreen-psize,psize,psize);
     } 
     for(int i=0;i<disegno.npixel;i++)
      cell1.ioldv[i]=cell1.newv[i];   
   
}
  j= j +1;
}

}


/*
 Classe ACanvas - Canvas contenente il disegno dell'automa 
*/

class ACanvas extends Canvas{
Automa appl;
int Xscreen,Yscreen,npixel,psizeold;
ACanvas(Automa appl1){
  super();
  appl=appl1;
  psizeold=0;
}
 

synchronized public void paint(Graphics g){
 if(getSize().width != Xscreen || getSize().height != Yscreen || appl.psize != psizeold ){
 psizeold=appl.psize;
 Xscreen = getSize().width;
 Yscreen = getSize().height;
 npixel =(int)(Xscreen/appl.psize);
 Xscreen = npixel*appl.psize;
 System.out.println(Xscreen+" "+Yscreen+" "+npixel);
 appl.o = createImage(Xscreen,Yscreen);
 appl.og = appl.o.getGraphics();
 appl.j=0;
 appl.cell1.setWorld();
  if(appl.pixelCentrale > -1){
  if(appl.pixelCentrale < appl.k)appl.cell1.setPixel(appl.pixelCentrale);
    else appl.cell1.setPixelRandom();
  }
 }
 g.drawImage(appl.o,0,0,this);   }
 public void update(Graphics g){
 paint(g);}
}

/*
  Classe AC - rappresenta l'automa cellulare
*/

class AC {
Automa appl;
Color c[];
int K,IR,nmax;
int irule[] ,ioldv[],newv[];

 AC(Automa appl1){
appl = appl1;
K = (int)( Math.random()*10);
K = K +1;if(K>10)K=10;if(K==1)K=2;
setCol();  
IR = (int)(Math.random()*4+1);
nmax = (IR*2+1)*(K-1)+1;
 irule= new int[nmax];
 for(int i=0;i<nmax;i++){
    int irval = (int)(Math.random()*K);
    if(irval >=K)irval = K-1;
    irule[i] = irval;
    }
  setWorld();
}

AC(int K1,Automa appl1){
appl = appl1;
K = K1;
setCol();  
IR = (int)(Math.random()*4+1);
nmax = (IR*2+1)*(K-1)+1;
 irule= new int[nmax];
 for(int i=0;i<nmax;i++){
    int irval = (int)(Math.random()*K);
    if(irval >=K)irval = K-1;
    irule[i] = irval;
    }
  setWorld();
}

AC(int K1,int IR1, Automa appl1){
appl = appl1;
K = K1;
setCol();  
IR = IR1;
System.out.println(K+" "+IR);
nmax = (IR*2+1)*(K-1)+1;
 irule= new int[nmax];
 for(int i=0;i<nmax;i++){
    int irval = (int)(Math.random()*K);
    if(irval >=K)irval = K-1;
    irule[i] = irval;
    }
  setWorld();
}


 AC(int K1,int IR1, String s1,Automa appl1){
appl = appl1;
K = K1;
setCol();  
IR = IR1;
nmax = (IR*2+1)*(K-1)+1;
 irule= new int[nmax];
// System.out.println(s1+" "+s1.length()+" "+nmax);
 if(s1.length() >= nmax){
  for(int i=0;i<nmax;i++){
  //  System.out.println(s1.substring(i,i+1));
try {irule[nmax-i-1] = Integer.parseInt(s1.substring(i,i+1));}
  catch(NumberFormatException e){irule[nmax-i-1]=0;} 
    if (irule[nmax-i-1] >=K)irule[nmax-i-1]=0;
    }
  }else {
  for(int i=0;i<nmax;i++){
    int irval = (int)(Math.random()*K);
    if(irval >=K)irval = K-1;
    irule[i] = irval;
    }
   }
 setWorld();
}


int itestru(int pixel){
    int ip;
    int isum = ioldv[pixel];
    for (int i =pixel-IR;i<=pixel-1;i++){
      ip = i;
      if(i<0)ip=ip+appl.disegno.npixel;
      isum = isum+ioldv[ip];
       }
for (int i =pixel+1;i<=pixel+IR;i++){
      ip = i;
      if(i>(appl.disegno.npixel-1))ip=ip-appl.disegno.npixel;
      isum = isum+ioldv[ip];
       }
//System.out.println(j+" "+pixel+" "+nmax+" " +isum);
    newv[pixel]=irule[isum];

return newv[pixel];
}


void setWorld(){
ioldv = new int[appl.disegno.npixel];
 newv = new int[appl.disegno.npixel];
 for(int i=0;i<appl.disegno.npixel;i++){
    int irval = (int)(Math.random()*K);
    if(irval >=K)irval = K-1;
    ioldv[i] = irval;
    }
}

void setPixel(int val){
ioldv = new int[appl.disegno.npixel];
 newv = new int[appl.disegno.npixel];
 for(int i=0;i<appl.disegno.npixel;i++){
        ioldv[i] = 0;
    }
 ioldv[appl.disegno.npixel/2]=val;
}

void setPixelRandom(){
ioldv = new int[appl.disegno.npixel];
 newv = new int[appl.disegno.npixel];
 for(int i=0;i<appl.disegno.npixel;i++){
        ioldv[i] = 0;
    }
 int irval = (int)(Math.random()*(K-1));
 ioldv[appl.disegno.npixel/2]=irval;
}

void setCol(){
 c = new Color[10];
 c[0] = Color.black;
 c[1] = Color.red;
 c[2] = Color.green;
 c[3] = Color.blue;
 c[4] = Color.yellow;
 c[5] = Color.cyan;
 c[6] = Color.magenta;
 c[7] = Color.orange;
 c[8] = Color.pink;
 c[9] = Color.gray;
}
 String rule(){
  String s="";
  for (int i=0;i<nmax;i++){
   s = s + Integer.toString(irule[nmax-i-1]); 
   }
  return s;

 }
}
