/* revdet.java - jpq - 11/11/2000
 * l'allumeur de réverbères
 * modification et ajout de main() le 13/02/18
 */

import java.awt.event.*;
import java.awt.*;
import java.applet.*;
import java.util.*;

public class revdet extends java.applet.Applet {
  static final long serialVersionUID = 180213L;
  controles C;
  dessin D;

  private int paramint (String s, int i) {
    try {
      s = getParameter (s);
      i = Integer.parseInt (s);
    }
    catch (NumberFormatException e) {}
    catch (NullPointerException e) {}
    return i;
  }

  private double paramdouble (String s, double d) {
    try {
      s = getParameter (s);
      d = Double.parseDouble (s);
    }
    catch (NumberFormatException e) {}
    catch (NullPointerException e) {}
    return d;
  }

  public void init ()
  { setLayout (new BorderLayout ());
    D = new dessin (paramint ("lignes", 10), paramint ("colonnes", 40),
                    paramdouble ("pra", 0.5),
                    paramdouble ("pase", 0.8), paramdouble ("pesa", 0.5),
                    paramint ("nsimul", 100));
    C = new controles (D);
    D.C = C;
    add (C, BorderLayout.NORTH);
    add (D, BorderLayout.CENTER);
  }

  public void destroy ()
  { remove (D); remove (C); }

  public String getAppletInfo ()
  { return "revdet par j.-p. Quelen"; }

////////////////////////////////////////////////////////////////////////////////
protected class dessin extends Canvas {
  static final long serialVersionUID = 180213L;
  int lignes, colonnes, nsimul, n, nrayon, H, Hm20, W, npash, npasw, an, en, totsimul;
  int [] index;
  double pase, pesa, pra;
  Random rnd;
  boolean rev [], raz, simul;
  controles C;
  Image img;
  Graphics g;

  public dessin (int lignes, int colonnes, double pra, double pase, double pesa, int nsimul)
  { this.lignes = lignes;
    this.colonnes = colonnes;
    this.pra = pra;
    this.pase = pase;
    this.pesa = pesa;
    this.nsimul = nsimul;
    n = lignes * colonnes;
    rev = new boolean [n];
    index = new int [n];
    rnd = new Random ();
    simul = raz = true;
    setFont (new Font ("Arial", Font.PLAIN, 10));
  }

  private void prevdet (Graphics g1)
  { g.setColor (Color.black);
    g.fillRect (0, 0, W, H);
    int na = 0;
    int x = 0; int y = 0; int k = 0;
    for (int i = 0; i < lignes; i ++)
    { for (int j = 0; j < colonnes; j ++)
      { if (rev [k])
        { na ++;
          g.setColor (Color.yellow);
        }
        else g.setColor (Color.darkGray);
        g.fillOval (x, y, nrayon, nrayon);
        k ++;
        x += npasw;
      }
      x = 0;
      y += npash;
    }
    g.setColor (Color.white);
    g.drawString (" Nb simul : " + Integer.toString (totsimul) +
                  "; réverbères allumés : " + Integer.toString (na), 0, H - 2);
    g1.drawImage (img, 0, 0, this);
  }

  public void update (Graphics g1)
  { paint (g1); }

  public void paint (Graphics g1) {
    if (img == null || W != getSize().width || H != getSize().height) {
      W = getSize().width;
      H = getSize().height;
      img = createImage (W, H);
      g = img.getGraphics ();
    }

    npasw = W / colonnes;
    Hm20 = H - 20;
    npash = Hm20 / lignes;
    nrayon = Math.min (npasw, npash);
    if (nrayon > 10) nrayon = nrayon - 5;
    else if (nrayon == 0)
    { nrayon = 1;
      if (npasw == 0) { npasw = 1; lignes = W; }
      if (npash == 0) { npash = 1; colonnes = Hm20; }
      raz = true;
    }
    if (raz)
// état initial
    { raz = false;
      n = lignes * colonnes;
      C.lbln.setText ("nb de réverbères : " + Integer.toString (n));
      if ((rev == null) || (rev.length < n)) rev = new boolean [n];
      int npra = (int)(pra * n + .5);
      for (int i = 0; i < npra; i++) rev [i] = true;
      for (int i = npra; i < n; i++) rev [i] = false;
      an = npra;
      totsimul = 0;
      int nm1 = n - 1;
      for (int i = 0; i < nm1; i ++)
      { int j = i + (int)(rnd.nextDouble () * (n - i - 1)) + 1;
        boolean b = rev [i];
        rev [i] = rev [j];
        rev [j] = b;
      }
    }
// changements d'états
    else if (simul)
    { simul = false;
      for (int isimul = 0; isimul < nsimul; isimul ++)
      { int npase = (int)(pase * (n - an) + 0.5);
        int npesa = (int)(pesa * an + 0.5);
        an = 0;
        int ase = 0; int esa = 0;
        for (int i = 0; i < n; i++) index [i] = i;
        int nm1 = n - 1;
        for (int i = 0; i < nm1; i ++)
        { int j = i + (int)(rnd.nextDouble () * (n - i - 1)) + 1;
          int k = index [i];
          index [i] = index [j];
          index [j] = k;
        }
        for (int i = 0; i < n; i ++)
        { if (rev [index [i]] && (esa < npesa)) { rev [index [i]] = false; esa ++; }
          else if (! rev [index [i]] && (ase < npase)) { rev [index [i]] = true; ase ++; }
          if (rev [index [i]]) an ++;
        }
        totsimul ++;
        prevdet (g1);
      }
    }
//
    prevdet (g1);
  }
}
////////////////////////////////////////////////////////////////////////////////
protected class controles extends Panel implements ActionListener {
  static final long serialVersionUID = 180213L;
  dessin D;
  TextField tpra, tpase, tpesa, tsimul;
  Label lbln;
  Button ok, raz;
  Font f;
  Panel h, b;

