var size =310.0;
function distance(a, b){
	return Math.sqrt(Math.pow(b[0]-a[0],2)+Math.pow(b[1]-a[1],2)+Math.pow(b[2]-a[2],2));
}
var m_w = 123456789;
var m_z = 987654321;
var mask = 0xffffffff;

// Takes any integer
function seed(i) {
    m_w = i;
}

// Returns number between 0 (inclusive) and 1.0 (exclusive),
// just like Math.random().
function random()
{
    m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
    m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;
    var result = ((m_z << 16) + m_w) & mask;
    result /= 4294967296;
    return result + 0.5;
}
function calculate_ribbon(x, y, z)
{
	var route = [];
		route[0]=x;
		route[1]=y;
		route[2]=z;
	for(var i=1; i<3000; i++) {
		route[i*3+0]=route[i*3-3]-3+random()*6;
		route[i*3+1]=y;
		route[i*3+2]=route[i*3-1]-3+random()*6;
	}
	return route;
}
function Ribbon(setPath, starttime){
	var path = setPath;
	var previous_time=0.0;
	var direction=[0.0, 0.0, 0.0];
	var speed=[0.0, 0.0, 0.0];
	var currentPointToFollow = 0;
	var position=[path[currentPointToFollow*3+0], path[currentPointToFollow*3+1], path[currentPointToFollow*3+2]];
	var ribbonGeometry=[];
	var starttime = starttime;
	var alku = (new Date()).getTime();
	var geo = new THREE.Geometry();
	var vertices = [];
	this.checkIfCloseThenMoveOn = function()
	{
		var pathPoint=[	path[currentPointToFollow*3+0],
						path[currentPointToFollow*3+1],
						path[currentPointToFollow*3+2]];
		if(distance(pathPoint,position)<13.0){
			currentPointToFollow++;
			speed[0]/=1.5;
			speed[1]/=1.5;
			speed[2]/=1.5;
		}
	}
	this.calculateDirection = function()
	{
		direction[0]=path[currentPointToFollow*3+0]-position[0];
		direction[1]=path[currentPointToFollow*3+1]-position[1];
		direction[2]=path[currentPointToFollow*3+2]-position[2];
		direction[0]=direction[0] || 0;			//float may cause NaNs
		direction[1]=direction[1] || 0;
		direction[2]=direction[2] || 0;
	}
	this.update = function(time){
		time=((new Date()).getTime() - alku)*0.001;
		if(time<starttime) return new THREE.Geometry();
		else if(time>starttime+30) return geo;
		var jump=(1.0/200.0);
		time*=0.00000000000001;
		//console.log(geo.vertices.length+","+geo.faces.length)
		geo = new THREE.Geometry();
		vertices=[];
		var ribbonGeometrythen=ribbonGeometry.length;
		var lastvertices=vertices.length;
		for(var step=previous_time; step<time; step+=jump){
			this.calculateDirection();		//edits direction
			var gravity=distance(position,[	path[currentPointToFollow*3+0],
										path[currentPointToFollow*3+1],
										path[currentPointToFollow*3+2]]);
			speed[0]+=Math.max(Math.min(direction[0]*0.0011*gravity,0.36),-0.36);
			speed[1]+=Math.max(Math.min(direction[1]*0.0011*gravity,0.36),-0.36);
			speed[2]+=Math.max(Math.min(direction[2]*0.0011*gravity,0.36),-0.36);

			position[0]+=Math.max(Math.min(speed[0]*0.915,2.2),-2.2);
			position[1]+=Math.max(Math.min(speed[1]*0.915,2.2),-2.2);
			position[2]+=Math.max(Math.min(speed[2]*0.915,2.2),-2.2);
			ribbonGeometry.push(position[0]);
			ribbonGeometry.push(position[1]);
			ribbonGeometry.push(position[2]);
			this.checkIfCloseThenMoveOn();
		}
		var previousPart=[0.0, 0.0, 0.0];
		for(var i=lastvertices/4; i<lastvertices/4+ribbonGeometry.length; i+=3){
			var curPos=[ribbonGeometry[i+0], 
						ribbonGeometry[i+1]+i*0.025,
						ribbonGeometry[i+2]];

			var backend=getroad(curPos, previousPart);
			vertices.push(new THREE.Vector3(backend[0]*20, backend[1]*20, backend[2]*20));
			vertices.push(new THREE.Vector3(backend[3]*20, backend[4]*20, backend[5]*20));
			vertices.push(new THREE.Vector3(backend[0]*20, backend[1]*20+2.0, backend[2]*20));
			vertices.push(new THREE.Vector3(backend[3]*20, backend[4]*20+2.0, backend[5]*20));
			previousPart=curPos;
		}
		geo.vertices=vertices;
		if(Math.ceil(geo.vertices.length/4)>2){
			for(var i=Math.max(Math.ceil(geo.vertices.length/4)-size,0.0); i<Math.ceil(geo.vertices.length/4)-1; i++)
			{
				geo.faces.push( new THREE.Face3(i*4+1,i*4+0,i*4+4));
				geo.faces.push( new THREE.Face3(i*4+4,i*4+5,i*4+1));

				geo.faces.push( new THREE.Face3(i*4+0,i*4+6,i*4+2));
				geo.faces.push( new THREE.Face3(i*4+0,i*4+4,i*4+6));

				geo.faces.push( new THREE.Face3(i*4+1,i*4+3,i*4+7));
				geo.faces.push( new THREE.Face3(i*4+5,i*4+1,i*4+7));

				geo.faces.push( new THREE.Face3(i*4+2,i*4+3,i*4+6));
				geo.faces.push( new THREE.Face3(i*4+3,i*4+7,i*4+6));
			}
		}
		previous_time=time;
		return geo;
	}
	this.getLastPoint = function()
	{
		var a = [];
		a[0] = ribbonGeometry[Math.floor(ribbonGeometry.length/3.0)*3.0+0.0-3.0];
		a[1] = ribbonGeometry[Math.floor(ribbonGeometry.length/3.0)*3.0+1.0-3.0];
		a[2] = ribbonGeometry[Math.floor(ribbonGeometry.length/3.0)*3.0+2.0-3.0];
		return a;
	}
}
function directionalroad(angle) {
	var returnable = [];
	returnable.push(Math.cos(angle), Math.sin(angle), 0.0);
	returnable.push(-Math.cos(angle), -Math.sin(angle), 0.0);
	return returnable;
}
function getroad(point, prev) {
	var score = [];
	score[0] = point[0] - prev[0];
	score[1] = point[1] - prev[1];
	score[2] = point[2] - prev[2];
	var scorelength = Math.sqrt(score[0]+score[1]+score[2]);
	var x = Math.cos(Math.PI*(90/180))*score[0]-Math.sin(Math.PI*(90/180))*score[2];
	var z = Math.sin(Math.PI*(90/180))*score[0]+Math.cos(Math.PI*(90/180))*score[2];
	score[0] = x;
	score[2] = z;
	var returner = [];
	returner[0] = point[0] + score[0];
	returner[1] = point[1] + score[1];
	returner[2] = point[2] + score[2];
	returner[3] = point[0] - score[0];
	returner[4] = point[1] - score[1];
	returner[5] = point[2] - score[2];
	return returner;
}