var Scene = (function() {
	var lerp = function(s, e, p) {
		return s - ((s - e) * p);
	}

	return {
		id: getIdForLastLoadedScript(),
		load: function()
		{
			this.scene = new THREE.Scene();

			this.axishelper = new THREE.AxisHelper(5);
			this.scene.add(this.axishelper);

			
			var light = new THREE.HemisphereLight( 0xffffff, 0x000000, .2 );
			light.position.y = 100;
			this.scene.add( light );
			

			this.light2 = new THREE.PointLight(0xffffff, 1, 1000);
			//this.light2.castShadow = true;
			this.light2.position.set(50,50,50);
			this.scene.add(this.light2);

			this.caveWallMat = new THREE.MeshLambertMaterial({
					color: 0x7E430E,
					//color: 0x433242,
					shading: THREE.FlatShading,
					//side: THREE.BackSide,
					wireframe: false
			});

			this.entranceWallMat = new THREE.MeshLambertMaterial({
					color: 0x7E430E,
					shading: THREE.FlatShading,
					side: THREE.DoubleSide,
					wireframe: false
			});

			this.testlight = new THREE.PointLight(0xffffff, 0.4, 50);
			this.scene.add(this.testlight);

			// "sky" material
			var nothingMaterial = new THREE.MeshBasicMaterial({ color: 0xd1f2e9});

			var rockMat = new THREE.MeshPhongMaterial({
				color: 0x442914,
				//shading: THREE.FlatShading
			});
			/*this.skyGeo = new THREE.SphereGeometry(5000, 30,30);
			this.skyMat = new THREE.MeshBasicMaterial({
				color: 0xd1f2e9,
				side: THREE.DoubleSide
			});
			this.sky = new THREE.Mesh(this.skyGeo, this.skyMat);
			this.scene.add(this.sky);
			*/

			var helperMatRed = new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true});

			var caves = [
				{ // entrance
					position: new THREE.Vector3(0,0,200),
					radius: 10
				},
				{
					position: new THREE.Vector3(0,-30,-200),
					radius: 100
				}				
			];

			var tunnelPoints = [
				new THREE.Vector3(0,0,200),
				new THREE.Vector3(0,0,150),
				new THREE.Vector3(0,0,100),
				new THREE.Vector3(0,0,50),
				//new THREE.Vector3(0,0,20),
				new THREE.Vector3(0,-30,-30),

				new THREE.Vector3(0,-30,-150),

				new THREE.Vector3(0,-30,-200),


				new THREE.Vector3(0,-30, -400),
				new THREE.Vector3(0,-30, -450),

				new THREE.Vector3(100,-30, -600),

				new THREE.Vector3(50, 100, -800),

				new THREE.Vector3(-50, 0, -900),

				new THREE.Vector3(50, 0, -1000),

				new THREE.Vector3(50, 0, -1100),

				new THREE.Vector3(100, 0, -1150),

				new THREE.Vector3(100, 100, -1250),

				new THREE.Vector3(50, 50, -1350),


				new THREE.Vector3(0,-30,-1750),
				new THREE.Vector3(0,-30,-1850),
				new THREE.Vector3(0,-30,-1950),

				new THREE.Vector3(0,-30,-2000),
				new THREE.Vector3(75,50,-2050),
				new THREE.Vector3(100,0,-2100)
			];

			// Creating a tunnel in the cave system
			this.tunnelSpline = new THREE.SplineCurve3(tunnelPoints);

			// Camera spline
			var cameraPoints = [new THREE.Vector3(0,0,400)];
			for (var i=0; i<tunnelPoints.length; i++)
			{
				cameraPoints.push(tunnelPoints[i]);
			}
			this.cameraSpline = new THREE.SplineCurve3(cameraPoints);

			var splineMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
			//var spline = new THREE.Mesh();

			var geometry = new THREE.Geometry();
			
			var splinePoints = this.cameraSpline.getPoints(100);

			for(var i = 0; i < splinePoints.length; i++){
				geometry.vertices.push(splinePoints[i]);  
			}

			var line = new THREE.Line(geometry, splineMaterial);
			//this.scene.add(line);


			this.tunnelGeometry = new THREE.TubeGeometry (
				this.tunnelSpline,
				750, // segments
				5, // radius
				11, //radius segments
				false // open ended
			);


			this.wiggleCoordinates(this.tunnelGeometry, 1, 3434);

			
			var cavePosition = new THREE.Vector3(0,0,100);
			var caveRadius = 50;

			// add helper
			var sphereGeo = new THREE.DodecahedronGeometry(caveRadius,1);
			var sphereMEsh = new THREE.Mesh(sphereGeo, helperMatRed);
			sphereMEsh.position.copy(cavePosition);
			//this.scene.add(sphereMEsh);

			
			for (var c=0; c<caves.length; c++)
			{
				for (var i=0; i<this.tunnelGeometry.vertices.length; i++)
				{
					var v = this.tunnelGeometry.vertices[i];

					// Find distance to center of cave.
					var dist = caves[c].position.distanceTo(v);
					if (dist <= caves[c].radius)
					{
						var angle = new THREE.Vector3().subVectors(v, caves[c].position).normalize();

						v.x = caves[c].position.x + angle.x * caves[c].radius;
						v.y = caves[c].position.y + angle.y * caves[c].radius;
						v.z = caves[c].position.z + angle.z * caves[c].radius;
					}
				}
			}

			flipNormals(this.tunnelGeometry);

			/*for (var i=0; i<tunnelGeometry.faces.length; i++)
			{
				var f = tunnelGeometry.faces[i];
				var vert = [f.a, f.b, f.c];

				for(var vi=0; vi<vert.length; vi++)
				{
					v = tunnelGeometry.vertices[vi];

				}
			}*/


			var debugMaterial = new THREE.MeshBasicMaterial({color:0xBC760E, wireframe:true});
			var debugMesh = new THREE.Mesh(this.tunnelGeometry, debugMaterial);
			//this.scene.add(debugMesh);


			this.caveMesh = new THREE.Mesh(this.tunnelGeometry, this.caveWallMat);
			this.scene.add(this.caveMesh)

			
			// ================================================================================================
			// Add the entrance
			this.entrance = new THREE.Object3D(); // to enable hiding it when not in use.

			var sphincterGeometry = new THREE.TorusGeometry(10,3,5,10);
			var sphincter = new THREE.Mesh(sphincterGeometry, this.entranceWallMat);
			sphincter.position.set(0,0,200);
			//this.entrance.add(sphincter);

			var bigRock = makeRock(70,1,654654);

			//var entranceBoxGeo = new THREE.BoxGeometry(50,50,50,1,1,1);
			//entranceBoxGeo = bevelBoxEdges(entranceBoxGeo, 4);
			var entranceBoxGeo = bigRock.geometry;
			var entranceCubeCSG = new ThreeBSP(entranceBoxGeo);
			var cylinderThree = new THREE.CylinderGeometry(10.5,10.5,1000);
			cylinderThree.applyMatrix(new THREE.Matrix4().makeRotationX(deg2rad(90)));
			var entranceCylinderCSG = new ThreeBSP(cylinderThree);
			entranceCubeCSG = entranceCubeCSG.subtract(entranceCylinderCSG);

			var entranceBoxGeo = entranceCubeCSG.toGeometry();
			this.entranceBox = new THREE.Mesh(entranceBoxGeo, rockMat);
			this.entranceBox.position.z = 200 - 50/2;
			this.entrance.add(this.entranceBox);
			

			// Add some rocks to the box.
			//var entranceRocks = makeRock(7,3,444);
			var seed = 44555;
			Math.seedrandom(seed);
			for (var i=0; i<100; i++)
			{
				var entranceRock = makeRock(4+Math.random()*3, 0, seed++);
				var x = (Math.random()-0.5)*2*160;
				x = x>0 ? x+30 : x-30;
				var y = (Math.random()-0.5)*2*160;
				y = y>0 ? y+30 : y-30;
				entranceRock.position.set( x, y, ((Math.random()-0.5)*2)*150);
				entranceRock.position.z += 250;
				this.entrance.add(entranceRock);
			}

			// Add the nothingness
			// Start with a tube to hide the tunnel
			var nothingCylinderGeo = new THREE.CylinderGeometry(10,10,90,10,1,true);
			var nothingCylinder = new THREE.Mesh(nothingCylinderGeo, nothingMaterial);
			nothingCylinder.customOverrideMaterial = new THREE.MeshBasicMaterial({color:0x000000});
			nothingCylinder.rotation.x = deg2rad(90);
			nothingCylinder.position.z = 200-90;
			this.entrance.add(nothingCylinder);

			// Add a plane of nothingness
			var nothingPlaneCSG = new ThreeBSP(new THREE.BoxGeometry(800,1,800));
			//var nothingRemover = new ThreeBSP
			nothingPlaneCSG = nothingPlaneCSG.subtract(new ThreeBSP(new THREE.CylinderGeometry(10,10,10,10,1,false)));
			var nothingPlaneGeo = nothingPlaneCSG.toGeometry();
			var nothingPlane = new THREE.Mesh(nothingPlaneGeo, nothingMaterial);
			nothingPlane.customOverrideMaterial = new THREE.MeshBasicMaterial({color:0x000000});
			nothingPlane.rotation.x = deg2rad(90);
			nothingPlane.position.set(0,0,70);
			this.entrance.add(nothingPlane);
			var nothingPlane2 = new THREE.Mesh(new THREE.BoxGeometry(1,800,800), nothingMaterial);
			nothingPlane2.customOverrideMaterial = new THREE.MeshBasicMaterial({color:0x000000});
			nothingPlane2.position.set(400,0,470);
			this.entrance.add(nothingPlane2);
			var nothingPlane3 = new THREE.Mesh(new THREE.BoxGeometry(1,800,800), nothingMaterial);
			nothingPlane3.customOverrideMaterial = new THREE.MeshBasicMaterial({color:0x000000});
			nothingPlane3.position.set(-400,0,470);
			this.entrance.add(nothingPlane3);
			
			this.entrance.entranceLight = new THREE.PointLight(0xffffff, 1, 250);
			this.entrance.entranceLight.position.set(0,100,300);
			this.scene.add(this.entrance.entranceLight);

			this.scene.add(this.entrance);



			this.floor = new THREE.Mesh(
				new THREE.BoxGeometry(200,100,200),
				rockMat
			);
			this.floor.position.set(0, -70-50, -200);
			this.scene.add(this.floor);


			this.wall1 = new THREE.StoneSinus();
			this.wall1.position.set(-60,-35,-200);
			this.scene.add(this.wall1);

			this.wall2 = new THREE.StoneSinus();
			this.wall2.position.set(60,-35,-200);
			this.wall2.rotation.y = deg2rad(180);
			this.scene.add(this.wall2);
			

			// Add the zomgtronics logo
			this.neonColors = {
				true: {
					"inner": new THREE.Color(1.0, 1.0, 1.0),
					"outer": new THREE.Color(1.0, 1.0, 0.0)
				},
				false: {
					"inner": new THREE.Color(0.5, 0.5, 0.5),
					"outer": new THREE.Color(0.3, 0.3, 0.3)
				}
			}

			this.neonMaterial = new THREE.ShaderMaterial({
				uniforms: {
					"OuterColor": { "type": "c", "value": this.neonColors[true]["outer"] },
					"InnerColor": { "type": "c", "value": this.neonColors[true]["inner"] }
				},
				vertexShader: shaders.vertexShader_microscope,
				fragmentShader: shaders.fragmentShader_microscope
			});

			var loader = new THREE.JSONLoader();
			loader.load( "models/zomgtronics.js", function( geometry ) {
				var mesh = new THREE.Mesh( geometry, scenes.caves.neonMaterial );
				mesh.customOverrideMaterial = new THREE.MeshBasicMaterial({color:0x000000});
				mesh.scale.x = mesh.scale.y = mesh.scale.z = 0.3;
				mesh.rotation.set(-1.41, 1.37, 1.4);
				mesh.position.set(-59.1, -58.1, -170);
				scenes.caves.zomgtronics = mesh;
				scenes.caves.scene.add( mesh );
			});

			this.neonLight = new THREE.PointLight(0xffff00, 1, 25);
			this.neonLight.position.set(-50, -61, -148);
			this.scene.add(this.neonLight);

			this.neonLight2 = new THREE.PointLight(0xffff00, 1, 25);
			this.neonLight2.position.set(-50, -61, -162);
			this.scene.add(this.neonLight2);

			this.neonLight3 = new THREE.PointLight(0xffff00, 1, 25);
			this.neonLight3.position.set(-50, -61, -180);
			this.scene.add(this.neonLight3);


			// Add flying pig
			this.pig = new THREE.Pig();
			this.pig.scale.set(0.55, 0.55, 0.55);
			this.pig.position.set(1000,1000,1000);
			this.scene.add(this.pig);


			this.composer = new THREE.EffectComposer(renderer);

			this.camera = new THREE.PerspectiveCamera(55, 16/9, 0.1, 6000);
			this.camera.position.set(500, 500, 500);
			this.camera.lookAt(new THREE.Vector3(0, 0, 0));

			//this.controls = new THREE.OrbitControls( this.camera );

			// SSAO Depth
			this.depthShader = THREE.ShaderLib[ "depthRGBA" ];
			this.depthUniforms = THREE.UniformsUtils.clone( this.depthShader.uniforms );

			this.depthMaterial = new THREE.ShaderMaterial({
				fragmentShader: this.depthShader.fragmentShader,
				vertexShader: this.depthShader.vertexShader,
				uniforms: this.depthUniforms
			});
			this.depthMaterial.blending = THREE.NoBlending;

			this.depthTarget = new THREE.WebGLRenderTarget( wWidth, wHeight, {
				minFilter: THREE.NearestFilter,
				magFilter: THREE.NearestFilter,
				format: THREE.RGBAFormat
			});
			
			this.renderPass = new THREE.RenderPass(this.scene, this.camera);
			this.composer.addPass(this.renderPass);
/*
			this.blurPass1 = new THREE.ShaderPass(THREE.VerticalBlurShader);
			this.blurPass1.uniforms["v"].value = 1 / wHeight;
			this.composer.addPass(this.blurPass1);

			this.blurPass2 = new THREE.ShaderPass(THREE.HorizontalBlurShader);
			this.blurPass2.uniforms["h"].value = 1 / wWidth;
			this.composer.addPass(this.blurPass2);
*/
			this.SSAOPass = new THREE.ShaderPass( THREE.SSAOShader );
			this.SSAOPass.uniforms[ 'tDepth' ].value = this.depthTarget;
			this.SSAOPass.uniforms[ 'size' ].value.set( wWidth * 0.75, wHeight * 0.75 );
			this.SSAOPass.uniforms[ 'cameraNear' ].value = this.camera.near;
			this.SSAOPass.uniforms[ 'cameraFar' ].value = this.camera.far;
			this.SSAOPass.uniforms[ 'aoClamp' ].value = 0.5;
			this.SSAOPass.enabled = true;
			this.composer.addPass( this.SSAOPass );

			this.fxaaPass = new THREE.ShaderPass(THREE.FXAAShader);
			this.fxaaPass.uniforms[ 'resolution' ].value = new THREE.Vector2( 1 / wWidth, 1 / wHeight );
			this.composer.addPass(this.fxaaPass);

			this.composer.addPass(final_pass);
		},

		render: function(time, percent) 
		{

			var p = getVal('var5');

			var pigPos = this.cameraSpline.getPointAt(p/100);
			this.pig.position.set(pigPos.x, pigPos.y, pigPos.z);
			this.pig.rotation.set(p*0.8/4,p/4,p*1.2/4);


			this.wall1.animate(time);
			this.wall2.animate(time+1);

			var neonBrightness = getVal("var3")*0.2;
			this.neonLight.intensity = neonBrightness;
			this.neonLight2.intensity = neonBrightness;
			this.neonLight3.intensity = neonBrightness;


			var neonIsOn = (getVal("var3") >= 1.0);
			if(neonIsOn !== this.prevNeonIsOn) {
				this.neonMaterial.uniforms["InnerColor"].value = this.neonColors[neonIsOn]["inner"];
				this.neonMaterial.uniforms["OuterColor"].value = this.neonColors[neonIsOn]["outer"];
				this.prevNeonIsOn = neonIsOn;
			}

			this.controlCamera(this.camera);
			
			//this.testlight.position.copy(camVector);
		
			//console.log('Camera: '+camVector.x+', '+camVector.y+', '+camVector.z+' LookAt: '+lookVector.x+', '+lookVector.y+', '+lookVector.z);


			this.scene.overrideMaterial = this.depthMaterial;
			renderer.render( this.scene, this.camera, this.depthTarget );

			this.scene.overrideMaterial = null;
			this.composer.render();
		},

		wiggleCoordinates: function(geo, dist, seed)
		{
			for (var i=0; i < geo.vertices.length; i++)
			{
				geo.vertices[i].x += (Math.random()-0.5)*dist;
				geo.vertices[i].y += (Math.random()-0.5)*dist;
				geo.vertices[i].z += (Math.random()-0.5)*dist;
			}
			geo.computeFaceNormals();
			geo.computeVertexNormals();
		},

		getCamOrbit: function(lat, lon, radius)
		{
			var cam_x = radius * Math.sin(lat)*Math.cos(lon);
			var cam_z = radius * Math.sin(lat)*Math.sin(lon) - 200;
			var cam_y = radius * Math.cos(lat) - 30;

			return new THREE.Vector3(cam_x, cam_y, cam_z);
		},

		/*
			Call controlCamera() to get premade easy to use camera movements.

			var7 0-100: follow the spline
					var6 controls the lookAt point, also 0-100
			var7 >100: sherical coordinates. lookAt center.
				var7: lat
				var6: lon

			Camera offsets work like normal.

		*/

		controlCamera: function(camera)
		{
			var v7 = getVal('var7');
			var v6 = getVal('var6');

			if (v7 >= 0 && v7 <=100)
			{
				var campos = this.cameraSpline.getPointAt(v7/100)
				var lookpos = this.cameraSpline.getPointAt(v6/100)

			} else if (v7 > 100)
			{
				lookpos = new THREE.Vector3(0,0,0);
				campos = this.getCamOrbit(deg2rad(v7-100), deg2rad(v6), 95);
			}

			var camVector = new THREE.Vector3(campos.x + getVal('camx'), campos.y + getVal('camy'), campos.z + getVal('camz'));
			var lookVector = new THREE.Vector3(lookpos.x + getVal('cam2x'), lookpos.y + getVal('cam2y'), lookpos.z + getVal('cam2z'));

			camera.position.set(camVector.x, camVector.y, camVector.z);
			camera.lookAt(lookVector);
		}

	};
})();

var scenes = scenes || [];
scenes[Scene.id] = Scene;
