/**
 * @version $Id: Hotal2.java,v 1.7 1997/03/28 07:03:11 rekimoto Exp $
 * @author  Jun Rekimoto
 */

import java.awt.*;
import java.applet.*;
import java.awt.event.*;

public class Hotal extends Applet implements Runnable, ActionListener {
	private transient Image offscreen;
	private double theta[][], vel[][], om[][];
	private double speed = 0.1;
	private double ratio = 0.6;
	private int num = 10;
    private transient Thread thread;
    private int design = 0;
    private Button bt_shuffle, bt_design;

	public Hotal() {
		this.theta = new double[num][num];
		this.vel = new double[num][num];
		this.om = new double[num][num];
		shuffle();
		setBackground(Color.black);

		bt_shuffle = new Button("Shuffle");
		bt_shuffle.addActionListener(this);
		add(bt_shuffle);
		
		bt_design = new Button("View Change");
		bt_design.addActionListener(this);
		add(bt_design);
	}
	
	public Dimension getMinimumSize() {
		return new Dimension(280, 300);
	}

	public Dimension getPreferredSize() {
		return getMinimumSize();
	}


	public void shuffle() {
		for (int i = 0; i < num; i++) {
			for (int j = 0; j < num; j++) {
				theta[i][j] = Math.random() * 2.0 * Math.PI;
				vel[i][j] = om[i][j] = Math.random() / 4.0 + 1.0;
			}
		}
	}
	
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == bt_shuffle) {
			shuffle();
		} else if (e.getSource() == bt_design) {
			design = (design + 1) % 2;
		}
	}

	private void incrementValues() {
		for (int i = 0; i < num; i++) {
			for (int j = 0; j < num; j++) {
				double sum = 0.0;
				for (int ii = i - 1; ii <= i + 1; ii++) {
					for (int jj = j - 1; jj < j + 1; jj++) {
						sum += Math.sin(theta[(ii+num)%num][(jj+num)%num]
										- theta[i][j]);
					}
				}
				/**
				om[i][j] = (sum * ratio) / (double)num;
				vel[i][j] += om[i][j] * speed;
				**/
				vel[i][j] = om[i][j] + (sum * ratio) / (double)num;
			}
		}
		
		for (int i = 0; i < num; i++) {
			for (int j = 0; j < num; j++) {
				theta[i][j] += vel[i][j] * speed;
			}
		}
	}

    public void start() {
        if (thread == null) {
            thread = new Thread(this);
            thread.start();
        }
    }

    public void stop() {
        if (thread != null) {
            thread.stop();
            thread = null;
        }
    }

	public void run() {
		while(true) {
            try{Thread.sleep(10);} catch (InterruptedException e) {}
			incrementValues();
			repaint();
		}
	}

	public void update(Graphics g) {
		paint(g); // paint draws all the area, so clear is not necessary
	}

    public static Color theta2color(double th) {
		return new Color((float)0.0,
						 (float)((Math.sin(th) + 1.0) / 2.0),
						 (float)((Math.cos(th) + 1.0) / 2.0));
	}

	public synchronized void paint(Graphics g1) {
		if (offscreen == null) 
			offscreen = this.createImage(300, 300);
		Graphics g = offscreen.getGraphics();
		g.clearRect(0, 0, 300, 300);
		
		switch (design) {
		case 0:
			for (int i = 0; i < num; i++) {
				for (int j = 0; j < num; j++) {
					int x = i * 25 + 10;
					int y = j * 25 + 10;
					g.setColor(theta2color(theta[i][j]));
					g.fillRect(x, y, 20, 20);
				}
			}
			break;
		
		case 1:
			g.setColor(Color.green);
			for (int i = 0; i < num; i++) {
				for (int j = 0; j < num; j++) {
					int r = (int)((Math.sin(theta[i][j]) + 1.0) * 7);
					int x = i * 25 + 15 - r;
					int y = j * 25 + 20 - r;
					g.fillOval(x, y, 2 * r, 2 * r);
				}
			}
			break;
		}
		
		g1.drawImage(offscreen, 10, 30, this);
	}
	
	public static void main(String argv[]) {
		Frame f = new Frame();
		Hotal app = new Hotal();
		f.add(app);
		app.init();
		app.start();
		f.pack();
		f.show();
	}
}

