//Semantic Linking (slink) system by Weston Ruter <westonmr@spu.edu> 2005-03-21

//var xmlrpc=null;
//try{
//    var xmlrpc = importModule("xmlrpc");
//}catch(e){
//    reportException(e);
//    throw "importing of xmlrpc module failed.";
//}
//
//var serverURL = "/corpus/slink/RVA-LSG/server.php";
//var xmlrpcService = new xmlrpc.ServiceProxy(serverURL, ['login', 'synchronizeDatabase', 'logout', 'loadSemanticLinks']); //
var errorNotice = document.createElement('div');
errorNotice.style.position = "absolute";
errorNotice.style.border = "solid 1px red";
errorNotice.style.padding = "5px";
errorNotice.style.left = '300px';
errorNotice.style.background = '#FFCCCC';
errorNotice.style.top = '5px';
errorNotice.style.width = '500px';
errorNotice.style.fontSize = '12px';

var xmlrpcService = {};
xmlrpcService.login = xmlrpcService.logout = function(){
	errorNotice.innerHTML = 'The server-component of prototype is no longer available.';
};
xmlrpcService.synchronizeDatabase = function(){
	errorNotice.innerHTML = 'The server-component of prototype is no longer available. No changes will be saved.';
};
xmlrpcService.loadSemanticLinks = function(){
	errorNotice.innerHTML = "The server component of prototype is no longer online. You may play around with semantic linking on the client, but you won't be able to save your changes back to the server.";
	return semanticLinks;
};

//var xmlrpcServer = "/corpus/slink/RVA-LSG/server.php";
var DELETE = 0;
var INSERT = 1;

var semanticLinks = {
	fr:{ //maps from Spanish ids to French ids
		1:[1,2,3,4],
		2:[5],
		3:[6],
		4:[6],
		5:[7],
		6:[8],
		7:[9],
		8:[10],
		9:[11],
		10:[12]//,
		//11:[13,14,15,16],
		//12:[13,14,15,16],
		//13:[13,14,15,16],
		//14:[13,14,15,16],
		//15:[17],
		//16:[18]
	},
	es:{ //maps from French ids to Spanish ids
		1:[1],
		2:[1],
		3:[1],
		4:[1],
		5:[2],
		6:[3,4],
		7:[5],
		8:[6],
		9:[7],
		10:[8],
		11:[9],
		12:[10]//,
		//13:[11,12,13,14],
		//14:[11,12,13,14],
		//15:[11,12,13,14],
		//16:[11,12,13,14],
		//17:[15],
		//18:[16]
	}
};


//global variables
var databaseUpdates = new Array(); //what needs to be updated??? NAH: just update on the fly, XML-RPC
//var _dump;
var currentSourceIds = new Object();
var currentTargetIds = new Object();
var currentSourceLang;
var linkMode = false;
var enableShowUnslinked = true;
var loaded = false;
var currentNodeMouseover = null;



function getTargetLang(sourceLang){
	if(sourceLang == 'es')
		return 'fr';
	else
		return 'es';
}

function getSlink(sourceLang, sourceIndex){
	var targetLang = getTargetLang(sourceLang);
	
	var sourceList = new Object();
	var targetList = new Object();
	
	if(semanticLinks[sourceLang][sourceIndex]){
		sourceList[sourceLang + sourceIndex] = true;
		for(var i = 0; i < semanticLinks[sourceLang][sourceIndex].length; i++){
			var targetIndex = semanticLinks[sourceLang][sourceIndex][i];
			targetList[targetLang + targetIndex] = true;
			if(semanticLinks[targetLang][targetIndex]){
				for(var j = 0; j < semanticLinks[targetLang][targetIndex].length; j++){
					sourceList[sourceLang + semanticLinks[targetLang][targetIndex][j]] = true;
				}
			}
		}
	}
	
	return {
		source:sourceList,
		target:targetList
	};
}

function highlightUnslinked(){
	var spans = document.getElementById('slinkTable').getElementsByTagName('span');
	var lang, index;
	for(var i = 0; i < spans.length; i++){
		lang = spans.item(i).id.substr(0,2);
		index = parseInt(spans.item(i).id.substr(2));
		if(!semanticLinks[lang][index])
			//spans.item(i).className = spans.item(i).className + " unslinked";
			addClass(spans.item(i), 'unslinked');
	}
}
function unhighlightUnslinked(){
	var spans = document.getElementById('slinkTable').getElementsByTagName('span');
	var lang, index;
	for(var i = 0; i < spans.length; i++){
		lang = spans.item(i).id.substr(0,2);
		index = parseInt(spans.item(i).id.substr(2));
		if(!semanticLinks[lang][index])
			//spans.item(i).className = String(spans.item(i).className).replace(/\s*unslinked/g, '');
			removeClass(spans.item(i), 'unslinked');
	}
}

