/*
Visual glBlendFunc Tool
Created by Anders Riggelsen.
Copyright © Anders Riggelsen 2010
This code may not be used elsewhere than on www.andersriggelsen.dk without my permission. (pretty please?) :)
*/

//Global color values
var Rs, Gs, Bs, As;
var Rd, Gd, Bd, Ad;
var Rb, Gb, Bb, Ab;
//
var selectedFuncA, selectedFuncB, eqTxt;
var sM, dM;
//Misc.
var explanation, codeview, imagesources, colorpickerbox, imagePresets, displayMode, blendequation, tabHeaders, tabContents;
//Lists
var source, destination;
//Input fields
var backgroundURL, foregroundURL;
//Image objects
var frontImage, backImage, colorPickerImage, imageUrlsTab, blendingColorTab, imagePresetsTab;
//Canvas objects
var canvas, frontCanvas, backCanvas, colorpickerCanvas;
//Contexts
var context, frontContext, backContext, colorpickerContext;
//Image data
var imageData, backImageData, frontImageData, colorPickerImageData;
//Loaded flags
var frontLoaded = false, backLoaded = false, colorPickerLoaded = false;
//Color picker stuff
var hueTracker, alphaTracker, dragObject, colorX = 99, colorY = 0, isOnPicker = false;

//OpenGL blending parameters.
var modes = [
	[function gl_zero()						{return [0,0,0,0];},								'(0,0,0,0)'],
	[function gl_one()						{return [255,255,255,255];},						'(1,1,1,1)'],
	[function gl_src_color()				{return [Rs,Gs,Bs,As];},							'(sR,sG,sB,sA)'],
	[function gl_one_minus_src_color()		{return [255-Rs,255-Gs,255-Bs,255-As];},			'(1-(sR,sG,sB,sA))'],
	[function gl_dst_color()				{return [Rd,Gd,Bd,Ad];},							'(dR,dG,dB,dA)'],
	[function gl_one_minus_dst_color()		{return [255-Rd,255-Gd,255-Bd,255-Ad];},			'(1-(dR,dG,dB,dA))'],
	[function gl_src_alpha()				{return [As,As,As,As];},							'(sA,sA,sA,sA)'],
	[function gl_one_minus_src_alpha()		{return [255-As,255-As,255-As,255-As];},			'(1-(sA,sA,sA,sA))'],
	[function gl_dst_alpha()				{return [Ad,Ad,Ad,Ad];},							'(dA,dA,dA,dA)'],
	[function gl_one_minus_dst_alpha()		{return [255-Ad,255-Ad,255-Ad,255-Ad];},			'(1-(dA,dA,dA,dA))'],
	[function gl_src_alpha_saturate()		{i = Math.min(As, 255.0-Ad);return [i,i,i,255];},	'(i,i,i,1)'],
	[function gl_constant_color()			{return [Rb,Gb,Bb,Ab];},							'(bR,bG,bB,bA)'],
	[function gl_one_minus_constant_color()	{return [255-Rb,255-Gb,255-Bb,255-Ab];},			'(1-(bR,bG,bB,bA))'],
	[function gl_constant_alpha()			{return [Ab,Ab,Ab,Ab];},							'(bA,bA,bA,bA)'],
	[function gl_one_minus_constant_alpha()	{return [255-Ab,255-Ab,255-Ab,255-Ab];},			'(1-(bA,bA,bA,bA))']
];

