//~--- JDK imports ------------------------------------------------------------

import java.applet.*;

//poincar.java; by ES, June 12, 2005; last updated May 19 2009
//walls toroidal or reflecting
//Basic idea and some of the code from Happy.java of Christopher J. Grayce:
//http://www.chem.uci.edu/undergrad/applets/credits.htm
//The program expects n_mol = 480, width=height=400; 
//molecules form a centered square grid at startup;
//number of states visited to recurrence computed
import java.awt.*;

public class poincar extends Applet implements Runnable {
    static Color molColors[] = {
        new Color(0xeb000c), new Color(0xfc851c), new Color(0xcccc00), new Color(0x44f44f), new Color(0x04cccc),
        new Color(0x8080ff), new Color(0xbb00bb), new Color(0x7f04de)
    };
    int       MAX_INTX, MAX_INTY;
    Image     buffer;
    Dimension buffer_d;
    Thread    engine;
    Graphics  gbuffer;
    String    modestr, modes;
    int       molecules[][];
    int       n_mol, recur, steps, mm, vf, mode, mod, width, height;

    public poincar() {}

    public boolean action(Event e, Object arg) {
        if (e.target instanceof Button) {
            mstr((String) arg);
        }

        return true;
    }

    void mstr(String bname) {
        if (bname.equals(" toroidal ")) {
            mod   = 1;
            modes = "toroidal";
        } else if (bname.equals(" reflecting ")) {
            mod   = 2;
            modes = "reflecting";
        }
    }

    public void init() {
        setFont(new Font("Arial", 1, 12));
        add(new Button(" toroidal "));
        add(new Button(" reflecting "));
        n_mol = 480;
        vf    = 15;

        if (mode == 0) {
            mode    = 1;
            mod     = mode;
            modestr = "toroidal";
            modes   = modestr;
        }

        int rtn_mol   = (int) Math.round(Math.sqrt(n_mol));
        int rtn_mol_2 = rtn_mol / 2;

        if (rtn_mol_2 % 2 != 0) {
            rtn_mol_2++;
            rtn_mol = 2 * rtn_mol_2;
        }

        n_mol     = rtn_mol * rtn_mol;
        molecules = new int[n_mol][5];

        Dimension dimension = getSize();

        MAX_INTX = 32 * dimension.width;
        MAX_INTY = 32 * dimension.height;

        for (int i = 0; i < n_mol; i++) {
            molecules[i][0] = i % 8;
            mm              = i / rtn_mol;
            molecules[i][1] = MAX_INTX / 2 + 128 * (mm % rtn_mol - rtn_mol_2);
            molecules[i][2] = MAX_INTY / 2 + 128 * (i % rtn_mol - rtn_mol_2);
            molecules[i][3] = (int) (Math.round(2 * vf * (Math.random() - 0.5)));
            molecules[i][4] = (int) (Math.round(2 * vf * (Math.random() - 0.5)));
        }
    }

    public void start() {
        if (engine == null) {
            engine = new Thread(this);
            engine.start();
        }
    }

    public void stop() {
        if (engine != null) {
            engine = null;
        }
    }

//  Euclid's method to compute the greatest common divisor gcd(a,b)
//  The least common multiple is a*b/gcd(a,b)
    public static int gcd(int a, int b) {
        if (b == 0) {
            return 1;
        }

        int r = a % b;

        return (r == 0)
               ? b
               : gcd(b, r);
    }

    public void run() {
        steps = 0;

        int a = Math.max(MAX_INTX, MAX_INTY);
        int b = Math.min(MAX_INTX, MAX_INTY);

        recur = mode * MAX_INTX * MAX_INTY / gcd(a, b);

        do {
            update(getGraphics());

            if (steps % recur == 0) {
                mode    = mod;
                modestr = modes;

                try {
                    Thread.sleep(1000);

                    if (steps > 0) {
                        recur = recur + mode * MAX_INTX * MAX_INTY / gcd(a, b);
                    }

                    for (int i = 0; i < n_mol;
                            i++)    // new random vel.
                    {
                        molecules[i][3] = (int) (Math.round(2 * vf * (Math.random() - 0.5)));
                        molecules[i][4] = (int) (Math.round(2 * vf * (Math.random() - 0.5)));
                    }
                } catch (InterruptedException _ex) {}
            }

            steps++;

            for (int i = 0; i < n_mol; i++) {
                if (mode == 1)      // mode = "toroidal"
                {
                    int j = molecules[i][1];

                    j += molecules[i][3];
                    j %= MAX_INTX;

                    if (j < 0) {
                        j += MAX_INTX;
                    }

                    molecules[i][1] = j;

                    int k = molecules[i][2];

                    k += molecules[i][4];
                    k %= MAX_INTY;

                    if (k < 0) {
                        k += MAX_INTY;
                    }

                    molecules[i][2] = k;
                } else              // mode = "reflecting"
                {
                    int j = molecules[i][1];

                    j += molecules[i][3];

                    if (j < 0) {
                        j               = -j;
                        molecules[i][3] = -molecules[i][3];
                    }

                    if (j > MAX_INTX) {
                        j               = 2 * MAX_INTX - j;
                        molecules[i][3] = -molecules[i][3];
                    }

                    molecules[i][1] = j;

                    int k = molecules[i][2];

                    k += molecules[i][4];

                    if (k < 0) {
                        k               = -k;
                        molecules[i][4] = -molecules[i][4];
                    }

                    if (k > MAX_INTY) {
                        k               = 2 * MAX_INTY - k;
                        molecules[i][4] = -molecules[i][4];
                    }

                    molecules[i][2] = k;
                }
            }
        } while (true);
    }

    public void paint(Graphics g) {
        Dimension dimension = getSize();

        g.setColor(Color.white);
        g.fillRect(0, 0, dimension.width, dimension.height);
        g.setColor(Color.black);
        g.drawString("steps: " + steps, 20, dimension.height - 15);
        g.drawString("mode: " + modestr, 120, dimension.height - 9);
        g.drawString("recur: " + recur, 20, dimension.height - 3);

        for (int i = 0; i < n_mol; i++) {
            g.setColor(molColors[molecules[i][0]]);

            int j = -1 + molecules[i][1] >> 5;
            int k = -1 + molecules[i][2] >> 5;

            g.fillRect(j, k, 3, 3);
        }
    }

    public void update(Graphics g) {
        Dimension dimension = getSize();

        if ((gbuffer == null) || (dimension.width != buffer_d.width) || (dimension.height != buffer_d.height)) {
            buffer   = createImage(dimension.width, dimension.height);
            gbuffer  = buffer.getGraphics();
            buffer_d = dimension;
        }

        paint(gbuffer);
        g.drawImage(buffer, 0, 0, this);
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
