//Namespace
var timeDirection=1;

this.wideload = this.wideload || {};

(function(){
	
	var skipToTime = 0;
	
	//Constructor
	var Main = function(){
		
	    this.fft = new wideload.FFT();

	    
		
		//we can start after the sound is loaded and we are in fullscreen
		Main.LOAD_COMPLETED = {sound: false, fullScreen: false};
		
		this.parts = {
			red: new wideload.BasePart(this,20,"red"),

			Xor: new wideload.XOR(this, 20, "XOR"),
			Noise: new wideload.Noise(this, 20, "Noise"),
			Julia: new wideload.Julia(this, 20, "Julia"),
			Teddy: new wideload.Teddy(this, 20, "Teddy"),
			LightGradient: new wideload.LightGradient(this, 20, "LightGradient"),
			RandomEffect: new wideload.RandomEffect(this, 20, "RandomEffect"),
      		Fireballs: new wideload.Fireballs(this,20,"Fireballs"),
      		DemoName: new wideload.DemoName(this,20,"DemoName"),
      		Greets: new wideload.Greets(this,20,"Greets"),
      		Starfall: new wideload.Starfall(this,20,"Starfall"),
      		Ballsea: new wideload.Ballsea(this,20,"Ballsea"),
			
			Eclipser: new wideload.GenericText(this, 20, "Eclipser", "ECLIPSER", 3, 100),
			Poro: new wideload.GenericText(this, 20,"Poro", "PORO", 2, 100),
			Rimina: new wideload.GenericText(this, 20,"Rimina", "RIMINA", 3, 100),
			Exca: new wideload.GenericText(this, 20, "Exca", "EXCA", 2, 100),
			Music: new wideload.GenericText(this, 20, "Music", "MUSIC", 2, 100),

			Synchronized: new wideload.GenericText(this, 20, "Synchronized", "SYNCHRONIZED", 3, 70,60),
			Catastrophic: new wideload.GenericText(this, 20, "Catastrophic", "CATASTROPHIC", 3, 70,60),
			Monitor: new wideload.GenericText(this, 20, "Monitor", "MONITOR", 3, 70,50),
			Overload: new wideload.GenericText(this, 20, "Overload", "OVERLOAD", 3, 70,50),	
			Bywideload: new wideload.GenericText(this, 20, "Bywideload", "BY WIDE LOAD", 3, 60),	

		};
		
		//Add listeners for windowed & fullscreen
		document.getElementById("fullscreen").addEventListener('click', createjs.proxy(this.handleStart, this), false);
		document.getElementById("windowed").addEventListener('click', createjs.proxy(this.handleStart, this), false);
		
		//Add listener for bg music.
		createjs.Sound.addEventListener("fileload", createjs.proxy(this.soundLoaded, this));
		createjs.Sound.alternateExtensions=["mp3"];
		createjs.Sound.registerSound("bin/bg.ogg", "bg");
		this.drumDelta = 0;
		this.otherDelta = 0;
		this.drumIndex = 0;
		this.otherIndex = 0;
		this.drumSync = [
		];
		
		
		this.otherSync = [

		];
		
		this.partialTime = 0;
		
	}

	//Prototype reference.
	var p = Main.prototype;
	
	/**
	* Mode selection handler.
	*/
	p.handleStart = function(e)
	{
		//Check if target is fullscreen-button
		this.fullscreen = e.target == document.getElementById("fullscreen");
		
		//Initialize the program.
		this.initialize();
		
		//Set element states
		var element = this.renderer.domElement;
		var demoEl = document.getElementById("demo");
		if(this.fullscreen)
		{
			if (demoEl.requestFullscreen) {
				demoEl.requestFullscreen();
			} else if (demoEl.mozRequestFullScreen) {
				demoEl.mozRequestFullScreen();
			} else if (demoEl.webkitRequestFullscreen) {
				demoEl.webkitRequestFullscreen();
			} else{
				console.log("fail");
			}
		}
		document.getElementById("fullscreen").style.display = "none";
		document.getElementById("windowed").style.display = "none";
		Main.LOAD_COMPLETED.fullScreen = true;
		//Add delay if going to fullscreen.
		if(e.target == document.getElementById("fullscreen"))
		{ 
			window.setTimeout( createjs.proxy(this.checkStart,this),5000);
		}
		else
		{
			element.style.position="absolute";
			element.style.top = "50%";
			element.style.left = "50%";
			element.style.marginTop = "-360px";
			element.style.marginLeft= "-640px";

			this.checkStart();
		}
	}
	
	/**
	* Sound loaded -handler
	*/
	p.soundLoaded = function(){
		this.fft.loadSound();
		Main.LOAD_COMPLETED.sound = true;
		this.checkStart();
	}

	/**
	* Check if the demo can be started.
	*/
	p.checkStart = function(){
		if(Main.LOAD_COMPLETED.sound && Main.LOAD_COMPLETED.fullScreen){
			//Start the sound if not playing
			
			//tämän leveys/korkeus tulee olla sama kuin RESO.x/RESO.y
			this.display = new THREE.Mesh(new THREE.PlaneGeometry(this.width, this.height),
				new THREE.MeshBasicMaterial({map : this.renderTarget}));
			this.display.position.z = -200;
			var copypass = new THREE.RenderPass(THREE.CopyPass);
			copypass.renderToScreen = true;
			this.finalScene.add(this.display);
			
			
			this.effectBloom = new THREE.BloomPass( 2.5, 20, 18 , 512);
			var effectScreen = new THREE.ShaderPass( THREE.ShaderExtras[ "screen" ] );
			effectScreen.renderToScreen = true;
			
			
			// Prepare the occlusion composer's render target
			var renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBufer: false };
			this.renderTargetOcl = new THREE.WebGLRenderTarget(this.width , this.height , renderTargetParameters);

		    // Prepare the simple blur shader passes
			var hblur = new THREE.ShaderPass(THREE.ShaderExtras["horizontalBlur"]);
			var vblur = new THREE.ShaderPass(THREE.ShaderExtras["verticalBlur"]);

			var bluriness = 3;

			hblur.uniforms["h"].value = bluriness / this.width;
			vblur.uniforms["v"].value = bluriness / this.height;

		    // Prepare the occlusion scene render pass
			var renderModelOcl = new THREE.RenderPass(this.oclscene, this.camera);

		    // Prepare the godray shader pass
			this.grPass = new THREE.ShaderPass(THREE.Extras.Shaders.Godrays);
			this.grPass.needsSwap = true;
			this.grPass.renderToScreen = false;
            
		    // Prepare the composer
			var oclcomposer = new THREE.EffectComposer(this.renderer, this.renderTargetOcl);
			
			oclcomposer.addPass(renderModelOcl);
			oclcomposer.addPass(hblur);
			oclcomposer.addPass(vblur);
			oclcomposer.addPass(hblur);
			oclcomposer.addPass(vblur);
			//oclcomposer.addPass(this.grPass);
            
		    this.oclcomposer = oclcomposer;

			var finalPass = new THREE.ShaderPass(THREE.Extras.Shaders.Additive);
			finalPass.needsSwap = true;
			finalPass.renderToScreen = false;
			finalPass.uniforms['tAdd'].value = oclcomposer.renderTarget1;
			finalPass.uniforms['fCoeff'].value = 1;

		    var copyPass = new THREE.ShaderPass(THREE.CopyShader);
		    copyPass.renderToScreen = true;
            /*
		    this.display.material.map = oclcomposer.renderTarget1;
		    this.display.material.needsUpdate = true;
            */
		    var renderModel = new THREE.RenderPass(this.finalScene, this.finalCamera);

		    var effectFXAA = new THREE.ShaderPass(THREE.ShaderExtras["fxaa"]);
		    effectFXAA.uniforms['resolution'].value.set(1 / this.width, 1 / this.height);

		    this.composer = new THREE.EffectComposer(this.renderer);
		 //   this.composer.addPass(renderModelOcl);
		    this.composer.addPass(renderModel);
		    
		    //this.composer.addPass(copyPass);
		    
			
		    this.composer.addPass(this.effectBloom);
		//    this.composer.addPass(effectFXAA);
		 //   this.composer.addPass(finalPass);
		 //   this.composer.addPass(renderModelOcl);
		//    this.composer.addPass(this.grPass);
		  //  this.composer.addPass(finalPass);
			this.composer.addPass(effectScreen);
			var ss = this;
			setTimeout(function(){
if(!this.soundPlaying){
				ss.soundPlaying = true;
				ss.bgSound = createjs.Sound.play("bg");
				ss.bgSound.setPosition(skipToTime);
				ss.bgSound.setVolume(1);
			}
			ss.mainLoop();
			},1000);
			ss.mainLoop();
		}
	}
	
	/**
	* Initialize the main scene & other related stuff.
	*/
	p.initialize = function() {
		//because demo is in fullscreen mode setting width and height according to
		//screen property should work. 
		if(this.fullscreen)
		{
			var w = screen.width;
			var h = screen.height;
			//TODO - calculate the dimensions so that black borders can be applied
			this.width = w;
			this.height = h;
		}
		else
		{
			this.width = 1280;
			this.height = 720;
		}
		this.screenResolution = {width:this.width, height: this.height};
	    //Main render targets resolution. Parts can have different targets.
	    this.resolution = { width: 256, height: 256 };

		if(this.fullscreen)
		{
			var w = screen.width;
			var h = screen.height;
			//TODO - calculate the dimensions so that black borders can be applied
			this.width = w;
			this.height = h;
		}
		else
		{
			this.width = 1280;
			this.height = 720;
		}
		this.renderTarget = new THREE.WebGLRenderTarget( this.width, this.height, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat } );	
		
		
		//Build the renderer.
		this.renderer = new THREE.WebGLRenderer();
		this.renderer.setSize(this.width, this.height);
		this.renderer.setClearColor(0x000000);
		
		this.renderer.domElement.style.width = this.width;
		this.renderer.domElement.style.height = this.height;
		
		//Add the renderer element.
		document.getElementById("demo").appendChild( this.renderer.domElement );

		//Create the main scene that displays only resulting rendertarget
		this.finalScene = new THREE.Scene();
		
		//Camera to watch the rendertarget drawn to plane.
		this.finalCamera = new THREE.OrthographicCamera(-(this.width/2), this.width/2, (this.height/2), -(this.height/2));
		this.finalCamera.position.z = 400;
		this.finalScene.add(this.finalCamera);
		
		this.currentPart = 0;
		this.frameCount = 0;
		this.timeCount = 0;
		
		/* Initialize 'screens'*/
		
        // Part initialization
		for (var partKey in this.parts) {
		    var part = this.parts[partKey];
		    part.width = this.width;
		    part.height = this.height;
		    part.initialize(this.renderer);
		}


		this.initializeScene();
		this.initializeScreens(this.effectRenderTargets);
		
	    // Initializing configuration for master

		this.MasterRenderer = new wideload.MasterRenderer(this, this.renderer, this.resolution);
		this.InitializeConfiguration();
		
		this.camera.position.x = this.configuration.cameraDrive[0].startAt[0];
		this.camera.position.y = this.configuration.cameraDrive[0].startAt[1];
		this.camera.position.z = this.configuration.cameraDrive[0].startAt[2];
		var lookTarget = this.configuration.cameraDrive[0].lookAt;
		this.camera.lookAt(new THREE.Vector3(lookTarget[0], lookTarget[1], lookTarget[2]));
		this.cameraIndex = 0;
		this.camLook = lookTarget;
		this.campos = [this.camera.position.x, this.camera.position.y, this.camera.position.z];

		this.activeParts = new Array();
	}
	
    p.InitializeConfiguration = function() {
        var configuration = new wideload.Configuration();
        this.configuration = configuration;
		configuration.prepare();
        
        
        this.soundPlaying = false;
        this.blinkColor = new THREE.Color(0x000000);
        this.blinkTime = 0;
        this.monitorsBlink = 0;
    }

    p.playEffect = function(partConfiguration){
		var part = this.parts[partConfiguration.effect];
        var screens = [];
        for (var j = 0; j < partConfiguration.screens.length; ++j) {
            var confScreen = partConfiguration.screens[j];
            var actualScreen = this.getMeshByPosition(confScreen[0], confScreen[1]);
            actualScreen.xSegment = confScreen[0];
            actualScreen.ySegment = confScreen[1];
            screens.push(actualScreen);
        }
		if(configuration.zoomBall){
			this.zoomMonitorsTo(0.049);
		}
        this.MasterRenderer.playEffect(part, screens, partConfig);        
    }

	p.initializeScene = function(){
		this.scene = new THREE.Scene();
		this.scene.fog = new THREE.Fog( 0x000000, 1, 20000 );
		this.camera = new THREE.PerspectiveCamera( 45, this.width / this.height, 1, 20000 );
		this.zoom = 0;
		this.rotationZ = 0;
		this.curblink = 0;
	    // Oclusion:
		this.oclscene = new THREE.Scene();
		//this.oclscene.add(new THREE.AmbientLight(0xffffff));
		this.scene.autoUpdate = true;
	    // Volumetric light
		this.vlight = new THREE.Mesh(
            new THREE.IcosahedronGeometry(40, 3),
            new THREE.MeshBasicMaterial({
                color: 0x77bbff
            })
        );
		this.vlight.position.y = 0;
		this.oclscene.add(this.vlight);

		var dustTexture = new THREE.Texture(jsAsset_spark1);
        dustTexture.needsUpdate = true;
		// DustParticles
		this.dustParticleCount = 800,
	    this.dustParticles = new THREE.Geometry(),
		pMaterial = new THREE.ParticleBasicMaterial({
			color: 0xaaaa88,
			size: 12,
			map: dustTexture,
			blending: THREE.AdditiveAlphaBlending ,
			transparent: true,
			opacity:0.5
		});

		for(var p = 0; p < this.dustParticleCount; p++) {
			var pX = wideload.Random.nextFloat() * 600 - 300,
				pY = wideload.Random.nextFloat() * 1000 - 500 ,
				pZ = wideload.Random.nextFloat() * 600 - 300,
		    	particle = new THREE.Vector3(pX, pY, pZ);
			particle.velocity = new THREE.Vector3(
				wideload.Random.nextFloat()/20,				// x
				wideload.Random.nextFloat()/5,	// y
				wideload.Random.nextFloat()/20);				// z

			// add it to the geometry
			this.dustParticles.vertices.push(particle);
		}
		this.dustParticleSystem = new THREE.ParticleSystem(this.dustParticles,pMaterial);
		this.dustParticleSystem.sortParticles = true;
		this.scene.add(this.dustParticleSystem);


		// Camera drives

		var camera = this.camera;
		var scope = this;
		document.addEventListener('keydown',function(event){
			
		var camdir = new THREE.Vector3(0,0,-10);
		var left = new THREE.Vector3(-10,0,0);
		var right = new THREE.Vector3(10,0,0);
		var up = new THREE.Vector3(0,10,0);
		var down = new THREE.Vector3(0,-10,0);
		var forward = new THREE.Vector3(0,0,-10);
		var back = new THREE.Vector3(0,0,10);
		
		var rotLeft = new THREE.Vector3(0,0,1);
		var rotRight = new THREE.Vector3(0.1,0,0);
		var rotUp = new THREE.Vector3(0,0.1,0);
		var rotDown = new THREE.Vector3(0,-0.1,0);
		
		camdir.applyQuaternion(camera.quaternion);
		left.applyQuaternion(camera.quaternion);
		right.applyQuaternion(camera.quaternion);
		up.applyQuaternion(camera.quaternion);
		down.applyQuaternion(camera.quaternion);
		forward.applyQuaternion(camera.quaternion);
		back.applyQuaternion(camera.quaternion);
		rotLeft.applyQuaternion(camera.quaternion);
		rotRight.applyQuaternion(camera.quaternion);
		rotUp.applyQuaternion(camera.quaternion);
		rotDown.applyQuaternion(camera.quaternion);
		
		if(event.keyCode == '37'){
			camera.position.add(left);
		}
		if(event.keyCode == '39'){
			camera.position.add(right);
		}
		if(event.keyCode == '38'){
			camera.position.add(up);
		}
		if(event.keyCode == '40'){
			camera.position.add(down);
		}
		if(event.keyCode == '79'){
			camera.position.add(forward);
		}
		if(event.keyCode == '76'){
			camera.position.add(back);
		}
		if(event.keyCode == '65')
		{
			camera.rotateOnAxis(new THREE.Vector3(0,1,0), 0.1);
		}
		if(event.keyCode == '68')
		{
			camera.rotateOnAxis(new THREE.Vector3(0,1,0), -0.1);
		}
		if(event.keyCode == '87')
		{
			camera.rotateOnAxis(new THREE.Vector3(1,0,0), 0.1);
		}
		if(event.keyCode == '83')
		{
			camera.rotateOnAxis(new THREE.Vector3(1,0,0), -0.1);
		}
		if(event.keyCode == '81')
		{
			camera.rotateOnAxis(new THREE.Vector3(0,0,1), 0.1);
		}
		if(event.keyCode == '69')
		{
			camera.rotateOnAxis(new THREE.Vector3(0,0,1), -0.1);
		}
		if(event.keyCode == '80')
		{
			console.log(camera.position);
			console.log(camdir);
			console.log("Camera rotations: x " + camera.rotation.x + " y " + camera.rotation.y + " z " + camera.rotation.z+ " Array ( " + camera.rotation.x + ","+camera.rotation.y+","+camera.rotation.z+ " )");
		}
		if(event.keyCode == "49")
		{
			scope.zoomMonitorsTo(scope.zoom + 0.001);
		}
		if(event.keyCode == "50")
		{
			scope.zoomMonitorsTo(scope.zoom - 0.001);
		}
		
	}, false);

	}

	p.initializeScreens = function(){
		var segments = 12;
		var heightSegments = 10;
		this.waitBlink = false;
		// if temprt is in use, all effects will be drawn here.
	//	var tempRt = new THREE.WebGLRenderTarget( this.width, this.height, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat } );
		this.monitors = new Array();
		this.effectProjectors = new Array();
	    this.godrayPanels = new Array();
		
		var height = Math.PI/10;
		var spaceBetween = Math.PI/(4*segments)-0.016;
		var radius = 400;
	    var innerRadius = 40;
		
	    var light = new THREE.PointLight(0xff0000, 1, 100);
	    this.scene.add(light);

	    var sphere = new THREE.SphereGeometry(innerRadius,8,8);
	    this.scene.add(new THREE.Mesh(sphere, new THREE.MeshBasicMaterial({color:0x000000})));

	    this.innerSpaceBetweenMaterial = new THREE.MeshBasicMaterial({ color: 'black', side: THREE.DoubleSide });
		this.monitorMaterials = [];
		
		for(var j = 0; j < segments; ++j){
			this.monitors.push(new Array());
			this.monitorMaterials.push([]);
			this.effectProjectors.push(new Array());
		    this.godrayPanels.push(new Array());
			for(var i = 0; i < heightSegments; ++i){
				var step = 2*Math.PI/segments;
				var geometry = new THREE.SphereGeometry(radius,8,8, j*step, -step+spaceBetween,i*height,height-spaceBetween);
			//	var geometryIn = new THREE.SphereGeometry(radius,8,8, j*step, -step+spaceBetween-0.049,i*height,height-(spaceBetween-0.049));
			//	geometry = geometryIn;
				var material = new THREE.MeshBasicMaterial({ color: 'black', side:THREE.DoubleSide });
				if(i == 0 || i == heightSegments-1){
					//top and bottom are always in lit position
					material = new THREE.MeshBasicMaterial({ color: 'white', side:THREE.DoubleSide});
				}
				this.monitorMaterials[j][i] = material;
				var mesh = new THREE.Mesh(geometry, material);
				//mesh.scale.x = 1;
				//mesh.position.x -= 100
				this.scene.add(mesh);
				
			    // Let's create innermesh too
				var innerGeometry = new THREE.SphereGeometry(innerRadius, 8, 8, j * step, -step+spaceBetween, i * height, height-spaceBetween);
				var innerMaterial = new THREE.MeshBasicMaterial({ color: 'black', side: THREE.DoubleSide });
				var innerMesh = new THREE.Mesh(innerGeometry, innerMaterial);
				var innerBetween = new THREE.Mesh(new THREE.SphereGeometry(innerRadius, 8, 8, (j * step)+spaceBetween, -spaceBetween,i * height, height), this.innerSpaceBetweenMaterial);
				var innerBetween2 = new THREE.Mesh(new THREE.SphereGeometry(innerRadius, 8, 8, j * step, -step+spaceBetween, (i * height), -spaceBetween), this.innerSpaceBetweenMaterial);
			    this.effectProjectors[j][i] = innerMesh;
			    this.monitors[j][i] = mesh;
			    this.scene.add(innerMesh);
			    this.scene.add(innerBetween);
			    this.scene.add(innerBetween2);
				
			    // OCclusion
			    // Occluding object
			    var gmat = new THREE.MeshBasicMaterial({ color: 'black', side:THREE.DoubleSide, envMap:this.renderTarget });
			    //var gmesh = new THREE.Mesh(innerBetween.geometry, gmat);
			    //var gmesh2 = new THREE.Mesh(innerBetween2.geometry, gmat);
			    var gGeometry = new THREE.SphereGeometry(innerRadius, 8, 8, j * step, -step, i * height, height);
			    var gmesh3 = new THREE.Mesh(gGeometry, gmat);
			    gmesh3.position = innerBetween.position;
			    gmesh3.rotation = innerBetween.rotation;
			    gmesh3.scale = innerBetween.scale;
			  //  gmesh2.position = innerBetween2.position;
			 //   gmesh2.rotation = innerBetween2.rotation;
			 //   gmesh2.scale = innerBetween2.scale;
			    //this.oclscene.add(gmesh);
			    //this.oclscene.add(gmesh2);
			    this.godrayPanels[j][i] = gmesh3;
			    this.oclscene.add(gmesh3);
			}
		}
	//	this.zoomMonitorsTo(0.02);
	}
	
	p.zoomMonitorsTo = function(zoom)
	{
		console.log(zoom);
		this.zoom = zoom;
		var segments = 12;
		var heightSegments = 10;
		
		var height = Math.PI/10;
		var spaceBetween = Math.PI/(4*segments)-0.016-zoom;
		var radius = 400;
		
		for(var j = 0; j < segments; ++j){
			for(var i = 0; i < heightSegments; ++i){
				var step = 2*Math.PI/segments;
				var geometry = new THREE.SphereGeometry(radius,8,8, j*step, -step+spaceBetween,i*height,height-spaceBetween);
				var mesh = this.monitors[j][i];
				//var material = new THREE.MeshBasicMaterial({ color: 'white', twosided:true });
			//material.visible = true;
			//	var nmesh = new THREE.Mesh(geometry, mesh.material);
			//	this.scene.remove(mesh);
			//	this.scene.add(nmesh);
				mesh.geometry.vertices = geometry.vertices;
				mesh.geometry.verticesNeedUpdate = true;
			//	this.monitors[j][i] = nmesh;
			}
		}
	}
	
	p.getMeshByPosition = function(xSegment, ySegment) {
	    return this.monitors[xSegment][ySegment];
	}

    // Example of open projector faces. Indexes are same as used in rendertargets:
    //this.changeProjectorFaces([w,h]);
	p.openProjectorFaces = function(openIndexes){
		for(var i = 0; i < openIndexes.length; ++i){
			var x = openIndexes[i][0];
			var y = openIndexes[i][1];
			var projector = this.effectProjectors[x][y];
		//	this.monitors[x][y].material.color.setHex(0xffffff);
		    projector.material.color.setHex(0x77bbff);
			//this.godrayPanels[x][y].material.color.setHex(0xffffff);
			//this.godrayPanels[x][y].material.visible = false;
		}
	}

	p.openAllMonitors = function(){
		for(var i = 0; i < this.monitors.length;++i){
			for(var j = 0; j < this.monitors[i].length;++j){
			//	this.monitors[i][j].material.color.setHex(0xffffff);
				this.monitors[i][j].material.visible = true;
				this.effectProjectors[i][j].material.color.setHex(0x77bbff);
				//this.effectProjectors[i][j].material.visible = false;
				//this.godrayPanels[i][j].material.color.setHex(0xffffff);
				//this.godrayPanels[i][j].material.visible = false;
			}
		}
	}
	
	p.closeProjectorFaces = function(openIndexes){
		for(var i = 0; i < openIndexes.length; ++i){
			var x = openIndexes[i][0];
			var y = openIndexes[i][1];
			var projector = this.effectProjectors[x][y];
			projector.material.visible = true;
		}
	}
	
	p.setEnvMapping = function(){
		// create a new THREE.Texture
		var envMap = new THREE.Texture();
 
		envMap.image = this.rendertarget; // array of images = [];
 
		// Set the mapping type
		envMap.mapping = new THREE.CubeReflectionMapping();
 
		// You have to set this to false, otherwise your images will be inverted on the Y axis
		envMap.flipY = false;
 
		// Mark as dirty, otherwise you won't see anything
		envMap.needsUpdate = true;
		for(var i = 0; i < this.effectProjectors.length;++i){
			for(var j = 0; j < this.monitors[i].length;++j){
			//	this.monitors[i][j].material.color.setHex(0xffffff);
				this.effectProjectors[i][j].material.envMap = envMap;
				//this.effectProjectors[i][j].material.needsUpdate = true;
				//this.effectProjectors[i][j].material.color.setHex(0x77bbff);
				//this.effectProjectors[i][j].material.visible = false;
				//this.godrayPanels[i][j].material.color.setHex(0xffffff);
				//this.godrayPanels[i][j].material.visible = false;
			}
		}
	}
	
	p.mainLoop = function(){
		var time = 0;
		if(this.bgSound != null)
			time = this.bgSound.getPosition();
		var frameTime = time - this.previous;
		var td = frameTime / 22;
		this.previous = time;
		if(time == 0)
			td = 1;
		if(time == 0 && this.currentPart > 0)
			time = this.bgSound.length;
		
		if(time > 136000 && !this.faded)
		{
			this.faded = true;
			$("#demo").animate({"opacity":0}, 1000);
		}
		
		time=timeDirection*time
		
		
		;	
        //this.openAllProjectorFaces();
        //TODO: Actual timing
        //

		//TEST
	//	this.camera.position.z = 200;
		
		
	//	this.camera.rotation.y += 0.0025*td;
	//	this.camera.rotation.z += 0.0015*td;
	//	this.camera.rotation.x += 0.0005*td;
		var previousXAmount = this.camera.position.x;
	//	this.camera.position.x = Math.sin(time/1000)*250;
	//	this.camera.position.y = Math.sin(time/2000)*250;
	//	this.camera.position.z = Math.cos(time/3000)*250;
		
		//this.camera.rotation.z += this.rotationZ;
		
		//this.camera.lookAt(new THREE.Vector3(0,0,0));	
	    //this.camera.position.z = 200;	
		
	    //Camera calculus
		if(this.cameraIndex < this.configuration.cameraDrive.length)
		{
			if(time > this.configuration.cameraDrive[this.cameraIndex].endTime)
			{
				this.partialTime += 1/this.configuration.cameraDrive.length;
				this.cameraIndex++;
			}
			if(this.cameraIndex < this.configuration.cameraDrive.length && time >= this.configuration.cameraDrive[this.cameraIndex].startTime)
			{
				var camo = this.configuration.cameraDrive[this.cameraIndex];
				var pos = (time - camo.startTime)/(camo.endTime - camo.startTime);
				if(pos > 1)
					pos = 1;
				this.campos[0] -=(this.campos[0]-( pos * (camo.moveTo[0] - camo.startAt[0])+camo.startAt[0]))/40;
				this.campos[1] -=(this.campos[1]- (pos * (camo.moveTo[1] - camo.startAt[1])+camo.startAt[1]))/40;
				this.campos[2] -=(this.campos[2]- (pos * (camo.moveTo[2] - camo.startAt[2])+camo.startAt[2]))/40;

//				this.camera.position.x = pos * (camo.moveTo[0] - camo.startAt[0])+camo.startAt[0];
	//			this.camera.position.y = pos * (camo.moveTo[1] - camo.startAt[1])+camo.startAt[1];
		//		this.camera.position.z = pos * (camo.moveTo[2] - camo.startAt[2])+camo.startAt[2];
				this.camera.position.x = this.campos[0];
				this.camera.position.y = this.campos[1];
				this.camera.position.z = this.campos[2];
				
				var partial = this.partialTime + pos * 1/this.configuration.cameraDrive.length;
			//	console.log("P " + partial);
				var p = this.configuration.path.getPoint(partial);
				this.camera.position.x = p.x;
				this.camera.position.y = p.y;
				this.camera.position.z = p.z;
				
				var lookTarget = new THREE.Vector3(
					camo.startLookAt[0] + (pos * (camo.lookAt[0]-camo.startLookAt[0])),
					camo.startLookAt[1] + (pos * (camo.lookAt[1]-camo.startLookAt[1])),
					camo.startLookAt[2] + (pos * (camo.lookAt[2]-camo.startLookAt[2]))
				);
				lookTarget = this.configuration.lookPath.getPoint(partial);
			//	console.log(lookTarget);
				this.camera.lookAt(lookTarget);
			}
		}
		this.drumDelta -= 0.05;
		this.otherDelta -= 0.05;
		if(this.drumDelta < 0)
			this.drumDelta = 0;
		if(this.otherDelta < 0)
			this.otherDelta = 0;
		if( this.drumIndex < this.drumSync.length && time > this.drumSync[this.drumIndex])
		{
			console.log("drum " + time);
			this.drumDelta = 1;
			this.drumIndex++;
		}
		if( this.otherIndex < this.otherSync.length && time > this.otherSync[this.otherIndex])
		{
			this.otherDelta = 1;
			this.otherIndex++;
		}
		
		++this.frameCount;
		
		var projectorSequence = this.configuration.projectorStartUpSequence;
        if(time >= projectorSequence.stayBlack && time <= projectorSequence.stayOn){
			this.blinkTime += frameTime;
			
			if(this.blinkTime >= projectorSequence.blinks[this.curblink]){
					var dr = projectorSequence.targetColor.r/projectorSequence.blinkTime;
					var dg = projectorSequence.targetColor.g/projectorSequence.blinkTime;
					var db = projectorSequence.targetColor.b/projectorSequence.blinkTime;
				var colPower = (projectorSequence.blinks[this.curblink]+400)-this.blinkTime;
				if(colPower < 0)
					colPower = 0;
				this.blinkColor.setRGB(dr*colPower/10,dg*colPower/10,db*colPower/10);
					/*
				if(!this.waitBlink)
				{
					//var dr = projectorSequence.targetColor.r/projectorSequence.blinkTime;
					//var dg = projectorSequence.targetColor.g/projectorSequence.blinkTime;
					//var db = projectorSequence.targetColor.b/projectorSequence.blinkTime;
					
					this.blinkColor.setRGB(this.blinkColor.r+dr*td,this.blinkColor.g+dg*td,this.blinkColor.b+db*td);
					var allColorsPeaked = true;
					if(this.blinkColor.r < projectorSequence.targetColor.r){
						allColorsPeaked = false;
					}
					if(this.blinkColor.g < projectorSequence.targetColor.g){
						allColorsPeaked = false;
					}
					if(this.blinkColor.b < projectorSequence.targetColor.b){
						allColorsPeaked = false;
					}
					if(allColorsPeaked){
						this.peak = true;
					}
				}
				if(this.peak ){
					
					this.waitBlink = true;
					
					var nr = Math.max(this.blinkColor.r-2*dr*td,0);
					var ng = Math.max(this.blinkColor.g-2*dg*td,0);
					var nb = Math.max(this.blinkColor.b-2*db*td,0);
					this.blinkColor.setRGB( nr,ng ,nb );
					
					if(this.blinkColor.r <= 0 && this.blinkColor.g <= 0 && this.blinkColor.b <= 0)
					{
						this.peak = false;
					//	this.blinkTime = 0;
					//	this.curblink++;
					//	projectorSequence.blinkInterval-=150;
					//	this.blinkColor.setRGB(0,0,0);
					}
					//if(time + projectorSequence.blinkInterval + 200 >= projectorSequence.stayOn){
					//		this.blinkColor.setHex(projectorSequence.targetColor.getHex());
					//}
				}*/
				if(this.blinkTime >= projectorSequence.blinks[this.curblink+1])
				{
					console.log("move to next peak"+projectorSequence.blinks[this.curblink+1]);
					this.waitBlink = false;
						this.peak = false;
					//	this.blinkTime = 0;
						this.curblink++;
					//	projectorSequence.blinkInterval-=150;
					//	this.blinkColor.setRGB(0,0,0);
					//	if(time + projectorSequence.blinkInterval + 200 >= projectorSequence.stayOn){
					//		this.blinkColor.setHex(projectorSequence.targetColor.getHex());
					//}
				}
				
				this.innerSpaceBetweenMaterial.color.setRGB(this.blinkColor.r,this.blinkColor.g,this.blinkColor.b);
				for(var i = 0; i < this.monitors.length; ++i){
					for(var j = 0; j < this.monitors[i].length; ++j){
						//This should be changed into behaving in a more smart way..looks annoying this way.
						//this.monitors[i][j].material.color.setRGB((this.greenAmount/60),(this.greenAmount/60),(this.greenAmount/60));
					}
					
				}
			}
        }
        if(time >= projectorSequence.stayOn){
        	this.openAllMonitors();
            //this.openProjectorFaces([[11, 5], [11, 4], [10, 5], [10, 4]]);
        }
		
		// Dust particle movement
		this.dustParticleSystem.rotation.y += 0.0005*td;
		var pCount = this.dustParticleCount;
		while(pCount--) {
			// get the particle
			var particle = this.dustParticles.vertices[pCount];
			
			// Particle went too low, lets return it
			if(particle.y > 500) {
				particle.y = -500;
			}
			
			// update the velocity
		//	particle.velocity.y += Math.random() /20;
			
			// and the position
			particle.add(particle.velocity);
		}
		
		this.dustParticleSystem.geometry.__dirtyVertices = true;
		
		this.renderer.autoClear = false;
		this.renderer.setClearColor(0x000000,1);
		//this.renderer.clear();
		// Part rendering
		for (var i = 0; i < this.activeParts.length;++i) {
			var part = this.activeParts[i];
		 	    part.renderedThisFrame=false;   
		}

		for (var i = 0; i < this.activeParts.length;++i) {
			var part = this.activeParts[i];
		    part.isActive = true;
		    if(!part.renderedThisFrame){
			    part.renderedThisFrame=true;
				part.render(time);	
		    }
		    
		}

		for(var i = 0; i < this.configuration.parts.length;++i){
			var pConf = this.configuration.parts[i];
			pConf.finished = pConf.finished || false;
			if(pConf.finished){
				continue;
			}
			var part = this.parts[pConf.effect];
			pConf.started = pConf.started || false;
			if(pConf.started && (pConf.startTime+pConf.runningTime) <= time){
        		var index = this.activeParts.indexOf(part);
        		this.activeParts.splice(index,1);
        		console.log("stopping effect: " + part.partId + " at " + time);
        		pConf.started = false;
        		pConf.finished = true;
        	}
			if(!pConf.finished && !pConf.started && time >= pConf.startTime ){
				console.log("starting effect: " + part.partId + " at " + time);
				pConf.started = true;
            	var screens = [];
            	for (var j = 0; j < pConf.screens.length; ++j) {
                	var confScreen = pConf.screens[j];
                	var actualScreen = this.getMeshByPosition(confScreen[0], confScreen[1]);
                	actualScreen.xSegment = confScreen[0];
                	actualScreen.ySegment = confScreen[1];
                	screens.push(actualScreen);
            	}
				if(!part.lastUpdate)
					part.lastUpdate = time;
				if(pConf.zoomBall != undefined){
					if(pConf.zoomBall)
						this.zoomMonitorsTo(0.05);
					else
						this.zoomMonitorsTo(0.00);
				}
				
            	this.MasterRenderer.playEffect(part, screens, pConf);
            	this.activeParts.push(part);
        	}

		}
		
		this.MasterRenderer.render(time); // This renders images to stage monitors
	    //this.renderer.clear();
	    this.renderer.render(this.scene,this.camera,this.renderTarget,true); // This renders the actual camera image from stage to rendertarget

	   // this.vlight.updateMatrixWorld();
	 //   var lPos = THREE.Extras.Utils.projectOnScreen(this.vlight, this.camera);
	//	this.grPass.uniforms["fX"].value = lPos.x;
	//	this.grPass.uniforms["fY"].value = lPos.y;
			

		//this.oclcomposer.render();
		//this.oclcomposer.render();
		this.composer.render();	 // Composer finally adds postprocessing effects and renders the final image to screen.
		
		requestAnimationFrame( createjs.proxy(this.mainLoop, this) );
	//	$("#timestamp").html(Math.round(time));
		
		//if(time > 10000 && this.zoom < 0.049 && !this.foo)
		//{
		//	this.foo = true;
		//	this.zoomMonitorsTo(this.zoom+0.049);
		//}
		//if(time > 15000 && this.zoom > 0)
		//{
		//	this.zoomMonitorsTo(0);
		///}
		
		
		//this.setEnvMapping();
};

	//Expose the class
	wideload.Main = Main;
}())

setTimeout(function(){
//var stats = new Stats();
//stats.setMode(0); // 0: fps, 1: ms

// Align top-left
//stats.domElement.style.position = 'absolute';
//stats.domElement.style.left = '0px';
//stats.domElement.style.top = '0px';

//document.body.appendChild( stats.domElement );

//setInterval( function () {

   // stats.begin();

    // your code goes here

 //   stats.end();

//}, 1000 / 60 );
},100);