var equations = [
	[function gl_func_add(){
			return [
				Math.min(255,(Rs*sM[0])/255.0+(Rd*dM[0])/255.0),
				Math.min(255,(Gs*sM[1])/255.0+(Gd*dM[1])/255.0),
				Math.min(255,(Bs*sM[2])/255.0+(Bd*dM[2])/255.0),
				Math.min(255,(As*sM[2])/255.0+(Ad*dM[2])/255.0)
			];
		},function (){return "foreground*" + selectedFuncA[1] + " + background*"+selectedFuncB[1];}
	],
	[function gl_func_add(){
			return [
				Math.max(0,(Rs*sM[0])/255.0-(Rd*dM[0])/255.0),
				Math.max(0,(Gs*sM[1])/255.0-(Gd*dM[1])/255.0),
				Math.max(0,(Bs*sM[2])/255.0-(Bd*dM[2])/255.0),
				Math.max(0,(As*sM[2])/255.0-(Ad*dM[2])/255.0)
			];
		},function (){return "foreground*" + selectedFuncA[1] + " - background*"+selectedFuncB[1];}
	],
	[function gl_func_add(){
			return [
				Math.max(0,(Rd*sM[0])/255.0-(Rs*dM[0])/255.0),
				Math.max(0,(Gd*sM[1])/255.0-(Gs*dM[1])/255.0),
				Math.max(0,(Bd*sM[2])/255.0-(Bs*dM[2])/255.0),
				Math.max(0,(Ad*sM[2])/255.0-(As*dM[2])/255.0)
			];
		},function (){return "background*" + selectedFuncB[1] + " - foreground*"+selectedFuncA[1];}
	]
]

function onLoad()
{
	canvas = document.getElementById('display');
	source = document.getElementById('source');
	destination = document.getElementById('destination');
	explanation = document.getElementById('explanation');
	hueTracker = document.getElementById('huetracker');
	alphaTracker = document.getElementById('alphatracker');
	colorpickerCanvas = document.getElementById('colorpicker');
	codeview = document.getElementById('codeview');
	
	imagePresetsTab = document.getElementById('imagePresetsTab');
	imageUrlsTab = document.getElementById('imageUrlsTab');
	blendingColorTab = document.getElementById('blendingColorTab');
	tabHeaders = [imagePresetsTab, imageUrlsTab, blendingColorTab];

	backgroundURL = document.getElementById('backgroundURL');
	foregroundURL = document.getElementById('foregroundURL');
	
	imagePresets = document.getElementById('imagePresets');
	imagesources = document.getElementById('imagesources');
	colorpickerbox = document.getElementById('colorpickerbox');
	tabContents = [imagePresets, imagesources, colorpickerbox];
	
	displayMode = document.getElementById('displayMode');
	blendequation = document.getElementById('blendequation');
	
	frontCanvas = document.createElement("canvas");
	backCanvas = document.createElement("canvas");
	frontCanvas.width = frontCanvas.height = backCanvas.width = backCanvas.height = 400;
	
	context = canvas.getContext('2d');
	frontContext = frontCanvas.getContext('2d');
	backContext = backCanvas.getContext('2d');
	colorpickerContext = colorpickerCanvas.getContext('2d');
	
	loadImages();
	
	imageData = context.getImageData(0,0,400,400);
	colorPickerImageData = colorpickerContext.getImageData(0,0,100,100);
	
	hueTracker.addEventListener('mousedown', mouseDown, false);
	alphaTracker.addEventListener('mousedown', mouseDown, false);
	colorpickerCanvas.addEventListener('mousedown', mouseDown, false);
	document.addEventListener('mousemove', mouseMove, false);
	document.addEventListener('mouseup', mouseUp, false);
	
	hueTracker.trackbar = document.getElementById('hueslider');
	alphaTracker.trackbar = document.getElementById('alphaslider');
	hueTracker.trackerValue = 0;
	alphaTracker.trackerValue = 200;
	
	updateColorPicker();
}

function loadImages()
{
	frontImage = new Image();
	frontImage.onload = function(){
		frontLoaded = true;
		frontContext.fillStyle = "rgba(0,0,0,0)";
		frontContext.clearRect (0,0,400,400);
		frontContext.drawImage(frontImage,0,0);
		frontImageData = frontContext.getImageData(0,0,400,400);
		if(backLoaded == true) updateDisplay();
	}
	frontImage.src = foregroundURL.value;
	
	backImage = new Image();
	backImage.onload = function(){
		backLoaded = true;
		backContext.fillStyle = "rgba(0,0,0,0)";
		backContext.clearRect (0,0,400,400);
		backContext.drawImage(backImage,0,0);
		backImageData = backContext.getImageData(0,0,400,400);
		if(frontLoaded == true) updateDisplay();
	}
	backImage.src = backgroundURL.value;
	
	colorPickerImage = new Image();
	colorPickerImage.onload = function(){
		colorPickerLoaded = true;
		updateColorPicker();
	}
	colorPickerImage.src = 'colorpicker.png';
}

