// Functions for Search Comparison GUI
// Coded by Atom for The Evolutionary Informatics Lab.

//Vars
var InitialSlideVal = 0.9;
var Speed = Math.floor(((1 - InitialSlideVal) * 1000) + 1);
var EnglishCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
var BinaryCharacters = "01";
var NucleotideCharacters = "ATCG";
var CurrentAllowedCharacters = ($('rdbEnglish').checked ? EnglishCharacters : ($('rdbBinary').checked ? BinaryCharacters : NucleotideCharacters));
var objSearch = new SearchObject('', CurrentAllowedCharacters);
var objMultiRun = new MultiRunObject();
var interID;
var arrTo = new Array();
var IsCompleted = false;

// Slider control for speed
new Control.Slider('speedHandle', 'speedTrack', {
	sliderValue: InitialSlideVal,
	onSlide: function(v){
			Speed = Math.floor(((1 - v) * 1000) + 1); 
			if (interID) {
				$('btnStop').click(); 
				while (arrTo.length > 0) clearTimeout(arrTo.pop());
				arrTo.push(setTimeout("$('btnStartSearch').click();", 200));
			}
	}
});

//Utility functions
function isControlKey(key) {
	return (key == 8 || key == 9 || key == 13);
}

function isNumeric(keyCode) {
	return !(keyCode > 57 || (keyCode > 30 && keyCode < 48));
}

function removeIllegalChars(obj) {
	var input = obj.value;
	var output = '';
	for (u = 0; u < input.length; u++) {
		if (CurrentAllowedCharacters.indexOf(input.charAt(u)) > -1 ) output += input.charAt(u);
	}
	obj.value = output;
}

function addLoadEvent(func) {
	var origOnload = window.onload;
	
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {		
			if (origOnload) {origOnload();}
			func();
		}
	}
}

function moveCursorToEndOfInput(obj) {
 	if (obj.createTextRange) {
        var objRange = obj.createTextRange();
        objRange.moveStart("character", obj.value.length);
        objRange.moveEnd("character", obj.value.length);
        objRange.select();
    } else if (obj.setSelectionRange) {
        obj.setSelectionRange(obj.value.length, obj.value.length);
    } 
}

function formatNucleotideGroupings(objTextbox) {
	var strippedString = objTextbox.value.replace(/ /g, '');
	var arrLetters = strippedString.split('');
	var output = '';

	for (k = 0; k < arrLetters.length; k += 1) {
		output += (k % 3 == 0 && k !=0) ? ' ' + arrLetters[k] : arrLetters[k];
	}

	objTextbox.value = output;
	moveCursorToEndOfInput(objTextbox);
}

function getKeyCode(e) {
	return e.keyCode ? e.keyCode : e.charCode ? e.charCode : e.which;
}

function tab_keydown(e){
	var element = Event.element(e);
	var id = element.id;
	var keyCode = getKeyCode(e);

	if (keyCode==9){
		Event.stop(e);

		if (document.selection) { // for IE                     
			$(id).focus();                        
			var sel = document.selection.createRange();		
			sel.text ='\t' + sel.text;	
		} else if (typeof $(id).selectionStart != 'undefined') { // for FF, Opera etc...
			var len = $(id).value.length;
			var start =  $(id).selectionStart;
			var end =  $(id).selectionEnd;
			var sel =  $(id).value.substring(start, end);
			$(id).value =  $(id).value.substring(0,start) + '\t' + sel +  $(id).value.substring(end);
			setTimeout('$("' + id + '").focus(); $("' + id + '").selectionStart = ' + (start + 1 + sel.length) + '; $("' + id + '").selectionEnd = ' + (start + 1 + sel.length), 5);
		}
	}
}