function highlightSlink(sourceLang, sourceIndex){
	var count = 0;
	var slink = getSlink(sourceLang, sourceIndex);
	//unhighlightSlink();
	for(var sourceId in slink.source)
		currentSourceIds[sourceId] = true;
	for(var targetId in slink.target)
		currentTargetIds[targetId] = true;
	//add a title attribute... the join of the linked words?
	
	var word;
	for(var sourceId in currentSourceIds){
		addClass(document.getElementById(sourceId), 'selected');
		//word = document.getElementById(sourceId);
		//word.className = word.className + " selected";
		count++;
	}
	for(var targetId in currentTargetIds){
		addClass(document.getElementById(targetId), 'selected');
		//word = document.getElementById(targetId);
		//word.className = word.className + " selected";
		count++;
	}
	//if(count){
	//	this.className = this.className + " selected";
	//	currentSourceIds[this.id] = true;
	//}
	return count;
}

function unhighlightSlink(){
	var count = 0;
	var word;
	for(var sourceId in currentSourceIds){
		removeClass(document.getElementById(sourceId), 'selected');
		//word = document.getElementById(sourceId);
		//word.className = String(word.className).replace(/\s*selected/g, '');
		count++;
		delete currentSourceIds[sourceId];
	}
	for(var targetId in currentTargetIds){
		removeClass(document.getElementById(targetId), 'selected');
		//word = document.getElementById(targetId);
		//word.className = String(word.className).replace(/\s*selected/g, '');
		count++;
		delete currentTargetIds[targetId];
	}
	return count;
}

function toggleShowUnlinked(){
	if(enableShowUnslinked){
		document.getElementById('showUnlinkedBtn').value = "Show unslinked";
		unhighlightUnslinked();
		enableShowUnslinked = false;
	}
	else {
		document.getElementById('showUnlinkedBtn').value = "Hide unslinked";
		highlightUnslinked();
		enableShowUnslinked = true;
	}
}

function removeSlink(sourceLang, sourceIndex){
	if(semanticLinks[sourceLang][sourceIndex]){
		var targetLang = getTargetLang(sourceLang);
		for(var i = 0; i < semanticLinks[sourceLang][sourceIndex].length; i++){
			var targetIndex = semanticLinks[sourceLang][sourceIndex][i];
			//UPDATE THE DATABASE?
			//databaseUpdates.push("DELETE FROM slink WHERE " + sourceLang + " = " + sourceIndex + " AND " + targetLang + " = " + targetIndex + ";");
			databaseUpdates.push([DELETE, sourceLang, sourceIndex, targetLang, targetIndex]);
			if(semanticLinks[targetLang][targetIndex]){
				for(var j = 0; j < semanticLinks[targetLang][targetIndex].length; j++)
					semanticLinks[targetLang][targetIndex].splice(j--, 1);
				if(semanticLinks[targetLang][targetIndex].length == 0)
					delete semanticLinks[targetLang][targetIndex];
			}
		}
		delete semanticLinks[sourceLang][sourceIndex];
	}
}

function addSlink(sourceLang, sourceIndex, targetLang, targetIndex){
	
}

function saveSlink(){
	if(!hasTrueProperty(currentSourceIds) ^ !hasTrueProperty(currentTargetIds)){
		alert("You must select words from both languages to create a link. Deselect all to delete the link.");
		return false;
	}
	//DumperAlert(currentSourceIds);
	//DumperAlert(currentTargetIds);
	
	//delete all existing links-----------
	for(var sourceId in currentSourceIds)
		removeSlink(sourceId.substr(0,2), parseInt(sourceId.substr(2)));
	for(var targetId in currentTargetIds)
		removeSlink(targetId.substr(0,2), parseInt(targetId.substr(2)));

	//DumperAlert(databaseUpdates);
	//create new links--------------------
	for(var sourceId in currentSourceIds){
		var sourceLang = sourceId.substr(0,2);
		var sourceIndex = sourceId.substr(2);
		//var word = document.getElementById(sourceId);
		if(currentSourceIds[sourceId]){
			semanticLinks[sourceLang][sourceIndex] = new Array();
			for(targetId in currentTargetIds){
				if(currentTargetIds[targetId]){
					//add links to database
					//databaseUpdates.push("INSERT INTO slink (" + sourceLang + ", " + targetId.substr(0,2) + ") VALUES(" + sourceIndex + ", " + targetId.substr(2) + ");");
					databaseUpdates.push([INSERT, sourceLang, sourceIndex, targetId.substr(0,2), targetId.substr(2)]);
					semanticLinks[sourceLang][sourceIndex].push(parseInt(targetId.substr(2)));
				}
			}
			//word.className = word.className.replace(/\s*unslinked/, '');
			removeClass(document.getElementById(sourceId), 'unslinked');
		}
		else
			//word.className = word.className + " unslinked";
			addClass(document.getElementById(sourceId), 'unslinked');
	}
	for(var targetId in currentTargetIds){
		var targetLang = targetId.substr(0,2);
		var targetIndex = targetId.substr(2);		
		//var word = document.getElementById(targetId);
		if(currentTargetIds[targetId]){
			semanticLinks[targetLang][targetIndex] = new Array();
			for(sourceId in currentSourceIds){
				if(currentSourceIds[sourceId]){
					semanticLinks[targetLang][targetIndex].push(parseInt(sourceId.substr(2)));
				}
			}
			//word.className = word.className.replace(/\s*unslinked/, '');
			removeClass(document.getElementById(targetId), 'unslinked');
		}
		else
			//word.className = word.className + " unslinked";
			addClass(document.getElementById(targetId), 'unslinked');
	}
	
	if(document.getElementById('liveUpdates').checked)
		updateDatabase();

	cancelLinkMode();
}

