function makeFlow(id, items, selected)
{
	var flow = new Object();
	flow.ID = id;
	flow.items = items;
	flow.selected = selected;
	flow.isFlow = true;
	flow.isContainer = false;
	flow.isChar = false;
	return flow;
}

function makeCharacter(id, value, selected)
{
	var c = new Object();
	c.ID = id;
	c.value = value;
	c.selected = selected;
	c.isFlow = false;
	c.isContainer = false;
	c.isChar = true;
	return c;
}



function makeContainer(id, type, flows, selected)
{
	var c = new Object();
	c.ID = id;
	c.type = type;
	c.flows = flows;
	c.selected = selected;
	c.isFlow = false;
	c.isContainer = true;
	c.isChar = false;
	return c;
}



var last_ID = -1;
var tree = makeFlow(++last_ID, [], 1);
var cursor_over = 0;
var click_id = 0;
var highest_click_id = 0;


function getFontSize(size)
{
	if (size == -4) return 36;
	if (size == -3) return 32;
	if (size == -2) return 28;
	if (size == -1) return 24;
	if (size == 0) return 20;
	if (size == 1) return 16;
	if (size == 2) return 12;
	if (size == 3) return 9;
	return 7;
}


function setCursor(id, click)
{
	if (click > click_id)
	{
		setCursorForSubtree(tree, id);
		++click_id;
	}
	
	document.getElementById("renderRoot").innerHTML = renderTree(tree, 0);
	debug();
}

function setCursorForSubtree(flowObject, id)
{
	flowObject.selected = (flowObject.ID == id);
	
	for (var i = 0; i < flowObject.items.length; ++i)
	{
		var item = flowObject.items[i];
		
		item.selected = (item.ID == id);
		
		if (!item.isChar)
		{
			for (var j = 0; j < item.flows.length; ++j)
			{
				setCursorForSubtree(item.flows[j], id);
			}
		}
	}
}

var blink_mode = true;

function blink_the_cursor()
{
	blink_mode = !blink_mode;
	
	var cursor = document.getElementById("cursor");
	
	if (cursor)
	{
		cursor.style.color = (blink_mode ? "white" : "black");
		//window.alert("hi");
	}
	
	window.setTimeout("blink_the_cursor()", 500);
}


function renderTree(flow, nestLevel)
{
	var output = '<div class="flow'+(flow.items.length == 0 ? 'Empty' : '')+'" onclick="setCursor(' + flow.ID + ','+(click_id+1)+')" style="text-align:center; '+(flow.items.length == 0 ? ' border:1px solid #888;' : '')+'">';
	output += '<table cellspacing="0" cellpadding="0" align="center"><tr>';
	
	if (flow.selected)
	{
		output += '<td><div id="cursors">|</div></td>';
	}
	
	for (var i = 0; i < flow.items.length; ++i)
	{
		var item = flow.items[i];
		
		output += '<td>';
		output += '<div'+(item.isContainer ? ' class="flow"' : '')+'>';
		if (item.isChar)
		{
			output += '<span style="font-size:'+getFontSize(nestLevel)+'px;" onclick="setCursor(' + item.ID + ','+(click_id+1)+')">' + item.value + '</span>';
		}
		else
		{
			var type = item.type;
			switch (type)
			{
				case "fraction":
					output += renderFraction(item, nestLevel);
					break;
				case "limit":
					output += renderLimit(item, nestLevel);
					break;
				case "sigma":
					output += renderSigma(item, nestLevel);
					break;
				case "super":
					output += renderSuper(item, nestLevel);
					break;
				case "sub":
					output += renderSub(item, nestLevel);
					break;
				default:
					output += "@@ERROR@@";
					break;
			}
			
		}
		output += '</div>';
		output += '</td>';
		
		if (item.selected)
		{
			output += '<td><div id="cursor">|</div></td>';
		}
	}
	
	if (i == 0) output += "<td>&nbsp;</td>";
	output += '</tr></table></div>';
	return output;
}