/************************
* Assign event handlers
************************/
$('rdbEnglish').onclick = function() {CurrentAllowedCharacters = EnglishCharacters; $('txtPhrase').value = ''; $('txtPhrase').focus(); $('btnStop').click(); ResetSearch();}
$('rdbBinary').onclick = function() {CurrentAllowedCharacters = BinaryCharacters; $('txtPhrase').value = ''; $('txtPhrase').focus();  $('btnStop').click(); ResetSearch();}
$('rdbNucleotides').onclick = function() {CurrentAllowedCharacters = NucleotideCharacters; $('txtPhrase').value = ''; $('txtPhrase').focus();  $('btnStop').click(); ResetSearch();}

$('txtPhrase').onkeypress = function(e) {
	var evt = (e) ? e : window.event;
	var key = (evt.keyCode) ? evt.keyCode : evt.which;
	var keyChar = (String.fromCharCode(key)).toUpperCase();
	var result = true;

	if (key != null) {
		result = (CurrentAllowedCharacters.indexOf(keyChar) > -1 || isControlKey(key));
		if (!isControlKey(key)) {
			this.value = this.value.toUpperCase();
			$('btnStop').click(); 	//Stop search if phrase changed
			ResetSearch();
		}
	}

	return result;
}

$('txtPhrase').onblur = function() {
	this.value = this.value.toUpperCase();
	removeIllegalChars(this);
	if ($('rdbNucleotides').checked) this.value = this.value.replace(/ /g, ''); 
	$('QueryNumOfChars').innerHTML = this.value.length;	
}

$('txtPhrase').onkeyup = function() {
	FormatInputUpdateCount(this);
}

$('txtPhrase').onfocus = function() {
	FormatInputUpdateCount(this);
}

$('txtSteps').onblur = function() {
	this.value = parseInt(this.value, 10) > 1000 ? 1000 : parseInt(this.value, 10);
}

$('txtSteps').onkeypress = function(e) {
	var evt = (e) ? e : window.event;
	var key = (evt.keyCode) ? evt.keyCode : evt.which;
	
	return isNumeric(key);
}

$('btnStartSearch').onclick = function() {
	if (interID) clearInterval(interID);
	interID = setInterval("TakeStepForward();", Speed);
}

$('btnStepForward').onclick = function() {
	var btnVal = $('btnStepForward').value;
	var tmpID = interID; //Save this to check if we were in auto search later. if we were, we'll want to reusme auto search after we do these steps forward.

	$('btnStop').click();
	
	if (IsCompleted) {
		NotifyUserSearchCompleted();
	} else {
		$('btnStepForward').disabled = true;
		$('txtProxOffspring').disabled = true;
		$('txtProxMutationRate').disabled = true;
		$('txtNeutOffspring').disabled = true;
		$('txtNeutMutationRate').disabled = true;

		$('btnStepForward').value = "Stepping...";
		setTimeout("for (n = 0; n < parseInt($('txtSteps').value, 10) && n < 1000 && !IsCompleted; n++) {TakeStepForward();} " +
					"$('btnStepForward').disabled = false; $('txtProxOffspring').disabled = false; $('txtProxMutationRate').disabled = $('chkProxFixedMutation').checked; " +
					"$('txtNeutOffspring').disabled = false; $('txtNeutMutationRate').disabled = false; " +
					"$('btnStepForward').value = '" + btnVal + "';" + (tmpID ? "$('btnStartSearch').click();" : ""), 1);
	}
}

$('btnReset').onclick = function() {
	if (interID) clearInterval(interID);
	interID = null;
	ResetSearch();
}

$('btnStop').onclick = function() {
	if (interID) clearInterval(interID);
	interID = null;
	while (arrTo.length > 0) clearTimeout(arrTo.pop()); //clear any timeouts set to restart search
}

$('btnViewPartHistory').onclick = function() {
	DisplaySearchHistory(objSearch.GetHistoryHTML(objSearch.PartHistory), "Partitioned Search");
}

$('btnViewProxHistory').onclick = function() {
	DisplaySearchHistory(objSearch.GetHistoryHTML(objSearch.ProxHistory), "Proximity Reward Search");
}