function changePreMultiplyState(check)
{
	if(check.checked==false)
	{
		restoreImage(frontContext, frontImage);
		restoreImage(backContext, backImage);
		frontImageData = frontContext.getImageData(0,0,400,400);
		backImageData = backContext.getImageData(0,0,400,400);
	}
	else
	{
		premultiplyImage(frontContext, frontImageData);
		premultiplyImage(backContext, backImageData);
	}
	updateDisplay();
}

function restoreImage(context, image)
{
	context.fillStyle = "rgba(0,0,0,0)";
	context.clearRect (0,0,400,400);
	context.drawImage(image, 0, 0);
}

function premultiplyImage(context,imageData)
{
	for(y=0; y<imageData.height; ++y)
	{
		for(x=0; x<imageData.width; ++x)
		{
			pos = (y*imageData.width + x)*4;
			imageData.data[ pos ] = (imageData.data[ pos ]*imageData.data[pos+3])/255;
			imageData.data[pos+1] = (imageData.data[pos+1]*imageData.data[pos+3])/255;
			imageData.data[pos+2] = (imageData.data[pos+2]*imageData.data[pos+3])/255;
		}
	}
	context.putImageData(imageData, 0, 0);
}

function findPos(obj)
{
	var curleft = curtop = 0;
	if (obj.offsetParent)
		do{
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		}
		while (obj = obj.offsetParent);
	return [curleft, curtop];
}

function mouseDown(ev)
{
	if(typeof( this.trackerValue ) != "undefined")
	{
		//Trackbars
		dragObject = this;
		moveTracker(ev.clientX);
	}
	else
	{
		//Color picker
		isOnPicker = true;
		moveColorPicker(ev.clientX, ev.clientY);
	}
	ev.preventDefault();
}

function mouseMove(ev)
{
	if(dragObject != null)
		moveTracker(ev.clientX);
	else if(isOnPicker)
		moveColorPicker(ev.clientX, ev.clientY);
}

function moveTracker(mouseX)
{
	dragObject.trackerValue = Math.min(Math.max(mouseX - findPos(dragObject.trackbar)[0], 0),200);
	dragObject.style.left = "" + (dragObject.trackerValue-9) + "px";
	updateColorPicker();
	updateText();
}

function mouseUp()
{
	if(dragObject != null || isOnPicker == true)
		updateDisplay();
	dragObject = null; isOnPicker = false;
}

function moveColorPicker(mouseX,mouseY)
{
	pos = findPos(colorpickerCanvas);
	colorX = Math.min(Math.max(mouseX - pos[0], 0),99);
	colorY = Math.min(Math.max(mouseY - pos[1], 0),99)
	updateColorPicker();
	updateText();
}

function updateDisplay()
{
	if(!frontLoaded || !backLoaded)
		return;
	
	switch(displayMode.selectedIndex)
	{
		case 0: drawFinalRGB(); break;
		case 1: drawFinalRGBA(); break;
		case 2: drawFinalAlpha(); break;
		case 3: drawImageRGB(frontImageData); break;
		case 4: drawImageRGBA(frontImageData); break;
		case 5: drawImageAlpha(frontImageData); break;
		case 6: drawImageRGB(backImageData); break;
		case 7: drawImageRGBA(backImageData); break;
		case 8: drawImageAlpha(backImageData); break;
	}
}

