import java.awt.*;
import java.util.Random;

// oh for value semantix!

public class Segment {
	final static double SPRINGY = 0.2,
		SLOWY = 0.9;
	static Random rnd = new Random();
	Matrix3D matrix_ = new Matrix3D();
	Vector3D root_ = new Vector3D(), // in local coordinates
		curr_ = new Vector3D(); // in world coords
	double theta_ = 0., size_ = 1.;
	
	Vector3D[] points_ = null,
		worldPoints_ = null;
	int[] ptbufX_ = null,
		ptbufY_ = null;
	Color color_ = null;
	
	static final int MAX_CHILDREN = 10;
	Child[] children_ = new Child[MAX_CHILDREN];
	int nchildren_ = 0;

	static Matrix3D tempM_ = new Matrix3D(),
		tempM2_ = new Matrix3D();
	static Vector3D tempV_ = new Vector3D();
	
	Segment(Vector3D root, Vector3D[] points, double size, Color color) {
		root_.copy(root);
		points_ = points;
		worldPoints_ = new Vector3D[points.length];
		for(int i = 0; i<points.length; ++i)
			worldPoints_[i] = new Vector3D();
		ptbufX_ = new int[points.length];
		ptbufY_ = new int[points.length];
		size_ = size;
		color_ = color;
	}
	
	public class Child {
		Vector3D offs_; // local to parent
		double restTheta_=0, currTheta_=0,lastTheta_=0;
		Segment seg_;
		
		Child(Vector3D offs, double rest, Segment seg) {
			offs_ = offs;
			currTheta_ = lastTheta_ = restTheta_ = rest;
			seg_ = seg;
		}
		void place(Segment parent, double t, double wind) {
			parent.matrix_.transform(offs_,tempV_);
			double dth = currTheta_ - lastTheta_,
				spr = (restTheta_ - currTheta_)*SPRINGY,
				winded = Math.cos(parent.theta_+currTheta_)*wind,
				nexth = currTheta_ + (dth + spr)*SLOWY+winded;
			lastTheta_ = currTheta_;
			currTheta_ = nexth;
			seg_.place(tempV_,parent.theta_+currTheta_,t,wind);
		}
		void render(Graphics g) {
			seg_.render(g);
		}
	}
	void place(Vector3D curr, double theta, double t, double wind) {
		curr_.copy(curr);
		theta_ = theta;
		//System.out.println("theta's "+theta_+"; size's "+size_+"; curr's "+curr.x()+", "+curr.y());
		
		matrix_.translation(tempV_.copy(root_).reverse());
		matrix_.rotateZ(-theta_);
		matrix_.scale(size_,size_,1);
		matrix_.translate(curr_);

		//System.out.println("matrix is "+matrix_);
		
		for(int i = 0; i<nchildren_; ++i)
			children_[i].place(this,t,wind);
	}
	void render(Graphics g) {
		for(int i = 0; i<points_.length; ++i) {
			matrix_.transform(points_[i], worldPoints_[i]);
			//System.out.println("point's "+worldPoints_[i].x()+", "+worldPoints_[i].y());
		}
		for(int i = 0; i<points_.length; ++i) {
			ptbufX_[i] = (int)worldPoints_[i].x();
			ptbufY_[i] = (int)worldPoints_[i].y();
		}
		g.setColor(color_);
		g.fillPolygon(ptbufX_,ptbufY_,ptbufX_.length);
		for(int i = 0; i<nchildren_; ++i)
			children_[i].render(g);
	}
	
	void addChild(Child child) {
		if(nchildren_<MAX_CHILDREN)
			children_[nchildren_++] = child;
	}
	
	void leafOut(int pass, int limit) {
		if(pass>=limit)
			return;
		int nbranch = (int)(3.4 + rnd.nextGaussian());
		if(pass==0 && nbranch==1 && rnd.nextInt(10000)!=17)
			++nbranch;
		for(int i = 0; i<nbranch; ++i) {
			double //sec = (double)(i+1)/(double)nbranch - (double)nbranch/2.0,
				dx = 0, //(rnd.nextDouble()-.5)*.2,
				dth = (rnd.nextDouble()-0.5)*2.*Math.PI/3; // bell curve didn't seem right here //rnd.nextGaussian()*Math.PI/6.*(1 + 1./((pass+1.)));
			Color color = (pass==limit-1)?new Color(12,100+rnd.nextInt(100),12):color_;
			Segment seg = new Segment(root_, points_,size_*.8,color);
			addChild(new Child(new Vector3D(0.5+dx,0.1,0), dth, seg));
			seg.leafOut(pass+1, limit);
		}
	}
		
}
