var canvas;
var gl;
var rfrenderer = {
	shaderProgram:null,
	vertexPositionAttribute : null,
	vertexPositionAttribute : null,
	
	colorsampleruniformloc :null,
	idsampleruniformloc :null,
	selectediduniformloc:null,
	hoverediduniformloc:null,
	normalizedpixelsizeuniformloc:null,
	pulsefactoruniformloc:null,
	
	squareVerticesBuffer:null,
	colordatatexture:null,
	colordataimage:null,
	iddatatexture:null,
	iddataimage:null,
	
	pickerisready:false,
	
	selectedid:0,
	hoveredid:0
};

function getMouseLocHoverPicture(pEvent, pImageId)
{
	return [pEvent.clientX - $(pImageId).offset().left + $('body').scrollLeft(), pEvent.clientY - $(pImageId).offset().top + $('body').scrollTop()]
}

/**###########################################**/
/** Metadata helpers **/
/**###########################################**/
function getIdAt(pPos)
{
	if(!rfrenderer.pickerisready)
	{
		console.debug("Picker not ready");
		return 0;
	}
	else if( pPos[0] <= 0 || pPos[0] >= rfrenderer.iddataimage.width)
	{
		console.debug("Pick out of bound");
		return 0;
	}
	else if( pPos[1] <= 0 || pPos[1] >= rfrenderer.iddataimage.height)
	{
		console.debug("Pick out of bound");
		return 0;
	}
	
	var lPixelData = document.getElementById('pickercanvas').getContext('2d').getImageData(pPos[0], pPos[1], 1, 1).data;
	return lPixelData[0] | lPixelData[1]<<8 | lPixelData[2]<<16;
}

function getIdCardsOfGeomId(pGeomId)
{
	lRes = [];
	if(sBomData["geom"][pGeomId] !== undefined)
	{
		lIdCard = [];
		lNextIdToProcess = sBomData["geom"][pGeomId];
		while(lNextIdToProcess != 0)
		{
			lInstanceInfo = sBomData['ps'][lNextIdToProcess];
			
			lIdCard.unshift(sBomData.bom[lInstanceInfo.part]);
			if (lInstanceInfo.instancemetadata !== undefined)
			{
				Object.assign(lIdCard[0], lInstanceInfo.instancemetadata);
			}
			
			lNextIdToProcess = lInstanceInfo.parent;
		}
		lRes.push(lIdCard);
	}
	
	return lRes;
	
}

/**###########################################**/
/** IdCard helpers **/
/**###########################################**/
function addRowToTable(pTable,pRow,pClass)
{
	var lTr = document.createElement("tr");
	lTr.className = pClass;
	for(var i = 0 ; i < pRow.length ; ++i)
	{
		var lTd = document.createElement("td");
		lTd.appendChild(document.createTextNode(pRow[i]))
		lTd.className += (i % 2 == 0) ? " ceven" : " codd";
		lTr.appendChild(lTd);
	}
	pTable.appendChild(lTr);
}
function fillIdCard(pIdCards) 
{
	var lIdCardDiv = document.getElementById('idcarddiv');
	lIdCardDiv.innerHTML = '';
	
	for(var i = 0 ; i < pIdCards.length ; ++i)
	{
		var lIdCard = pIdCards[i];
		var lTable = document.createElement("table");
		
		for(var j = lIdCard.length - 1 ; j >= 0 ; --j)
		{
			addRowToTable(lTable,["Level",j],"levelseparator");
			var lKeys = Object.keys(lIdCard[j]);
			lKeys.sort(function (a, b){return a.toLowerCase().localeCompare(b.toLowerCase())} );
			for (var k = 0 ; k < lKeys.length ; ++k)
			{
				var lKey = lKeys[k];
				addRowToTable(lTable,[lKey,lIdCard[j][lKey]],"idcardentry " + ((k % 2 == 0) ? " reven" : " rodd"));
			}
		}
		
		lIdCardDiv.appendChild(lTable);
	}

}

/**###########################################**/
/** init callback **/
/**###########################################**/