function drawFinalRGB()
{
	selectedFuncA = modes[source.selectedIndex];
	selectedFuncB = modes[destination.selectedIndex];
	fA = selectedFuncA[0];
	fB = selectedFuncB[0];
	eq = equations[blendequation.selectedIndex][0];
	eqTxt = equations[blendequation.selectedIndex][1];
	
	updateText();
	
	for(y=0; y<imageData.height; ++y)
	{
		for(x=0; x<imageData.width; ++x)
		{
			pos = (y*imageData.width+x)*4;
			Rd = backImageData.data[pos]; Gd = backImageData.data[pos+1]; Bd = backImageData.data[pos+2]; Ad = backImageData.data[pos+3];
			Rs = frontImageData.data[pos]; Gs = frontImageData.data[pos+1]; Bs = frontImageData.data[pos+2]; As = frontImageData.data[pos+3];
			sM = fA(); dM = fB(); fcolor = eq();
			imageData.data[ pos ] = fcolor[0];
			imageData.data[pos+1] = fcolor[1];
			imageData.data[pos+2] = fcolor[2];
			imageData.data[pos+3] = 255;
		}
	}
	context.putImageData(imageData, 0, 0);
}

function drawFinalRGBA()
{
	selectedFuncA = modes[source.selectedIndex];
	selectedFuncB = modes[destination.selectedIndex];
	fA = selectedFuncA[0];
	fB = selectedFuncB[0];
	eq = equations[blendequation.selectedIndex][0];
	eqTxt = equations[blendequation.selectedIndex][1];
	
	updateText();
	
	for(y=0; y<imageData.height; ++y)
	{
		for(x=0; x<imageData.width; ++x)
		{
			pos = (y*imageData.width+x)*4;
			Rd = backImageData.data[pos]; Gd = backImageData.data[pos+1]; Bd = backImageData.data[pos+2]; Ad = backImageData.data[pos+3];
			Rs = frontImageData.data[pos]; Gs = frontImageData.data[pos+1]; Bs = frontImageData.data[pos+2]; As = frontImageData.data[pos+3];
			sM = fA(); dM = fB(); fcolor = eq();
			imageData.data[ pos ] = fcolor[0];
			imageData.data[pos+1] = fcolor[1];
			imageData.data[pos+2] = fcolor[2];
			imageData.data[pos+3] = fcolor[3];
		}
	}
	context.putImageData(imageData, 0, 0);
}

function drawFinalAlpha()
{
	selectedFuncA = modes[source.selectedIndex];
	selectedFuncB = modes[destination.selectedIndex];
	fA = selectedFuncA[0];
	fB = selectedFuncB[0];
	eq = equations[blendequation.selectedIndex][0];
	eqTxt = equations[blendequation.selectedIndex][1];
	
	updateText();
	
	for(y=0; y<imageData.height; ++y)
	{
		for(x=0; x<imageData.width; ++x)
		{
			pos = (y*imageData.width+x)*4;
			Rd = backImageData.data[pos]; Gd = backImageData.data[pos+1]; Bd = backImageData.data[pos+2]; Ad = backImageData.data[pos+3];
			Rs = frontImageData.data[pos]; Gs = frontImageData.data[pos+1]; Bs = frontImageData.data[pos+2]; As = frontImageData.data[pos+3];
			sM = fA(); dM = fB(); fcolor = eq();
			imageData.data[ pos ] = fcolor[3];
			imageData.data[pos+1] = fcolor[3];
			imageData.data[pos+2] = fcolor[3];
			imageData.data[pos+3] = 255;
		}
	}
	context.putImageData(imageData, 0, 0);
}



function drawImageRGB(imgData)
{
	for(y=0; y<imageData.height; ++y)
		for(x=0; x<imageData.width; ++x)
		{
			pos = (y*imageData.width+x)*4;
			imageData.data[ pos ] = imgData.data[pos];
			imageData.data[pos+1] = imgData.data[pos+1];
			imageData.data[pos+2] = imgData.data[pos+2];
			imageData.data[pos+3] = 255;
		}
	context.putImageData(imageData, 0, 0);
}

function drawImageRGBA(imgData)
{
	for(y=0; y<imageData.height; ++y)
		for(x=0; x<imageData.width; ++x)
		{
			pos = (y*imageData.width+x)*4;
			imageData.data[ pos ] = imgData.data[pos];
			imageData.data[pos+1] = imgData.data[pos+1];
			imageData.data[pos+2] = imgData.data[pos+2];
			imageData.data[pos+3] = imgData.data[pos+3];
		}
	context.putImageData(imageData, 0, 0);
}

