/* <applet code="AspensRow.class" width="800" height="600"></applet> */

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.JColorChooser;

public class AspensRow extends Applet {
	private static final float WIDTH_D = 1.5f;
	private static final double SCALE = 0.8, CHILD_SCALE = 0.83, TRUNK_SCALE = 0.97;
	private static final double APPEAR = 0.85, TRUNK_ATT = 0.25;
	private static final int N_BEGIN = 15;
	private static final int ANGLE = 25, ANGLE_RANDOM = 10;
	private static final double ANG = 90.0, RAD = Math.PI / 180.0;
	private static final double X_LEN = 35.0, Y_LEN = 35.0;
	private static final int LEAF_NUM = 3;
	private static final Color COL_TRUNK = new Color(85, 62, 62);
	private static final Color[] ACOL_LEAVES = {
		new Color(80, 150, 17), new Color(0, 125, 47), new Color(0, 125, 70)
	};
	
	private BufferedImage iOff = null;
	private Graphics2D gOff = null;
	private int w, h;
	
	private TextField tfAngle, tfAngleRandom, tfAppear, tfTrunkAtt;
	private ICColorButton[] acbLeaves = new ICColorButton[LEAF_NUM];
	private ICColorButton cbTrunk;
	
	public AspensRow() {
		setLayout(new BorderLayout());
		
		Panel pBar = new Panel();
		Checkbox cbSmooth = new Checkbox("アンチエイリアス", true);
		Button bRedraw = new Button("　再描画　");
		final ARPanel arPanel = new ARPanel();
		
		tfAngle = new TextField(Integer.toString(ANGLE));
		tfAngleRandom = new TextField(Integer.toString(ANGLE_RANDOM));
		tfAppear = new TextField(Double.toString(APPEAR));
		tfTrunkAtt = new TextField(Double.toString(TRUNK_ATT));
		
		for (int n = 0; n < LEAF_NUM; n++)
			acbLeaves[n] = new ICColorButton("葉" + (n + 1), ACOL_LEAVES[n]);
		cbTrunk = new ICColorButton("幹", COL_TRUNK);
		
		pBar.add(new Label("枝の角度/揺れ/出現率", Label.RIGHT));
		pBar.add(tfAngle);
		pBar.add(tfAngleRandom);
		pBar.add(tfAppear);
		pBar.add(new Label("幹の減衰率", Label.RIGHT));
		pBar.add(tfTrunkAtt);
		for (int n = 0; n < LEAF_NUM; n++)
			pBar.add(acbLeaves[n]);
		pBar.add(cbTrunk);
		pBar.add(cbSmooth);
		pBar.add(bRedraw);
		pBar.setBackground(Color.GRAY);
		add(arPanel, BorderLayout.CENTER);
		add(pBar, BorderLayout.SOUTH);
		
		cbSmooth.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				gOff.setRenderingHint(RenderingHints.KEY_ANTIALIASING, e.getStateChange() == ItemEvent.SELECTED ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
			}
		});
		
		bRedraw.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				arPanel.paintTrees(gOff);
				arPanel.repaint();
			}
		});
	}
	
	class ARPanel extends Label {
		public void update(Graphics g) {
			paint(g);
		}
		
		public void paint(Graphics g) {
			if (iOff == null) {
				iOff = new BufferedImage(getSize().width, getSize().height, BufferedImage.TYPE_INT_RGB);
				gOff = iOff.createGraphics();
				gOff.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				paintTrees(gOff);
			}
			
			g.drawImage(iOff, 0, 0, this);
		}
		
		public void paintTrees(Graphics2D g) {
			AspensRow.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
			
			w = getSize().width;
			h = getSize().height;
			double x0, y0;
			x0 = w / 2;
			y0 = 0 + 25;
			g.setColor(Color.WHITE);
			g.fillRect(0, 0, w, h);
			g.setColor(cbTrunk.getBackground());
			
			
			rtree(g, N_BEGIN, x0 - w / 4, y0, X_LEN, Y_LEN, ANG, 0, true, false);
			rtree(g, N_BEGIN, x0 + w / 4, y0, X_LEN, Y_LEN, ANG, 0, true, false);
			rtree(g, N_BEGIN, x0, y0, X_LEN, Y_LEN, ANG, 0, true, false);
			
			AspensRow.this.setCursor(null);
		}
		
		private void rtree(Graphics2D g, int nn, double x0, double y0, double xlen, double ylen, double ang, int dir, boolean isTrunk, boolean isChild) {
			if (nn <= 0)
				return;
			
			if (!isTrunk && (!isChild || nn <= 1) && nn <= LEAF_NUM) {
				if (nn == 2)
					g.setColor(acbLeaves[1 + (int) (Math.random() * 2.0)].getBackground());
				else
					g.setColor(acbLeaves[nn - 1].getBackground());
			}
			
			g.setStroke(new BasicStroke(nn * WIDTH_D * (isTrunk ? 1.25f : 1.0f)));
			
			double x = xlen * Math.cos(RAD * ang) + x0;
			double y = ylen * Math.sin(RAD * ang) + y0;
			
			g.drawLine((int) x0, (int) (h - y0), (int) x, (int) (h - y));
			
			int angle = Integer.parseInt(tfAngle.getText());
			int angleRandom = Integer.parseInt(tfAngleRandom.getText());
			double trunkAtt = Double.parseDouble(tfTrunkAtt.getText());
			double appear = Double.parseDouble(tfAppear.getText());
			
			int ang_r1 = (dir ==  1 ? 0 : angle) + (int) (Math.random() * angleRandom) - angleRandom / 2;
			int ang_r2 = (dir == -1 ? 0 : angle) + (int) (Math.random() * angleRandom) - angleRandom / 2;
			double scale = (isTrunk) ? TRUNK_SCALE : (isChild ? CHILD_SCALE : SCALE);
			
			if (!isTrunk || Math.random() < appear) rtree(g, (isTrunk ? (int) Math.min(nn, LEAF_NUM) : (nn - (nn <= LEAF_NUM ? 1 : 17))), x, y, xlen * scale, ylen * scale, ang - ang_r1, 1, false, isTrunk);
			if (!isTrunk || Math.random() < appear) rtree(g, (isTrunk ? (int) Math.min(nn, LEAF_NUM) : (nn - (nn <= LEAF_NUM ? 1 : 17))), x, y, xlen * scale, ylen * scale, ang + ang_r2, -1, false, isTrunk);
			rtree(g, nn - (isTrunk ? 1 : (Math.random() < trunkAtt ? 1 : 0)), x, y, xlen * scale, ylen * scale, ang, 0, isTrunk, isChild);
			g.setColor(cbTrunk.getBackground());
		}
	}
	
	class ICColorButton extends Button implements ActionListener {
		public ICColorButton(String szLabel, Color c) {
			super(szLabel);
			setForeground(Color.WHITE);
			setBackground(c);
			addActionListener(this);
		}
		
		public void actionPerformed(ActionEvent e) {
			Color c = JColorChooser.showDialog(null, null, getBackground());
			if (c == null) return;
			setBackground(c);
		}
	}
}