$('btnViewNeutHistory').onclick = function() {
	DisplaySearchHistory(objSearch.GetHistoryHTML(objSearch.NeutHistory), "Proximity Neutral Search");
}

$('btnViewDetHistory').onclick = function() {
	DisplaySearchHistory(objSearch.GetHistoryHTML(objSearch.DetHistory), "Deterministic Search");
}

$('btnViewRandomHistory').onclick = function() {
	DisplaySearchHistory(objSearch.GetHistoryHTML(objSearch.RandomHistory), "Unassisted Random Search");
}


$('DisableOptionPart').onclick = function() {
	IsCompleted = false;
}
	
$('DisableOptionProx').onclick = function() {
	IsCompleted = false;
}

$('DisableOptionNeut').onclick = function() {
	IsCompleted = false;
}

$('DisableOptionDet').onclick = function() {
	IsCompleted = false;
}

$('DisableOptionRandom').onclick = function() {
	IsCompleted = false;
}

$('ddlNeutAlgorithms').onchange = function() {
	if ($(this).options[$(this).selectedIndex].text == 'Custom Fitness Function') {
		Effect.Appear('btnNeutEditFitnessFunction'); 
	} else {
		Effect.Fade('btnNeutEditFitnessFunction'); 
	}
}

$('btnNeutEditFitnessFunction').onclick = function() {
	var editor = GetCustomFunctionEditorHTML();

	Shadowbox.open({
        	player:     'html',
        	title:      'Custom Fitness Function: Code Edit',
        	content:   	editor,
        	height:     440,
        	width:      700,
			options:	{enableKeys: false}
	});

	setTimeout('Event.observe($("CustomFunctionTextarea"),"keydown",tab_keydown);', 2000);
}

function updateCustomFunction() {
	objSearch.CustomFitnessFunction = $('CustomFunctionTextarea').value;
	closeCustomFunction();
}

function closeCustomFunction() {
	Shadowbox.close();
}

$('btnMultiRunsStart').onclick = function() {
	if (typeof objMultiRun == "undefined") objMultiRun = new MultiRunObject();
	objMultiRun.CurrentlyOn = true;
	$('btnStartSearch').click();
}

$('btnMultiRunsReset').onclick = function() {
	objMultiRun = new MultiRunObject();
	$('CurrentIteration').innerHTML = 0;
	$('btnReset').click();
}

$('btnMultiRunsStop').onclick = function() {
	objMultiRun.CurrentlyOn = false;
	$('btnStop').click();
}

$('btnViewMultiRunsHistory').onclick = function() {
	DisplaySearchHistory(GetMultiRunResults(), "Multi-Runs Mode");
}

$('chkProxFixedMutation').onclick = function() {
	$('txtProxMutationRate').disabled = $('chkProxFixedMutation').checked;
}

/*********** Get HTML for display **************/
function GetMultiRunResults() {
	var output = "<a href=\"#\" onclick=\"$(this.parentNode).innerHTML = objMultiRun.GetHistoryCSV(); return false;\">View as CSV</a><br/>";
	output += "<a href=\"#\" onclick=\"$(this.parentNode).innerHTML = GetExcelTipsHTML(); return false;\">Tips for Pasting into Excel</a>";
	output += objMultiRun.GetHistoryHTML();
	return output;
}

function GetExcelTipsHTML() {
	return $('ExcelTipsContent').innerHTML.replace(/!--/ig, '').replace(/-->/ig, '>');
}

function GetCustomFunctionsTipsHTML() {
	return $('CustomFunctionsTipsContent').innerHTML.replace(/!--/ig, '').replace(/-->/ig, '>');
}

function GetCustomFunctionEditorHTML() {
	if (typeof objSearch == "undefined") objSearch = new SearchObject($('txtPhrase').value, CurrentAllowedCharacters);
	return $('CustomFitnessFunctionContent').innerHTML.replace(/{FITNESS_FUNCTION}/ig, objSearch.CustomFitnessFunction).replace(/!--/ig, '').replace(/-->/ig, '>');
}

