/**
 * @author oosmoxiecode
 * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473
 */

var Torusknot = function (radius, tube, segmentsR, segmentsT, p, q, heightScale) {
	var scope = this;
	THREE.Geometry.call(this);

	this.radius = radius || 200;
	this.tube = tube || 40;
	this.segmentsR = segmentsR || 64;
	this.segmentsT = segmentsT || 8;
	this.p = p || 2;
    this.q = q || 3;
	this.heightScale = heightScale || 1;
	this.grid = new Array(this.segmentsR);

	var tang = new THREE.Vector3();
	var n = new THREE.Vector3();
	var bitan = new THREE.Vector3();

	for (var i=0; i<this.segmentsR; ++i) {
		this.grid[i] = new Array(this.segmentsT);
		for (var j=0; j<this.segmentsT; ++j) {
			var u = i / this.segmentsR * 2 * this.p * Math.PI;
			var v = j / this.segmentsT * 2 * Math.PI;
			var p = getPos(u, v, this.q, this.p, this.radius, this.heightScale);
			var p2 = getPos(u+.01, v, this.q, this.p, this.radius, this.heightScale);
			var cx, cy;

			tang.x = p2.x - p.x; tang.y = p2.y - p.y; tang.z = p2.z - p.z;
			n.x = p2.x + p.x; n.y = p2.y + p.y; n.z = p2.z + p.z; 
			bitan.cross(tang, n);
			n.cross(bitan, tang);
			bitan.normalize();
			n.normalize();

			cx = this.tube*Math.cos(v); cy = this.tube*Math.sin(v);
			p.x += cx * n.x + cy * bitan.x;
			p.y += cx * n.y + cy * bitan.y;
			p.z += cx * n.z + cy * bitan.z;

			this.grid[i][j] = vert(p.x, p.y, p.z);
		}
	}

	for (var i=0; i<this.segmentsR; ++i) {
		for (var j=0; j<this.segmentsT; ++j) {
			var ip = (i+1) % this.segmentsR;
			var jp = (j+1) % this.segmentsT;
			var a = this.grid[i][j]; 
			var b = this.grid[ip][j];
			var c = this.grid[i][jp]; 
			var d = this.grid[ip][jp];

			var uva = new THREE.UV(i     / this.segmentsR, j     / this.segmentsT);
			var uvb = new THREE.UV((i+1) / this.segmentsR, j     / this.segmentsT);
			var uvc = new THREE.UV(i     / this.segmentsR, (j+1) / this.segmentsT);
			var uvd = new THREE.UV((i+1) / this.segmentsR, (j+1) / this.segmentsT);

			f3(a, b, c);
			this.uvs.push( [uva,uvb,uvc] );
			f3(d, c, b);
			this.uvs.push( [uvd,uvc,uvb] );
		}
	}

	this.computeCentroids();
	this.computeFaceNormals();
	this.computeVertexNormals();
	this.sortFacesByMaterial();

	function vert( x, y, z ) {
		var i = scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) );
		return i-1;
	}

	function f3( a, b, c ) {
		scope.faces.push( new THREE.Face3( a, b, c ) );
	}

	function getPos(u, v, in_q, in_p, radius, heightScale ) {
		var cu = Math.cos(u);
		var cv = Math.cos(v);
		var su = Math.sin(u);
		var quOverP = in_q/in_p*u;
		var cs = Math.cos(quOverP);

		var tx = radius*(2+cs)*.5 * cu;
		var ty = radius*(2+cs)*su*.5;
		var tz = heightScale*radius*Math.sin(quOverP)*.5;
		
		return new THREE.Vector3(tx,ty,tz);
	}

}

Torusknot.prototype = new THREE.Geometry();
Torusknot.prototype.constructor = Torusknot;