/*
 * fish.java
 * Aleksey Potashnik (cs184-bk)
 * Created on PC, NT4, MSJ++ 1.1
 * Lab5
 */

import vrml.*;
import vrml.node.*;
import vrml.field.*;
import java.lang.Math;
import java.util.Random;


public class fish extends Script {

	static final float TANK_RADIUS = 15;
	static final float TANK_HEIGHT = 7;

	private float velocity;

	private float tailAngle;
	private int tailShift;
	private SFRotation tail_changed;

	private SFVec3f curPos;
	private SFRotation rot_changed;
	private SFVec3f pos_changed;

	private float tailTurn;
	private int tailCur;
	private int tailDir;

	private float curX, curY, curZ, curA0;
	private float newX, newY, newZ;
	
	private float oldFrac;

	private Random rand;

	private int type;

	private SFVec3f lead_pos;
	private float fromLead;

	public void initialize () {

		velocity = ((SFFloat) getField ("velocity")).getValue();
		tailAngle = ((SFFloat) getField ("tailAngle")).getValue();
		tailShift = ((SFInt32) getField ("tailShift")).getValue();

		tail_changed = (SFRotation) getField ("tail_changed");
		rot_changed = (SFRotation) getField ("rot_changed");
		pos_changed = (SFVec3f) getField ("pos_changed");

		tailTurn = tailAngle / tailShift;
		tailCur = 0;
		tailDir = 1;
		tailUpdate();

		oldFrac = 0;

		curPos = (SFVec3f) getField ("curPos");
		newX = curX = curPos.getX();
		newY = curY = curPos.getY();
		newZ = curZ = curPos.getZ();

		curA0 = ((SFFloat) getField ("curAngle0")).getValue();
				
		rand = new Random();

		type = ((SFInt32) getField ("Type")).getValue();
		if (type == 1) {
			lead_pos = (SFVec3f) ((Node)((SFNode) getField ("lead")).
				getValue()).getExposedField("translation");
			fromLead = ((SFFloat) getField ("fromLead")).getValue();
		}
	}

	private void tailUpdate(){
		tail_changed.setValue(0.0f, 1.0f, 0.0f, tailTurn * tailCur);
		if (Math.abs(tailCur) == tailShift) tailDir *= -1;
		tailCur += tailDir;
	}

	private void fishMove(float x, float y, float z){
		curX = x;
		curY = y;
		curZ = z;
		pos_changed.setValue(curX, curY, curZ);
	}

	private void fishRotate(float a0){
		rot_changed.setValue(0.0f, 1.0f, 0.0f, a0);
	}

	private void fishUpdate_lead(float dt){

		float tr, xc, zc;
		float d = velocity * dt;
		float dist = (float)Math.sqrt( (newX - curX)*(newX - curX) +
			(newY - curY)*(newY - curY) + (newZ - curZ)*(newZ - curZ) );
		float ratio;

		if (d >= dist){
			fishMove(newX, newY, newZ);
			newY = TANK_HEIGHT * (0.2f + 0.6f * rand.nextFloat());
			curA0 += (float)Math.PI/6;
			if (curA0 > 2 * Math.PI) curA0 -= 2 * Math.PI;
			tr = TANK_RADIUS * (0.3f + 0.6f * rand.nextFloat());
			newX = tr * (float)Math.cos(curA0);
			newZ = -tr * (float)Math.sin(curA0);
			xc = newX - curX;
			zc = newZ - curZ;
			fishRotate((float)Math.atan2(xc, zc) + (float)Math.PI/2);
		} else {
			ratio = d/dist;
			fishMove(curX + ratio*(newX - curX),
					 curY + ratio*(newY - curY),
					 curZ + ratio*(newZ - curZ));
		}
	}

	private void fishUpdate_fol(float dt){

		float tr, xc, zc;
		float d = velocity * dt;
		float dist, ratio;
		
		newX = lead_pos.getX();
		newY = lead_pos.getY();
		newZ = lead_pos.getZ();

		dist = (float)Math.sqrt( (newX - curX)*(newX - curX) +
			(newY - curY)*(newY - curY) + (newZ - curZ)*(newZ - curZ) );

		xc = newX - curX;
		zc = newZ - curZ;
		fishRotate((float)Math.atan2(xc, zc) + (float)Math.PI/2);

		if (fromLead <= dist) {
			if (d > (dist - fromLead)) d = dist - fromLead;
			ratio = d / dist;
			fishMove(curX + ratio*(xc),
					 curY + ratio*(newY - curY),
					 curZ + ratio*(zc));
		}
	}

	public void processEvent(Event e){
		String name = e.getName();
		float frac;
		
		if (name.equals("set_fraction")){
			tailUpdate();
			frac = ((ConstSFFloat)e.getValue()).getValue();
			if (frac > oldFrac) {
				if (type == 0) fishUpdate_lead(frac - oldFrac);
				else fishUpdate_fol(frac - oldFrac);
				oldFrac = frac;
			} else oldFrac = 0;
		}
	}
}