function changeLiveUpdates(){
	if(document.getElementById('liveUpdates')){
		document.getElementById('liveUpdates').disabled = false;
		if(document.getElementById('liveUpdates').checked)
			document.getElementById('updateDBBtn').disabled = true;
		else
			document.getElementById('updateDBBtn').disabled = false;
	}
	if(document.getElementById('updateDBBtn'))
		document.getElementById('updateDBBtn').disabled = false;
}

function updateDatabase(){
	if(databaseUpdates.length > 0){
		try {
			xmlrpcService.synchronizeDatabase(databaseUpdates);
			databaseUpdates.length = 0;
			
			var msg = document.getElementById('updateStatus');
			msg.style.visibility = 'visible';
			msg.innerHTML = "Database updated";
			msg.style.color = "blue";
			window.setTimeout("hideUpdateStatus()", 1500);
		}
		catch(e){
			alert(e);
		}
	}
	else {
		var msg = document.getElementById('updateStatus');
		msg.style.visibility = 'visible';
		msg.innerHTML = "No updates to be made";
		msg.style.color = "green";
		window.setTimeout("hideUpdateStatus()", 1500);
	}
}

function hideUpdateStatus(){
	document.getElementById('updateStatus').style.visibility = 'hidden';
}

function cancelSlink(){
	cancelLinkMode();
}

function cancelLinkMode(){
	removeClass(document.getElementById('slinkTable'), 'linkMode');
	document.getElementById('saveSlinkBtn').disabled = true;
	document.getElementById('cancelSlinkBtn').disabled = true;
	linkMode = false;
	
	//if mouse if over a span that is not part of the current slink, mouseover() it
	if(currentNodeMouseover && !(currentSourceIds[currentNodeMouseover.id] != undefined || currentTargetIds[currentNodeMouseover.id] != undefined)){
		unhighlightSlink();
		currentNodeMouseover.onmouseover();
	}
	
	changeLiveUpdates();
}

//event handlers---------------------------
function word_mouseover(){
	currentNodeMouseover = this;
	try {
		if(loaded != undefined && loaded && !linkMode){
			unhighlightSlink();
			highlightSlink(this.id.substr(0,2), parseInt(this.id.substr(2)));
		}
	}
	catch(e){
	
	}
}
function word_mouseout(){
	currentNodeMouseover = null;
	try {
		if(loaded != undefined && loaded && !linkMode)
			unhighlightSlink();
	}
	catch(e){
	
	}
}
function word_click(){
	var lang = this.id.substr(0,2);
	var index = parseInt(this.id.substr(2));
	
	//enter link mode
	if(!linkMode){
		linkMode = true;
		addClass(document.getElementById('slinkTable'), 'linkMode');
		
		currentSourceLang = lang;
		document.getElementById('saveSlinkBtn').disabled = false;
		document.getElementById('cancelSlinkBtn').disabled = false;
		document.getElementById('liveUpdates').disabled = true;
		document.getElementById('updateDBBtn').disabled = true;

		//highlight, and if not part of slink
		if(!highlightSlink(this.id.substr(0,2), parseInt(this.id.substr(2)))){
			//this.className = this.className + " selected";
			addClass(this, 'selected')
			currentSourceIds[this.id] = true;
		}
	}
	//word is part of another slink... must be merged
	else if(semanticLinks[lang][index] && !(currentSourceIds[this.id] != undefined || currentTargetIds[this.id] != undefined)){
		if(!confirm("The word you selected is already part of a semantic link. Are you sure you wish to merge these two links?"))
			return false;
		var mergeLang = lang;
		var mergeIndex = index;
		if(mergeLang != currentSourceLang){
			mergeLang = getTargetLang(mergeLang);
			mergeIndex = semanticLinks[lang][index][0];
		}
		highlightSlink(mergeLang, mergeIndex);
	}
	//word is not part of slink or is part of this slink
	else {
		var added;
		if(lang == currentSourceLang){
			currentSourceIds[this.id] = !currentSourceIds[this.id];
			added = currentSourceIds[this.id];
		}
		else {
			currentTargetIds[this.id] = !currentTargetIds[this.id];
			added = currentTargetIds[this.id];
		}
		
		//select or deselect the word
		if(added)
			//this.className = this.className + " selected";
			addClass(this, 'selected');
		else 
			//this.className = this.className.replace(/\s*selected/g, '');
			removeClass(this, 'selected');
	}
}




