var container, camera, renderer, effectcomposer, container, controls, stats, light, music, audigain;
var launchpad;

var loadingScreenActive = true;
	
var wWidth = 0;
var wHeight = 0;
var wBufferWidth = 0;
var wBufferHeight = 0;
var music_started = 0;
var audio_delay = 0;

var final_pass_shader;

var globe, globe_glow;

var buffer1, buffer2, buffer3, buffer4; // full screen buffers

var texture_dir = 'gfx/';
var tex = {};

var syncDevice = new JSRocket.SyncDevice(),

	//Beats per minute of your demo tune
	BPM = 108,

	//The resolution between two beats, four is usually fine,- eight adds a bit more finer control
	ROWS_PER_BEAT = 8,

	//we calculate this now, so we can translate between rows and seconds later on
	ROW_RATE = BPM / 60 * ROWS_PER_BEAT,

	//the current row we're on
	row,

	// Are we live?!
	demoMode = true;


tex.rock_small_tile = load_texture(texture_dir+'tiles/rock_small.png','Rocks small');
tex.wood_tile = load_texture(texture_dir+'tiles/wood_vertical.jpg','Low resolution perlin noise');
tex.perlintile_low = load_texture(texture_dir+'tiles/perlintile_low.jpg','Low resolution perlin noise');
tex.brick_tile = load_texture(texture_dir+'tiles/small_brick_tile.jpg','Low resolution perlin noise');
tex.rooftiles = load_texture(texture_dir+'tiles/rooftiles.jpg','Low resolution perlin noise');

var rocketTracks = {
	"scene": 0,
	"percent": 0,
	"fadeToBlack": 0,
	"var1": 0,
	"var2": 0,
	"var3": 0,
	"var4": 0,
	"var5": 0,
	"var6": 0,
	"var7": 0,
	"lens": 0,
	"camx": 0,
	"camy": 0,
	"camz": 0,
	"cam2x": 0,
	"cam2y": 0,
	"cam2z": 0,
	"rgbshift": 0,
	"vignette": 0,
	"scanlines": 0,
	"grain": 0
};

function getVal(track) {
	if(rocketTracks[track] === 0) {
		return 0;
	}
	var v = rocketTracks[track].getValue(row);

	if(isNaN(v)) {
		return 0;
	}
	return v;
}

var _audio = new Audio();
var _context = new AudioContext();
var _analyzer = _context.createAnalyser();
_analyzer.smoothingTimeConstant = 0.7;
_analyzer.fftSize = 2048;

$(document).ready(function(){
	var source = _context.createMediaElementSource(_audio);
	source.connect(_analyzer);
	_analyzer.connect(_context.destination);
});


var shaders = function(){};

var geos = function(){}; // geometries

var meshes = function(){};
var materials = function(){};
var lights = function(){};
var scenes = scenes || [];


//var scene_composer = new scene_composer();
var launchpad = new _launchpad();

var clock = new THREE.Clock();
var dt = 0.0; // demo-time. clock that starts after loading.

noise.seed(0.1337); // MÅ være konstant. ikke rør.

function demo_init()
{
	//create_loading_console();

	$('div.table').addClass('table_show');
	
	
	SHADER_LOADER.load(
		function (data)
		{
			loading_console_write("Shady shaders are shady");
			
			loading_console_write(" * Glowing material shader");
			shaders.vertexShader_microscope = data.microscope.vertex;
			shaders.fragmentShader_microscope = data.microscope.fragment;
			
			loading_console_write(" * Final pass shader");
			shaders.fragmentShader_final_pass = data.final_pass.fragment;
			shaders.fragmentShader_overlay = data.overlay.fragment;

			//console.log("loaded shaders");

			demo_init3();
		}
	);
}