//Regular Methods
function ResetSearch() {
	if (objSearch) objSearch.ResetSearchArrays();
	$('PartSearchMedNumQueries').innerHTML = "&nbsp;";
	$('DetSearchMedNumQueries').innerHTML = "&nbsp;";
	$('RandomSearchMedNumQueries').innerHTML = "&nbsp;";

	$('PartSearchCurrentString').innerHTML = "&nbsp;";
	$('ProxSearchCurrentString').innerHTML = "&nbsp;";
	$('NeutSearchCurrentString').innerHTML = "&nbsp;";
	$('DetSearchCurrentString').innerHTML = "&nbsp;";
	$('RandomSearchCurrentString').innerHTML = "&nbsp;";

	$('PartSearchQueryCount').innerHTML = 0;
	$('ProxSearchQueryCount').innerHTML = 0;
	$('NeutSearchQueryCount').innerHTML = 0;
	$('DetSearchQueryCount').innerHTML = 0;
	$('RandomSearchQueryCount').innerHTML = 0;

	$('btnStepForward').disabled = false;
	$('txtProxOffspring').disabled = false;
	$('txtProxMutationRate').disabled = $('chkProxFixedMutation').checked;
	$('txtNeutOffspring').disabled = false;
	$('txtNeutMutationRate').disabled = false;

	if ($('btnStepForward').value == "Stepping...") $('btnStepForward').value = "Step Forward: ";

	IsCompleted = false;
}

function NotifyUserSearchCompleted() {
	alert('Search Completed. (All targets found.)');
}

function FormatInputUpdateCount(obj) {
	$('QueryNumOfChars').innerHTML = obj.value.length;
	
	if ($('rdbNucleotides').checked) {
		formatNucleotideGroupings(obj);
		$('QueryNumOfChars').innerHTML = obj.value.replace(/ /g, '').length;
	}
}