//support functions---------------------
function addClass(node, className){
	node.className = node.className + " " + className;
}
function removeClass(node, className){
	var re = new RegExp("\\s*" + className, "ig");
	node.className = String(node.className).replace(re, '');
}

function countProperties(obj){
	var count = 0;
	for(var key in obj)
		count++;
	return count;
}
function hasTrueProperty(obj){
	for(var key in obj){
		if(obj[key] == true)
			return true;
	}
	return false;
}

//initializer--------------------------------------------
function init(){
	document.getElementsByTagName('body')[0].appendChild(errorNotice);

	var table = document.getElementById('slinkTable');
	
	//make the text in the table unselectable so that double-clicking doesn't select
	var abandonClick = function(){return false};
	if(document.all){
		table.onselectstart = abandonClick;
	}
	table.onmousedown = abandonClick;
	var spans = table.getElementsByTagName('span');
	
	//get first spanish ID
	var firstSpanishId;
	for(var i = 0; i < spans.length; i++){
		if(spans.item(i).id.substr(0,2) == 'es'){
			firstSpanishId = parseInt(spans.item(i).id.substr(2));
			break;
		}
	}
	//get last spanish ID
	var lastSpanishId;
	for(var i = spans.length-1; i >= 0; i--){
		if(spans.item(i).id.substr(0,2) == 'es'){
			lastSpanishId = parseInt(spans.item(i).id.substr(2));
			break;
		}
	}
	
	//get semantic links
	try {
		semanticLinks = xmlrpcService.loadSemanticLinks(firstSpanishId,lastSpanishId);
	} catch(e){
		alert("Unable to load semantic links:\n" + e);
	}
	
	
	//add event listeners to words
	for(var i = 0; i < spans.length; i++){
		spans.item(i).onmouseover = word_mouseover;
		spans.item(i).onmouseout = word_mouseout;
		spans.item(i).onclick  = word_click;
		spans.item(i).ondblclick  = function(){return false;};
		var lang = spans.item(i).id.substr(0,2);
		var index = parseInt(spans.item(i).id.substr(2));
		if(!semanticLinks[lang][index])
			//spans.item(i).className = spans.item(i).className + " unslinked";
			addClass(spans.item(i), 'unslinked');
	}
	//_dump = document.getElementById('_dump');
	//toggleShowUnlinked();
	loaded = true;
	
	document.onkeypress = function(e){
		var key;		
		if(window.event)// for IE, e.keyCode or window.event.keyCode can be used
			key = e.keyCode; 
		else if(e.which)// netscape
			key = e.which;
		else // no event, so pass through
			return true;
		
		if(linkMode){
			if(key == 13 || key == 115 || key == 67){ //Return or "s" or "S"
				saveSlink();
				return false;
			}
			else if(key == 8 || key == 99 || key == 83){ //Backspace or "c" or "C"
				cancelSlink();
				return false;
			}
		}
		return true;
	}
	document.getElementById('username').onchange = function(){this.value = this.value.replace(/\W/g, ''); }
	document.getElementById('password').onchange = function(){this.value = this.value.replace(/\W/g, ''); }
	changeLiveUpdates();
	
	return true;
}

function finalize(){
	if(databaseUpdates.length != 0 && confirm("There are updates that have yet to applied to the database. Would you like to apply them now?"))
		updateDatabase();
	return true;
}

function loginUser(){
	var username = document.getElementById('username');
	var password = document.getElementById('password');
	if(!username.value){
		alert("You must enter a value for your username.");
		username.focus();
		return false;
	}
	if(!password.value){
		alert("You must enter a value for your password.");
		password.focus();
		return false
	}
	try {
		if(!xmlrpcService.login(username.value, password.value)){
			alert("Your username/password combination is incorrect.");
			username.focus();
			return false;
		}
	}
	catch(e){
		alert(e);
		return false;
	}
	document.getElementById('loginForm').style.display = 'none';
	return true;
}

function logoutUser(){
	if(xmlrpcService.logout())
		alert("You have been logged out.");
	else
		alert("You could not be logged out.");
	
}