// The shader loader is non-blocking, so we split the
// loading function in two to get our code executed in the right order.
function demo_init3()
{
	loading_console_write("Squeezing some lemons...");
	console.log("demo init stage 2")
	//scene = new THREE.Scene();

	$(document).keypress(function(){
		if (event.keyCode == 'm'.charCodeAt())
		{
			_audio.muted = !_audio.muted;
		}
	});

	//console.log(shaders.vertexShader_glow);

	// ===============================================
	// Set up the WebGL renderer

	var SCREEN_WIDTH = window.innerWidth
	var SCREEN_HEIGHT = window.innerHeight;
	var VIEW_ANGLE = 45;
	var ASPECT = SCREEN_WIDTH/SCREEN_HEIGHT;
	var ASPECT = 16/9;
	var NEAR = 0.1;
	var FAR = 20000;

	/*renderer = new THREE.WebGLRenderer(
		{
			antialias: true,
			autoClearColor: true,
			alpha: true,
			premultipliedAlpha: false
		}
	);
	renderer.setClearColor(0x000000, 0);*/

	renderer = new THREE.WebGLRenderer();
	renderer.setSize( wWidth, wHeight );

	

	// ===============================================
	// Automatically set the viewport to 16/9 and add borders if needed.
	resize_window();
	window.onresize = function(){
		resize_window();
	};
	//THREEx.FullScreen.bindKey({ charCode : 'f'.charCodeAt(0) });

	container = document.getElementById("container");
	container.appendChild(renderer.domElement);


	// ===============================================
	// Add FPS stats

	if(!demoMode) {
		stats = new Stats();
		stats.domElement.style.position = 'absolute';
		stats.domElement.style.bottom = '0px';
		stats.domElement.style.zIndex = 100;
		container.appendChild( stats.domElement );
	} else {
		$("#demotime").hide();
	}


	// ===============================================
	// Load the final pass post-processing shader
	load_final_pass_shader();

	effectcomposer = new THREE.EffectComposer(renderer);

	// ===============================================
	// Initialize full screen texture buffers
	buffer1 = new THREE.WebGLRenderTarget( wBufferWidth, wBufferHeight, {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.NearestFilter,
		format: THREE.RGBAFormat
	});

	buffer2 = new THREE.WebGLRenderTarget( wBufferWidth, wBufferHeight, {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.NearestFilter,
		format: THREE.RGBAFormat
	});

	buffer3 = new THREE.WebGLRenderTarget( wBufferWidth, wBufferHeight, {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.NearestFilter,
		format: THREE.RGBAFormat
	});

	buffer4 = new THREE.WebGLRenderTarget( wBufferWidth, wBufferHeight, {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.NearestFilter,
		format: THREE.RGBAFormat
	});

	buffer5 = new THREE.WebGLRenderTarget( wBufferWidth, wBufferHeight, {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.NearestFilter,
		format: THREE.RGBAFormat
	});

	buffer6 = new THREE.WebGLRenderTarget( wBufferWidth, wBufferHeight, {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.NearestFilter,
		format: THREE.RGBAFormat
	});

	// ===============================================
	// Iterate through the loaded scene files and run load() on each of them to init their variables
	var sceneId = 0;
	for (var key in scenes) {
		if (scenes.hasOwnProperty(key)) {
			//console.log("Initializing variables for scene: " + key);
			scenes[key].load();

			console.log("Assigning scene id [" + sceneId + "] to scene [" + key + "]");
			launchpad.add(scenes[key], scenes[key].render, sceneId);

			sceneId++;
		}
	}

	// Prepare to load megatextures into GPU memory
	prepare_texture_preloader();

	

	// Wait for loading to complete, and start renderloop.
	preload_texture();
}

function preload_scenes() {
	row = 0;
	demo_time = row / ROW_RATE;
	final_pass.uniforms['fadeToBlack'].value = 1.0;

	for(var i = 0; i < launchpad.size(); i++) {
		loading_console_write("Preloading scene " + launchpad.getScene(i).id + "...");
		launchpad.launch(i);
	}

	loading_console_write('All done!');
	destroy_loading_console();

	prepareSync();
}

// Sync Step 1
function prepareSync() {
	loading_console_write("Making some lemonade while loading timings from rocket...");

	if (demoMode) {
		syncDevice.setConfig({'rocketXML':'demo.rocket'});
		syncDevice.init("demo");
	} else {
		syncDevice.init();
	}

	syncDevice.on('ready', onSyncReady);
	syncDevice.on('update', onSyncUpdate);
	syncDevice.on('play', onPlay);
	syncDevice.on('pause', onPause);
}

// Sync Step 2, called when "ready" event of syncDevice runs
function onSyncReady() {
	for (var key in rocketTracks) {
		if (rocketTracks.hasOwnProperty(key)) {
			rocketTracks[key] = syncDevice.getTrack(key);
		}
	}

	prepareAudio();
}

// Sync step 3
function prepareAudio()
{
	loading_console_write("Loading music");

	_audio.src = 'sfx/TG2015_Demo_mixdown.ogg';
	_audio.addEventListener('canplay', onAudioReady);
	_audio.preload = true;
	_audio.muted = !demoMode;
	_audio.load();
}

// Sync step 4, called by "canplay" event of _audio
function onAudioReady()
{
	if(demoMode) {
		_audio.play();
	} else {
		_audio.pause();
		_audio.currentTime = (row / ROW_RATE);
	}
	render();
}

// Called when you move around in rocket
function onSyncUpdate(newRow){

	// newRow is empty if you are just changing stuff in rocket
	if (!isNaN(newRow)) {
		row = newRow;
	}

	//update your view
	render();
}

// Called by rocket when you press play. Seeks the audio and starts playing it
function onPlay() {
	_audio.currentTime = (row / ROW_RATE);
    _audio.play();
	//console.log("[onPlay] time in seconds", row / ROW_RATE);
    render();
}

// Called by rocket when you press pause. Stores current time from audio and cancel render requests.
function onPause(){
	row = _audio.currentTime * ROW_RATE;
	window.cancelAnimationFrame(render, document);
	_audio.pause();
}