function TakeStepForward() {
	if (typeof objSearch == "undefined") objSearch = new SearchObject($('txtPhrase').value, CurrentAllowedCharacters);
	if (typeof objMultiRun == "undefined") objMultiRun = new MultiRunObject();

	var algNeut = $('ddlNeutAlgorithms').options[$('ddlNeutAlgorithms').selectedIndex].value;
	
	//If target phrase changed, create new search
	if (objSearch.TargetPhrase != $('txtPhrase').value) {
		var currentCustomFunction = objSearch.CustomFitnessFunction;
		objSearch = new SearchObject($('txtPhrase').value, CurrentAllowedCharacters);
		objSearch.CustomFitnessFunction = currentCustomFunction;
	}

	//Set current Offspring per generation and mutations per organism from GUI
	objSearch.ProxChildrenPerGeneration = $('txtProxOffspring').value * 1;
	objSearch.ProxMutationRate = $('txtProxMutationRate').value * 1;
	objSearch.ProxFixedSingleMutation = $('chkProxFixedMutation').checked;
	objSearch.NeutChildrenPerGeneration = $('txtNeutOffspring').value * 1;
	objSearch.NeutMutationRate = $('txtNeutMutationRate').value * 1;

	//Set disabled array based on checkboxes
	objSearch.DisabledAlgorithms['Part'] = $('DisableOptionPart').checked;
	objSearch.DisabledAlgorithms['Prox'] = $('DisableOptionProx').checked;
	objSearch.DisabledAlgorithms['Neut'] = $('DisableOptionNeut').checked;
	objSearch.DisabledAlgorithms['Det'] = $('DisableOptionDet').checked;
	objSearch.DisabledAlgorithms['Random'] = $('DisableOptionRandom').checked;

	var arrMeds = objSearch.MedianNumberOfQueries;
	$('PartSearchMedNumQueries').innerHTML = arrMeds['Part'];
	$('DetSearchMedNumQueries').innerHTML = arrMeds['Det'];
	$('RandomSearchMedNumQueries').innerHTML = arrMeds['Random'];
	
	//Take one step forward in search
	var arrUpdatedStrings = objSearch.StepForward(CurrentAllowedCharacters, algNeut);

	//Update Query Counts and Current Strings
	if (arrUpdatedStrings['PartUpdated']) {
		$('PartSearchQueryCount').innerHTML = objSearch.QueryCountPart;
		$('PartSearchCurrentString').innerHTML = arrUpdatedStrings['Part'];
	}

	if (arrUpdatedStrings['ProxUpdated']) {
		$('ProxSearchQueryCount').innerHTML = objSearch.QueryCountProx;
		$('ProxSearchCurrentString').innerHTML = arrUpdatedStrings['Prox'];
	}

	if (arrUpdatedStrings['NeutUpdated']) {
		$('NeutSearchQueryCount').innerHTML = objSearch.QueryCountNeut;
		$('NeutSearchCurrentString').innerHTML = arrUpdatedStrings['Neut'];

		//Show any Custom Fitness messages
		$('CustomFitnessModeMessage').innerHTML = objSearch.CustomFitnessMessage;
		$('CustomFitnessModeMessage').title = objSearch.CustomFitnessMessageDetail;
	}

	if (arrUpdatedStrings['DetUpdated']) {
		$('DetSearchQueryCount').innerHTML = objSearch.QueryCountDet;
		$('DetSearchCurrentString').innerHTML = arrUpdatedStrings['Det'];
	}

	if (arrUpdatedStrings['RandomUpdated']) {
		$('RandomSearchQueryCount').innerHTML = objSearch.QueryCountRandom;
		$('RandomSearchCurrentString').innerHTML = arrUpdatedStrings['Random'];
	}

	//If no string was updated, it means our search has found all targets. So stop search.
	if (!(arrUpdatedStrings['PartUpdated'] || arrUpdatedStrings['ProxUpdated'] || arrUpdatedStrings['NeutUpdated'] || arrUpdatedStrings['DetUpdated'] || arrUpdatedStrings['RandomUpdated'])) {
		//If in multi-mode, reset and start next iteration, until limit. If not, notify user that search has ended.
		if (objMultiRun.CurrentlyOn && objMultiRun.Runs.length < $('txtMultiRunsIterations').value) { 
			objMultiRun.SaveRunResults(objSearch);
			$('CurrentIteration').innerHTML = objMultiRun.Runs.length;
			$('btnReset').click();
			$('btnStartSearch').click();
		} else {
			$('btnStop').click();
			NotifyUserSearchCompleted();
			IsCompleted = true;
		}
	}
}

function DisplaySearchHistory(text, searchType) {
	Shadowbox.open({
		player:     'html',
		title:      'History: ' + searchType,
		content:    text,
		height:     506,
		width:      460
    });
}

//Set focus
$('txtPhrase').focus();
$('txtPhrase').select();

//Initialize Shadowbox
addLoadEvent(Shadowbox.init);

//Create Sortable list
Sortable.create("SearchResults");

//Initialize Algorithms Select box
if (NeutFitnessModes) {
	var opt;
	var ddlNames = new Array('ddlNeutAlgorithms');
	for (var j = 0; j < ddlNames.length; j++) {
		while ($(ddlNames[j]).options.length > 0) {
			$(ddlNames[j]).options.remove(0);
		}
	
		for (var i = 0; i < NeutFitnessModes.length; i++) {
			opt = document.createElement('option');
			opt.text = NeutFitnessModes[i];
			opt.value = i;
			
			try {
				$(ddlNames[j]).options.add(opt, null) // standards compliant
			} catch(ex) {
				$(ddlNames[j]).options.add(opt); // IE only
			}
		}
		
		//Default to correct algorithm
		for (var i = $(ddlNames[j]).options.length  - 1; i >= 0; i--) { 
			$(ddlNames[j]).selectedIndex = i;
			if ('Partially-Neutral: Anagram' == $(ddlNames[j]).options[i].text) break;
		}
	}
}