function getShader(gl, id) 
{
	var shaderScript, theSource, currentChild, shader;

	shaderScript = document.getElementById(id);

	if (!shaderScript) {
		throw "Fail to retrieve shader " + id;
	}

	theSource = "";
	currentChild = shaderScript.firstChild;

	while(currentChild) {
		if (currentChild.nodeType == currentChild.TEXT_NODE) {
			theSource += currentChild.textContent;
		}
		currentChild = currentChild.nextSibling;
	}
	
	if (shaderScript.type == "x-shader/x-fragment") 
	{
		shader = gl.createShader(gl.FRAGMENT_SHADER);
	} 
	else if (shaderScript.type == "x-shader/x-vertex") 
	{
		shader = gl.createShader(gl.VERTEX_SHADER);
	}
	else 
	{
		throw "Unknown shader type " + shaderScript.type;
	}
	gl.shaderSource(shader, theSource);

	// Compile le programme shader
	gl.compileShader(shader);  

	// Vérifie si la compilation s'est bien déroulée
	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {  
		throw "Fail to compile shader " + id + " error was " + gl.getShaderInfoLog(shader);
	}

	return shader;
}

function initShaders() 
{
	var fragmentShader = getShader(gl, "shader-fs");
	var vertexShader = getShader(gl, "shader-vs");

	// Créer le programme shader

	rfrenderer.shaderProgram = gl.createProgram();
	gl.attachShader(rfrenderer.shaderProgram, vertexShader);
	gl.attachShader(rfrenderer.shaderProgram, fragmentShader);
	gl.linkProgram(rfrenderer.shaderProgram);

	// Faire une alerte si le chargement du shader échoue

	if (!gl.getProgramParameter(rfrenderer.shaderProgram, gl.LINK_STATUS)) 
	{
		throw "Fail to link shader";
	}

	gl.useProgram(rfrenderer.shaderProgram);

	rfrenderer.vertexPositionAttribute = gl.getAttribLocation(rfrenderer.shaderProgram, "uVertexPosition");
	gl.enableVertexAttribArray(rfrenderer.vertexPositionAttribute);
	
	rfrenderer.colorsampleruniformloc = gl.getUniformLocation(rfrenderer.shaderProgram, "uColorSampler");
	rfrenderer.idsampleruniformloc = gl.getUniformLocation(rfrenderer.shaderProgram, "uIdSampler");
	rfrenderer.selectediduniformloc = gl.getUniformLocation(rfrenderer.shaderProgram, "uSelectedId");
	rfrenderer.hoverediduniformloc = gl.getUniformLocation(rfrenderer.shaderProgram, "uHoveredId");
	rfrenderer.normalizedpixelsizeuniformloc = gl.getUniformLocation(rfrenderer.shaderProgram, "uNormalizedPixelSize");
	rfrenderer.pulsefactoruniformloc = gl.getUniformLocation(rfrenderer.shaderProgram, "uPulseFactor");
}

function initBuffers() 
{
	rfrenderer.squareVerticesBuffer = gl.createBuffer();
	gl.bindBuffer(gl.ARRAY_BUFFER, rfrenderer.squareVerticesBuffer);

	var vertices = [
	1.0,  1.0,  0.0,
	-1.0, 1.0,  0.0,
	1.0,  -1.0, 0.0,
	-1.0, -1.0, 0.0
	];

	gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}


function initTexture(pName) {
	var lTextureName = pName + "texture";
	var lImageName = pName + "image";
	
	rfrenderer[lTextureName] = gl.createTexture();
	rfrenderer[lImageName] = new Image();
	rfrenderer[lImageName].onload = function() { handleTextureLoaded(rfrenderer[lImageName], rfrenderer[lTextureName], pName); }
	rfrenderer[lImageName].src = "data:image/" + $('#' + pName + "type") + ";base64," + $('#' + pName).text();
}

function handleTextureLoaded(image, texture, name) {
	gl.bindTexture(gl.TEXTURE_2D, texture);
	gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
	gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
	//gl.generateMipmap(gl.TEXTURE_2D);
	gl.bindTexture(gl.TEXTURE_2D, null);
	
	if( name == 'iddata')
	{
		// init picker canvas
		document.getElementById("pickercanvas").getContext("2d").drawImage(rfrenderer.iddataimage, 0, 0, rfrenderer.iddataimage.width, rfrenderer.iddataimage.height);;
		rfrenderer.pickerisready=true;
		rfrenderer.drawScene();
	}
}

function refreshdrawonselection()
{
	if(rfrenderer.selectedid != 0)
	{
		rfrenderer.drawScene();
		rfrenderer.pulsefactor = Math.cos((new Date).getTime() / 800);
	}
}