function renderFraction(fraction, nestlevel)
{
	var output = "";
	
	output += '<table onclick="setCursor(' + fraction.ID + ','+(click_id+1)+')">';
	output += '<tr><td>&nbsp;</td><td>' + renderTree(fraction.flows[0], nestlevel) + '</td><td>&nbsp;</td></tr>';
	output += '<tr><td colspan="3" height="1" bgcolor="#000"> </td></tr>';
	output += '<tr><td>&nbsp;</td><td>' + renderTree(fraction.flows[1], nestlevel) + '</td><td>&nbsp;</td></tr>';
	output += "</table>";
	
	return output;
}

function renderLimit(limit, nestlevel)
{
	var output = "";
	
	output += '<table onclick="setCursor(' + limit.ID + ','+(click_id+1)+')">';
	output += '<tr><td colspan="3" valign="bottom" align="center">lim</td><td rowspan="2">' + renderTree(limit.flows[0], nestlevel) + '</td></tr>';
	output += '<tr><td>' + renderTree(limit.flows[1], nestlevel + 2) + '</td>';
	output += '<td style="font-size:'+getFontSize(nestlevel + 2)+'px;">&rarr;</td>';
	output += '<td>' + renderTree(limit.flows[2], nestlevel + 2) + '</td>';
	output += '</tr>';
	output += '</table>';
	
	return output;
}

function renderSigma(sigma, nestlevel)
{
	var output = "";
	
	output += '<table cellpadding="0" cellspacing="0" onclick="setCursor(' + sigma.ID + ','+(click_id+1)+')">';
	output += '<tr><td valign="bottom" align="center">' + renderTree(sigma.flows[1], nestlevel + 2) + '</td>';
	output += '<td rowspan="3">' + renderTree(sigma.flows[2], nestlevel) + '</td></tr>';
	output += '<tr><td align="center" style="font-size:' +getFontSize(nestlevel - 4)+ '">&Sigma;</td></tr>';
	output += '<tr><td valign="top" align="center">' + renderTree(sigma.flows[0], nestlevel + 2) + '</td></tr>';
	output += '</tr>';
	output += '</table>';
	
	return output;
}

function renderSuper(supers, nestlevel)
{
	var output = "";
	
	output += '<div onclick="setCursor(' + supers.ID + ','+(click_id+1)+')">';
	output += renderTree(supers.flows[1], nestlevel + 2) + "<br />";
	output += '</div>';
	
	return output;
}

function renderSub(subs, nestlevel)
{
	var output = "";
	
	output += '<div onclick="setCursor(' + subs.ID + ','+(click_id+1)+')">';
	output += "<br />" + renderTree(subs.flows[1], nestlevel + 2);
	output += '</div>';
	
	return output;
}

function insert(type, data)
{
	switch (type)
	{
		case "character":
			insertItem(tree, makeCharacter(++last_ID, data, true));
			break;
		case "fraction":
			insertItem(tree, makeContainer(++last_ID, "fraction", [
				makeFlow(++last_ID, [], true),
				makeFlow(++last_ID, [], false)
			], false));
			break;
		case "limit":
			insertItem(tree, makeContainer(++last_ID, "limit", [
				makeFlow(++last_ID, [], true),
				makeFlow(++last_ID, [], false),
				makeFlow(++last_ID, [], false)
			], false));
			break;
		case "sigma":
			insertItem(tree, makeContainer(++last_ID, "sigma", [
				makeFlow(++last_ID, [], true),
				makeFlow(++last_ID, [], false),
				makeFlow(++last_ID, [], false)
			], false));
			break;
		case "super":
			insertItem(tree, makeContainer(++last_ID, "super", [
				makeFlow(++last_ID, [], false),
				makeFlow(++last_ID, [], true)
			], false));
			break;
		case "sub":
			insertItem(tree, makeContainer(++last_ID, "sub", [
				makeFlow(++last_ID, [], false),
				makeFlow(++last_ID, [], true)
			], false));
			break;
		case "infinity":
			insertItem(tree, makeCharacter(++last_ID, "&infin;", true));
			break;
		default:
			window.alert("ERROR!");
			break;
	}
	
	document.getElementById("renderRoot").innerHTML = renderTree(tree, 0);
	debug();
}