function render()
{
	// If the audio is playing we update the current row location based on the audio time
	if(_audio.paused === false) {
		//otherwise we may jump into a point in the audio where there's
		//no timeframe, resulting in Rocket setting row 2 and we report
		//row 1 back - thus Rocket spasming out
		row = _audio.currentTime * ROW_RATE;

		// this informs Rocket where we are
		syncDevice.update(row);
	}

	// Calculates demotime based on the row selected in rocket
	demo_time = row / ROW_RATE;

	if (demo_time > 10 && loadingScreenActive == true)
	{
		loadingScreenActive = false;
		$('div.table').removeClass('table_show');
		setTimeout(function(){
			$('div.introcontainer').addClass('introcontainer_hide');
		},10000);
		//$('div.introcontainer').addClass('introcontainer_hide');
	}

	document.getElementById('demotime').innerHTML = demo_time.toFixed(3);

	final_pass.uniforms['demoTime'].value = demo_time;
	final_pass.uniforms['fadeToBlack'].value = getVal("fadeToBlack");
	final_pass.uniforms['lens'].value = 0;
	final_pass.uniforms['rgbShift'].value = getVal("rgbshift")/1000.0;
	final_pass.uniforms['vignette'].value = getVal("vignette")/100.0;
	final_pass.uniforms['scanlines'].value = getVal("scanlines")/100.0;

	final_pass.uniforms['grain'].value = getVal("grain");

	// FPS counter
	if(!demoMode) {
		stats.update();
	}

	// Requests the next frame after 1/60 sec from browser if we are playing or in demo mode
	if((demoMode === true)  || (_audio.paused === false))
		window.requestAnimationFrame(render, document);
	else
		window.cancelAnimationFrame(render, document);

	// Gets the launch pad to launch the appropriate render function
	if(rocketTracks.scene !== 0) {
		launchpad.launch(rocketTracks.scene.getValue(row));	
	}
	
}

function renderwait()
{
	console.log("Renderwait...");
	if (loadable_elements>loaded_elements)
	{
		setTimeout(function(){ renderwait() },100);
	} else
	{
		destroy_loading_console();
		render();
	}
}

function resize_window()
{
	// Window is too high
	if (window.innerWidth/window.innerHeight <= 16/9)
	{
		var width = window.innerWidth;
		var height = Math.floor(width/16*9);
		var top = Math.floor((window.innerHeight-height)/2);
		var left = 0;

	// Window is too wide
	} else
	{
		var height = window.innerHeight;
		var width = Math.floor(height/9*16);
		var top = 0;
		var left = Math.floor((window.innerWidth-width)/2);
	}

	document.getElementById('container').style.top = top+'px';
	document.getElementById('container').style.left = left+'px';
	document.getElementById('container').style.width = width+'px';
	document.getElementById('container').style.height = height+'px';

	renderer.setSize(width, height);

	wWidth = width;
	wHeight = height;
	/*
	if (wWidth > 1920)
	{
		wBufferWidth = 1920;
		console.log("using max buffer width of 1920");
	} else
	{
		wBufferWidth = wWidth;
	}

	if (wHeight > 1080)
	{
		wBufferHeight = 1080;
		console.log("using max buffer height of 1080");
	} else
	{
		wBufferHeight = wHeight;
	}
	*/
	wBufferWidth = wWidth;
	wBufferHeight = wHeight;
}


function morph (x1, x2, factor)
{
	if (x1<x2)
	{
		return (x1+(x2-x1)*factor);
	} else
	{
		return (x1-(x1-x2)*factor);
	}
}

function smoothstep (min, max, value) {
  var x = Math.max(0, Math.min(1, (value-min)/(max-min)));
  return x*x*(3 - 2*x);
};


function deg2rad(Value) {
    /** Converts numeric degrees to radians */
    return Value * Math.PI / 180;
}

function rad2deg (radians)
{
	return radians * (180/Math.PI);
}

// Take a value from 0 to 1, and ease it with an output of 0 to 1
function easeOne(easing, n)
{
	/*
		jQuery.easing.method(null, current_time, start_value, end_value, total_time)
	*/
	return jQuery.easing[easing](null, n, 0, 1, 1);
}


// Inside a large number x, lets say from 0 to 100
// transition i from 0 to 1 when x is inside the range of y1 and y2.
// If y1 is 40 and y2 is 60, i would be 0 when x is >0 and <40
// transition from 0 to 1 when x>=40 and >=60, then 1 when x>60.
// fuckit - x is from 0-1...
function segmentTransition (y1, y2, x)
{
	if (y2 < y1)
	{
		var yy = y1;
		y1 = y2;
		y2 = yy;
	}

	if (x<y1)
	{
		return 0;
	}
	if (x>y2)
	{
		return 1;
	}

	if (x>=y1 && x<=y2)
	{
		var diff = y2-y1;
		return (x-y1)/diff;
	}
}

function flipNormals( geom ) {
	for ( var i = 0; i < geom.faces.length; i++ ) {
    	var face = geom.faces[i];
    	var temp = face.a;
    	face.a = face.c;
    	face.c = temp;
	}

	geom.computeFaceNormals();
	geom.computeVertexNormals();
}