function initWebGL(canvas) { 
	// Initialise la variable globale gl à null
	gl = null; 

	try 
	{
		rfrenderer.drawScene = function(){
			gl.clearColor(1.0, 0.0, 0.0, 1.0);
			gl.disable(gl.DEPTH_TEST);
			gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
			
			gl.useProgram(rfrenderer.shaderProgram);
			
			gl.activeTexture(gl.TEXTURE0);
			gl.bindTexture(gl.TEXTURE_2D, rfrenderer.colordatatexture);
			gl.uniform1i(rfrenderer.colorsampleruniformloc, 0);
			
			gl.activeTexture(gl.TEXTURE1);
			gl.bindTexture(gl.TEXTURE_2D, rfrenderer.iddatatexture);
			gl.uniform1i(rfrenderer.idsampleruniformloc, 1);
			
			gl.uniform3f(rfrenderer.selectediduniformloc, (rfrenderer.selectedid & 0xFF)/255., (rfrenderer.selectedid>>8 & 0xFF)/255., (rfrenderer.selectedid>>16 & 0xFF)/255. );
			gl.uniform3f(rfrenderer.hoverediduniformloc,(rfrenderer.hoveredid & 0xFF)/255., (rfrenderer.hoveredid>>8 & 0xFF)/255., (rfrenderer.hoveredid>>16 & 0xFF)/255. );
			gl.uniform2f(rfrenderer.normalizedpixelsizeuniformloc,1./rfrenderer.colordataimage.width, 1./rfrenderer.colordataimage.height);
			gl.uniform1f(rfrenderer.pulsefactoruniformloc,rfrenderer.pulsefactor);
			
			gl.bindBuffer(gl.ARRAY_BUFFER, rfrenderer.squareVerticesBuffer);
			gl.vertexAttribPointer(rfrenderer.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
			gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
		}
		
		// Essaye de récupérer le contexte standard. En cas d'échec, il teste l'appel experimental
		gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
		
		initShaders();
		initBuffers();
		initTexture("colordata");
		initTexture('iddata');

		setInterval(refreshdrawonselection,15)
	}
	catch(e)
	{
		console.debug(e.toString())
		gl = null;
		rfrenderer.drawScene = function(){};
	}

	// Si le contexte GL n'est pas récupéré, on l'indique à l'utilisateur.
	if (!gl) 
	{
		console.debug("Fail to init WebGL");
		throw false;
	}
	else
	{
		gl.clearColor(1.0, 0.0, 0.0, 1.0);
		gl.disable(gl.DEPTH_TEST);
		gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
	}
}

function onLoadBody()
{
	var canvas = document.getElementById("glcanvas");

	initWebGL(canvas);// Initialise le contexte WebGL
	
	rfrenderer.drawScene();
}

/**###########################################**/
/** Mouse event callback **/
/**###########################################**/

function onMouseClickPicture(pEvent)
{
	var lPos = getMouseLocHoverPicture(pEvent,'#glcanvas')
	rfrenderer.selectedid = getIdAt(lPos);
	fillIdCard(getIdCardsOfGeomId(rfrenderer.selectedid));
	rfrenderer.drawScene();
}

function onMouseHoverPicture(pEvent)
{
	var lPos = getMouseLocHoverPicture(pEvent,'#glcanvas')
	rfrenderer.hoveredid = getIdAt(lPos);
	
	var lDiv = document.getElementById("hoverinfodiv");
	lDiv.innerHTML = '';
	var lIdCards = getIdCardsOfGeomId(rfrenderer.hoveredid);
	var lVal = undefined;
	if(lIdCards !== undefined && lIdCards.length > 0 && lIdCards[0] !== undefined && lIdCards[0].length > 0)
	{
		var lIdCard = lIdCards[0];
		var lMd = lIdCard[lIdCard.length - 1];
		lVal = lMd["PartNumber"];
		if(lVal === undefined)
			lVal = lMd["Name"];
		if(lVal === undefined)
			lVal = lMd["_id"];
		if(lVal === undefined)
		{
			var lKeys = Object.keys(lMd);
			lKeys.sort(function (a, b){return a.toLowerCase().localeCompare(b.toLowerCase())} );
			lVal = lMd[lKeys[0]];
		}
	}
	if(lVal === undefined)
		lVal = "n/a";
	lDiv.appendChild(document.createTextNode("Hover : " + lVal));
	
	rfrenderer.drawScene();
}

function onMouseLeavePicture(pEvent)
{
	rfrenderer.hoveredid = 0;
	rfrenderer.drawScene();
}