function insertItem(flowObject, builtItem)
{
	var inserted = false;
	
	var newFlowItems = [];
	
	if (flowObject.selected)
	{
		newFlowItems[0] = builtItem;
		flowObject.selected = false;
		inserted = true;
	}
	
	for (var i = 0; i < flowObject.items.length; ++i)
	{
		var item = flowObject.items[i];
		
		newFlowItems[newFlowItems.length] = item;
		
		if (!inserted)
		{
			if (item.selected)
			{
				item.selected = false;
				newFlowItems[newFlowItems.length] = builtItem;
				inserted = true;
			}
			
			if (!inserted && item.isContainer)
			{
				for (var j = 0; j < item.flows.length; ++j)
				{
					inserted = insertItem(item.flows[j], builtItem);
					if (inserted) break;
				}
			}
		}
	}
	
	flowObject.items = newFlowItems;
	return inserted;
}



function keyCodeToCharacter(char_id)
{
	if (char_id >= 48 && char_id <= 57)
	{
		return "" + (char_id - 48);
	}
	if (
		(char_id >= 65 && char_id <= 90) ||
		(char_id >= 97 && char_id <= 122) ||
		char_id == 43 || char_id == 45 || char_id == 61 ||
		char_id == 40 || char_id == 41 || char_id == 46 ||
		char_id == 33)
	{
		return String.fromCharCode(char_id);
	}
	
	if (char_id == 42)
	{
		return "&times;"
	}
	if (char_id == 47)
	{
		return "&divide;";
	}
	return '[' + char_id + ']';
}

function backspace(flow)
{
	var newFlowItems = [];
	var parentShouldBeSelected = false;
	
	for (var i = 0; i < flow.items.length; ++i)
	{
		var item = flow.items[i];
		
		if (item.selected)
		{
			if (i == 0)
			{
				parentShouldBeSelected = true;
			}
			else
			{
				newFlowItems[newFlowItems.length - 1].selected = true;
			}
		}
		else
		{
			newFlowItems[newFlowItems.length] = item;
			
			if (item.isContainer)
			{
				for (var j = 0; j < item.flows.length; ++j)
				{
					
					if (backspace(item.flows[j]))
					{
						item.flows[j].selected = true;
					}
				}
			}
		}
	}
	
	flow.items = newFlowItems;
	
	return parentShouldBeSelected;
}

function keyIsPressed(e)
{
	var keyCode = e.which ? e.which : event.keyCode;
	
	if (keyCode == 8)
	{
		//backspace
		backspace(tree);
		refresh();
	}
	else
	{
		//something else
		insert("character", keyCodeToCharacter(keyCode));
	}
}

function refresh()
{
	document.getElementById("renderRoot").innerHTML = renderTree(tree, 0);
}

function initialize()
{
	document.onkeypress = keyIsPressed
	refresh();
	blink_the_cursor();
}

function debug()
{
	//document.getElementById("debug").innerHTML = dh(tree);
}

function dh(flow)
{
	var output = '<div style="border:1px solid #888; '+(flow.selected ? 'background-color:#ff0;' : '')+'">Flow element<br />';
	output += 'ID: ' + flow.ID + '<br />';
	output += "Items ("+flow.items.length+"):<ul>";
	
	for (var i = 0; i < flow.items.length; ++i)
	{
		var item = flow.items[i];
		output += '<li'+(item.selected ? ' style="background-color:#ff0;"' : '')+'>';
		if (item.isChar)
		{
			output += item.value;
		}
		else
		{
			output += item.type + "<br />";
			output += "ID: " + item.ID + "<ul>";
			for (var j = 0; j < item.flows.length; ++j)
			{
				output += dh(item.flows[j]);
			}
			output += "</ul>";
			
		}
		output += "</li>";
	}
	output += "</ul>";
	output += "</div>";
	return output;
}
