// http://threejs.org/examples/webgl_interactive_cubes_gpu.html

var pos = new THREE.Vector3();
var normal = new THREE.Vector3();

var FXBirdBuilder2 = function () {

	FRAME.Module.call( this );

	this.parameters.input = {
		phase: 0
	};

	var phase;
	var width = renderer.domElement.width;
	var height = renderer.domElement.height;

	var camera = new THREE.PerspectiveCamera( 60, width / height, 0.0001, 100 );

	var scene = new THREE.Scene;

  	var ambientLight = new THREE.AmbientLight( 0xFF666666 );
    scene.add(ambientLight);

	var light1 = new THREE.PointLight( 0xffffff, 5, 300 );
	
	light1.position.set ( -20, 30, 250 );
	scene.add( light1 );

	this.numCubes = 50;
	//this.cubeSize = 200;

	this.cubeGeometry = new THREE.CubeGeometry(1,1,1);

	this.cubeMaterial = new THREE.ShaderMaterial( 
	{
		uniforms: 
		{
			texture:   { type: "t", value: resourceManager.getTexture("particle_steam") },
		},
		attributes:
		{
			customVisible:	{ type: 'f',  value: [] },
			customSize:		{ type: 'f',  value: [] },
			customColor:	{ type: 'c',  value: new THREE.Color( 0xffffff ) },
			customOpacity:	{ type: 'f',  value: [] }
		},
		vertexShader: [

			"attribute float customSize;",
			"void main()",
			"{",
				"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
				"gl_PointSize = customSize * ( 300.0 / length( mvPosition.xyz ) );",     // scale particles as objects in 3D space
				"gl_Position = projectionMatrix * mvPosition;",
			"}"
			].join("\n"),

		fragmentShader: [

			"uniform sampler2D texture;",
			"uniform float opacity;",
			"void main()", 
			"{",
				"gl_FragColor = texture2D( texture,  gl_PointCoord );",    
			"}"
			].join("\n"),

		transparent: true, 
		blending: THREE.NormalBlending, 
		depthTest: true,
		depthWrite: false
	});

    this.cubeMaterial = new THREE.MeshBasicMaterial({
        color: 0xff0000,
        wireframe: false
    });

	this.cubeMaterial = new THREE.MeshLambertMaterial({ 
		color: 0xffffff, 
		shading: THREE.FlatShading,
//		vertexColors: THREE.VertexColors
	} );

	this.cubeMesh = new THREE.Mesh( this.cubeGeometry, this.cubeMaterial );

	this.randomizer = new THREE.Randomizer();

	this.cubes = [];

	this.camera_distance = 1.5;
	camera.position.set( 0, 0, this.camera_distance );
	this.camera_focus = new THREE.Vector3();

	var mix = function(a,b,x)
	{
		return a + (b-a)*x;
	};

	this.init = function ( parameters ) {

		this.phase = parameters.phase;


		this.lasttime = 0;

		var closed_left_eye = 0.75;
		var closed_right_eye = 0.8;
		var bend_back = 1.;



		function vec3(x,y,z) {
			if (y===undefined && z===undefined)
				return x.clone();
			else
				return new THREE.Vector3(x,y,z);
		};

		var a = (this.phase-0.5)/6.0*2*Math.PI;
		this.camera_focus = vec3(Math.cos(a),0.2,Math.sin(a));

		function length(p) {
			return p.length();
		};

		function dot(p,q) {
			return p.dot(q);
		};

		function min(p,q) {
			return Math.min(p,q);
		};

		function max(p,q) {
			return Math.max(p,q);
		};

		function clamp(v,min_v,max_v) {
			return min(max_v,max(min_v,v));
		}

		function sphere( p,  c,  r)
		{
		    return length(vec3(p).sub(c)) - r;
		};

		function plane( p,  n,  d)
		{
		    return dot(p,n)-d;
		};

		function bendBody( p)
		{
		    // offset position to compute distance from to give the effect of the object moving
		    var bending = bend_back*0.025;
		    //float bending = 0.015*sin(4.*iGlobalTime);
		    p.z = p.z - p.y*p.y*bending;
		    /*#ifdef EAT_MUSHROOMS
		    p.x = p.x - sin(p.y);
		    #endif
		    #ifdef DANCE_GROOVE
		    float o = p.y*p.y*bending*2.*sin(4.*iGlobalTime);
		    p.x += o;
		    p.y += abs(o);
		    #endif
		    #ifdef DANCE_FUNK
		    float o2 = p.y*p.y*bending*2.*sin(4.*iGlobalTime);
		    p.x += o2;
		    p.y -= abs(o2);
		    #endif*/
		    return p;
		};

		var neck = 0.48;
		function birdieBody( p)
		{
		    // Model the body as a cylinder with varying radius
		    var y = clamp(p.y,0.,1.);

		    var r;
			var neck = 0.48;
		    if (y<neck)
		    	r = mix(1.4,0.15,y/neck);
		    else
		    {
		        var t = 1. - (y-neck)/(1.-neck);
		        r = -.85*t*t*t*t + 1.;
		        r *= Math.sin(Math.acos(1.-t));
		    }
		    
		    var body = length(vec3(p.x,0,p.z)) - r*0.1;
		    return Math.max(Math.max(p.y-1.,-p.y),body);
		}

		function birdieBeak1( p)
		{
		    var d_back = p.z - (-0.02);
		    var d_bottom = -(p.y - 0.57);
		    var d_left = sphere(p,vec3(-.15, 0.6, 0.02),.2);
		    var d_right = sphere(p,vec3(.15, 0.6, 0.02),.2);
		    
		    return max(max(d_left,d_right),max(d_bottom,d_back));
		}

		function birdieBeak2( p)
		{
		    var d_back = p.z - (-0.02);
		    var d_top = plane(p, vec3(0.,1.,-.6), 0.6);
		    var d_left = sphere(p,vec3(-.115, 0.65, 0.06),.2);
		    var d_right = sphere(p,vec3(.115, 0.65, 0.06),.2);
		    
		    return Math.max(Math.max(d_left,d_right),Math.max(d_top,d_back));
		}

		function birdieBeak( p)
		{
		    var beak1 = birdieBeak1(p);
		    var beak2 = birdieBeak2(p);
		    
		    return Math.min(beak1,beak2);
		}

		function birdieEyePupil( p)
		{
			p = vec3(p);
		    var off = vec3( 0.04, 0.0, 0);
		    p.y -= 0.717;
		    p.z += 0.048;
		    
		    return min(length(vec3(p).sub(off)),length(vec3(p).add(off) )) - .025; // distance to sphere
		}

		function birdieEyeWhite(body, p)
		{
		    var d_bottom = plane(p,vec3(0.,-1.,0.),-0.69);
		    var d_top = plane(p,vec3(0.,1.,0.),0.9);
		    var d_sphere = min(sphere(p,vec3(0.1,0.75,-0.1),.1),
		                         sphere(p,vec3(-0.1,0.75,-0.1),.1));
		    return max(max(body,d_sphere),max(d_top,d_bottom));
		}

		function birdieBandana(body, p)
		{
		    var d_bottom = plane(p,vec3(0.,-1.,0.),-0.85);
		    var d_top = plane(p,vec3(0.,1.,0.),0.91);
		    return Math.max(body,Math.max(d_top,d_bottom));
		}

		function birdieEyeLid( p,  closed_left,  closed_right)
		{
			p = vec3(p);
		    p.y = (p.y-0.75)*0.5 + 0.75;
		    var d_bottom = plane(p,new THREE.Vector3(-.4,-1.,0.),-0.9 + 0.21*closed_left);
		    var d_sphere1 = sphere(p,new THREE.Vector3(-0.1,0.75,-0.1),.1);
		    var d_sphere2 = sphere(p,new THREE.Vector3(0,0.77,0),.1);
		    var lid1 = Math.max(Math.max(d_sphere1,d_sphere2),d_bottom);

		    d_bottom = plane(p,new THREE.Vector3(0.2,-1.,0.),-0.9 + 0.21*closed_right);
		    d_sphere1 = sphere(p,new THREE.Vector3(0.1,0.75,-0.1),.1);
		    d_sphere2 = sphere(p,new THREE.Vector3(0,0.77,0),.1);
		    var lid2 = Math.max(Math.max(d_sphere1,d_sphere2),d_bottom);
		    return Math.min(lid1,lid2);
		}

		function scenedist(p)
		{
		    //p = bendBody(p);
		    var body = birdieBody(p);
		    var beak = birdieBeak(p);
		    var eyepupil = birdieEyePupil(p);
		    var eyewhite = birdieEyeWhite(body,p);
		    var eyelid = birdieEyeLid(p,closed_left_eye, closed_right_eye);
		    /*#ifdef BANDANA
		    var ninjabandana = birdieBandana(body,p);
		    #endif
		    
		    #ifdef ONLY_BEAK
		    body += 10.;
		    eyepupil += 10.;
		    eyewhite += 10.;
		    eyelid += 10.;
		    #endif*/

		    var m = min(min(min(body,beak),min(eyepupil,eyewhite)),eyelid);
		    
		    if (m==beak) return [0.8, 0.4, 0.2, m];
		    if (m==eyepupil) return [0.0, 0.0, 0.0, m];
		    //#ifdef BANDANA
		    //if (m==ninjabandana) return [0.1, 0.2, 0.3, m];
		    //#endif
		    if (m==eyewhite) return [1.0, 1.0, 1.0, m];
		    if (m==eyelid) return [0.6, 0.6, 0.6, m];
		    return [0.8, 0.8, 0.8, m]; // m==body
		}


		function applyVertexColors( g, c ) {
			g.faces.forEach( function( f ) {
				var n = ( f instanceof THREE.Face3 ) ? 3 : 4;
				for( var j = 0; j < n; j ++ ) {
					f.vertexColors[ j ] = c;
				}
			} );
		}

		var randomizer = this.randomizer;
		var a = randomizer.randFloat(0,1);
		
		var rnd = function(a,b) {
			if (a===undefined && b===undefined)
				return randomizer.randFloat(-1,1);
			else if (b===undefined)
				return randomizer.randFloat(0,a);
			return randomizer.randFloat(a,b);
		};

		var numCubes = this.numCubes;
		var cubesize = function() {
			return 2./numCubes*(1.+0.5*rnd());
		};

		var color = new THREE.Color();

		// build voxels
		for( var ix = 0; ix <= this.numCubes; ix++ )
		for( var iy = 0; iy <= this.numCubes; iy++ )
		for( var iz = 0; iz <= this.numCubes; iz++ )
		{
			var p = new THREE.Vector3();
			p.x = (ix/this.numCubes)*2-1;
			p.y = (iy/this.numCubes)*2-0.5;
			p.z = (iz/this.numCubes)*2-1;

			if (this.phase < 7) {
				if (p.y > neck)
					continue;
			} else {
				if (p.y <= neck)
					continue;
			}

			var d = scenedist(p);
			if (d[3] > 0.01)
				continue;

			p.y -= 0.5;
			var angle = (Math.atan2(p.x,p.z) + 2*Math.PI) % (2*Math.PI);

			if (this.phase < 7)
			{
				var p1 = (this.phase-1)/6.0*2*Math.PI;
				var p2 = (this.phase)/6.0*2*Math.PI;
				if (angle < p1 || angle >= p2)
					continue;
			}

			var startPosition = p.clone();
			//p.y += 5;
			startPosition.y = p.y + (5+p.y)*(1+rnd(1));

			var rotation = new THREE.Euler();
			rotation.x = rnd() * 0.01*Math.PI;
			rotation.y = angle;
			rotation.z = rnd() * 0.01*Math.PI;

			var scale = new THREE.Vector3();
			scale.x = cubesize();
			scale.y = cubesize();
			scale.z = cubesize();

			var cube = this.cubeMesh.clone();
			//cube.geometry = this.cubeMesh.geometry.clone();
			cube.endPosition = p;
			cube.startPosition = startPosition;
			cube.position.copy(cube.startPosition);
			cube.rotation = rotation;
			cube.quaternion.setFromEuler( rotation );
			cube.scale = scale;

			cube.material = this.cubeMesh.material.clone();
			cube.material.color.setRGB(d[0],d[1],d[2]);
			cube.material.ambient.setRGB(d[0],d[1],d[2]);

			//applyVertexColors( cube.geometry, color.setRGB(d[0],d[1],d[2]));

			this.cubes.push( cube );
			scene.add( cube );
		}
	},

	this.animate = function (t, perc, t_global )
	{
		perc *= 4;
		var inc = t-this.lasttime;
		this.lasttime = t;
		//scene.rotation.y = t;
		//scene.quaternion.setFromEuler( scene.rotation );
		//scene.matrix.compose(scene.position,scene.quaternion,scene.scale);

		camera.position.set( this.camera_distance*Math.sin(t_global), 0, this.camera_distance*Math.cos(t_global) );
		var sp = scene.position.clone();
		sp.y -= 0*0.4;
		camera.lookAt(sp);

		for(var i = 0, l=this.cubes.length; i < l; i++)
		{
			var cube = this.cubes[i];

			var position = new THREE.Vector3();
			position.x = mix(cube.startPosition.x, cube.endPosition.x, perc*perc);
			position.y = Math.max(cube.startPosition.y-10*perc*perc, cube.endPosition.y);
			position.z = mix(cube.startPosition.z, cube.endPosition.z, perc*perc);
			cube.position.copy(position);
		}
	}

	this.update = function ( t, perc, t_global ) {

		this.animate( t, perc, t_global );

		if ( this.renderToTexture )
			renderer.render( scene, camera, this.fbo, this.clearBuffer );
		else
			renderer.render( scene, camera );

	};

};