function drawImageAlpha(imgData)
{
	for(y=0; y<imageData.height; ++y)
		for(x=0; x<imageData.width; ++x)
		{
			pos = (y*imageData.width+x)*4;
			alpha = imgData.data[pos+3];
			imageData.data[pos] = alpha;
			imageData.data[pos+1] = alpha;
			imageData.data[pos+2] = alpha;
			imageData.data[pos+3] = 255;
		}
	context.putImageData(imageData, 0, 0);
}

function updateColorPicker()
{
	for(y=0; y<colorPickerImageData.height; ++y)
	{
		for(x=0; x<colorPickerImageData.width; ++x)
		{
			pos = (y*colorPickerImageData.width + x)*4;
			color = hsv((hueTracker.trackerValue*359)/200,x/100.0,1-y/100.0);
			colorPickerImageData.data[ pos ] = color[0];
			colorPickerImageData.data[pos+1] = color[1];
			colorPickerImageData.data[pos+2] = color[2];
			colorPickerImageData.data[pos+3] = (alphaTracker.trackerValue*255)/200;
		}
	}
	colorpickerContext.putImageData(colorPickerImageData, 0, 0);
	if(colorPickerLoaded)
		colorpickerContext.drawImage(colorPickerImage,colorX-5,colorY-5);
	
	//Update blending color value
	pos = (colorY*colorPickerImageData.width + colorX)*4;
	Rb = colorPickerImageData.data[ pos ];
	Gb = colorPickerImageData.data[pos+1];
	Bb = colorPickerImageData.data[pos+2];
	Ab = colorPickerImageData.data[pos+3];
	
}

function hsv(h, s, v)
{
    var r, g, b;
    var i;
    var f, p, q, t;
     
    if (s == 0)
	{
        r = g = b = v;
        return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
    }
   
    h /= 60;
    i  = Math.floor(h);
    f = h - i;
    p = v *  (1 - s);
    q = v * (1 - s * f);
    t = v * (1 - s * (1 - f));
   
    switch( i ) {
        case 0:
            r = v;
            g = t;
            b = p;
            break;
        case 1:
            r = q;
            g = v;
            b = p;
            break;
        case 2:
            r = p;
            g = v;
            b = t;
            break;
        case 3:
            r = p;
            g = q;
            b = v;
            break;
        case 4:
            r = t;
            g = p;
            b = v;
            break;
        default:        // case 5:
            r = v;
            g = p;
            b = q;
            break;
    }
    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}

function updateText()
{
	tA = modes[source.selectedIndex][1];
	tB = modes[destination.selectedIndex][1];
	
	explanation.innerHTML = "result = " + eqTxt();
		
	modeA = source.options[source.selectedIndex].text;
	modeB = destination.options[destination.selectedIndex].text;
	blendcolor = (source.selectedIndex >= 11||destination.selectedIndex >= 11) ? "glBlendColor("+Rb+","+Gb+","+Bb+","+Ab+");<br />" : "";
	codeview.innerHTML = "glEnable(GL_BLEND);<br />"+blendcolor+"glBlendFunc(" + modeA + ", " + modeB + ");";
}

function showTab(id)
{
	for (var i=0;i<tabHeaders.length;++i)
		tabHeaders[i].className = "";

	for (var i=0;i<tabContents.length;++i)
	{
		tabContent = tabContents[i];
		tabContent.style.visibility = "hidden";
		tabContent.style.display = "none";
	}
		
	tabHeaders[id].className = "selectedTab";
	tabContents[id].style.visibility = "visible";
	tabContents[id].style.display = "block";

}

function setForeground(url)
{
	foregroundURL.value = url;
	if(url.substr(0,4) == 'http')		
		frontImage.src = "redirect.php?url=" + url;
	else
		frontImage.src = url;
}

function setBackground(url)
{
	backgroundURL.value = url;
	if(url.substr(0,4) == 'http')		
		backImage.src = "redirect.php?url=" + url;
	else
		backImage.src = url;
}