  private Label ajoutlbl (String s)
  { Label l = new Label (s);
    l.setBackground (Color.lightGray);
    l.setFont (f);
    return l;
  }

  private TextField ajouttfi (int i, int j)
  { TextField T = new TextField (Integer.toString (i), j);
    T.setFont (f);
    return T;
  }

  private TextField ajouttfd (double d, int j)
  { TextField T = new TextField (Double.toString (d), j);
    T.setFont (f);
    return T;
  }

  public controles (dessin D)
  { this.D = D;
    setLayout (new GridLayout(2, 1));
    add (h = new Panel ());
    add (b = new Panel ());
    setBackground (Color.lightGray);
    f = new Font ("Arial", Font.PLAIN, 10);
    h.add (lbln = ajoutlbl ("nb de réverbères : " + Integer.toString (D.n)));
    h.add (ajoutlbl ("réverbères allumés :"));
    h.add (tpra = ajouttfd (D.pra, 2));
    h.add (ajoutlbl ("nb simul. :"));
    h.add (tsimul = ajouttfi (D.nsimul, 3));
    h.add (raz = new Button ("RAZ"));
    raz.addActionListener (this);
    raz.setFont (f);
    h.add (ok = new Button ("Ok"));
    ok.addActionListener (this);
    ok.setFont (f);
    b.add (ajoutlbl ("allumage d'un réverbère éteint :"));
    b.add (tpase = ajouttfd (D.pase, 2));
    b.add (ajoutlbl ("extinction d'un réverbère allumé :"));
    b.add (tpesa = ajouttfd (D.pesa, 2));
  }

  private int maji (TextField T, int n)
  { try { n = Integer.parseInt (T.getText ()); }
      catch (NumberFormatException nfe) { }
    if (n < 0) n = 0;
    return n;
  }

  private double majd (TextField T, double d)
  { try { d = new Double(T.getText ()).doubleValue(); }
      catch (NumberFormatException nfe) { }
    if (d < 0.0) d = 0.0;
    else if (d > 1.0) d = 1.0;
    return d;
  }

  public void majTi (TextField T, int i)
  { T.setText (Integer.toString (i)); }

  private void majTd (TextField T, double d)
  { T.setText (Double.toString (d)); }

  public void actionPerformed (ActionEvent e)
  { Object obj = e.getSource ();
    if (( obj == ok) || (obj == raz))
    { D.pra = majd (tpra, D.pra);
      D.pesa = majd (tpesa, D.pesa);
      D.pase = majd (tpase, D.pase);
      D.nsimul = maji (tsimul, D.nsimul);
      if (obj == raz) D.raz = true;
      majTd (tpra, D.pra);
      majTd (tpase, D.pase);
      majTd (tpesa, D.pesa);
      majTi (tsimul, D.nsimul);
      D.simul = true;
      D.repaint ();
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

    public static void main (String [] args) {

        revdet a = new revdet();
        a.init ();
        a.start ();

        Frame f = new Frame ("revdet");
        f.addWindowListener (new fermer ());
        f.add (a);
        f.setSize (500, 400);
        f.setVisible (true);
    }

    protected static final class fermer extends WindowAdapter {
        public void windowClosing (WindowEvent e) {
            System.exit (0);
        }
    }

}
