/*
	FILE: fsm.js
	DESC: Finite State Machine Simulator Controls
	AUTHOR: DAFYDD VAUGHAN 327309
	DATE: 11/12/2007 23:42
*/


//arrays for storing the devices
var deviceName = new Array(0);
var deviceCode = new Array(0);
var loadedDeviceNum;
var previousDeviceNum;
var loadedDevice;
var loadedState;


/* ******************************************************** */
/*  DEVICES                                                 */
/* ******************************************************** */
deviceName[0] = "Simple Torch";
deviceCode[0] = "{\n  \"notes\": \"Simple torch, with dim mode.\",\n  \"modelType\": \"Light bulb\",\n  \"buttons\": [\"Off\",\"On\",\"Dim\"],\n  \"buttonsImageMap\": [[88,27,15],[135,75,15],[157,100,15]],\n  \"stateNames\": [\"dim\",\"off\",\"on\"],\n  \"fsm\": [[1,2,0],[1,2,0],[1,2,0]],\n  \"startState\": 1,\n  \"state\": 1,\n  \"manual\": [\"dim\",\"dark\",\"bright\"],\n  \"action\": [\"press\",\"pressed\",\"pressing\"],\n  \"errors\": \"never\",\n  \"graphics\": \"pics/torch/off.gif\",\n  \"stateGraphics\": [\"pics/torch/dim.gif\",\"pics/torch/off.gif\",\"pics/torch/on.gif\"]\n}";
deviceName[1] = "Digital Photo Frame";
deviceCode[1] = "{ \n  \"notes\": \"Digital photo frame with 4 different navigators.\", \n  \"modelType\": \"Digital Photo Frame\", \n  \"buttons\": [\"Power\",\"Menu\",\"Music\",\"Movie\",\"Photo\",\"File Nav\"], \n  \"stateNames\": [\"off\",\"function selector\",\"music player\",\"movie player\",\"photo player\",\"file navigator\"], \n  \"fsm\": [[1,0,0,0,0,0],[0,1,2,3,4,5],[0,1,2,3,4,5],[0,1,2,3,4,5],[0,1,2,3,4,5],[0,1,2,3,4,5]], \n  \"startState\": 0, \n  \"state\": 0, \n  \"manual\": [\"power\",\"function selector\",\"music player\",\"movie player\",\"photo player\",\"file navigator\"], \n  \"action\": [\"press\",\"pressed\",\"pressing\"], \n  \"errors\": \"never\", \n  \"graphics\": \"pics/photoframe/off.gif\", \n  \"stateGraphics\": [\"pics/photoframe/off.gif\",\"pics/photoframe/menu.gif\",\"pics/photoframe/music.gif\",\"pics/photoframe/movie.gif\",\"pics/photoframe/photo.gif\",\"pics/photoframe/filenav.gif\"] \n}";



/* ******************************************************** */
/*  LOAD SIMULATOR                                          */
/* ******************************************************** */
function loadFSM() {

	//load the device list
	loadDeviceList();

	//hide all non filled divs
	document.getElementById('fsmSystemDeviceMenuHead').className="fsmsectionhead fsmsectionheaddisabled";


//	previousDeviceNum = 0;
//	loadNewDevice(1);

}


/* ******************************************************** */
/*  LOAD LIST OF DEVICES                                    */
/* ******************************************************** */
function loadDeviceList() {

	//begin list
	var btns = "<ul>";

	//for each device in the device array
	for(currBtn=0; currBtn < deviceCode.length; currBtn++)
	{
		//add a button
		btns = btns + "<li><a href=\"#device\" onclick=\"loadNewDevice(" + currBtn + ");\" class=\"fsmButton\">" + deviceName[currBtn] + "</a></li>";

	}

	//end list
	btns = btns + "</ul>"
	
	//post list into the available div
	document.getElementById('fsmDeviceList').innerHTML = btns;

}

/* ******************************************************** */
/*  LOAD A DEVICE                                           */
/* ******************************************************** */
function loadNewDevice(devNum) {

	//display all divs
	toggleElements(1);

	//load device name and code
	document.getElementById('fsmDeviceCode').value = deviceCode[devNum];
	document.getElementById('fsmDeviceName').innerHTML = deviceName[devNum];


	//store current device in memory
	loadedDevice = eval("(" + deviceCode[devNum] + ")");
	loadedDeviceNum = devNum;
	previousDeviceNum = devNum;
	loadedState = loadedDevice.startState;

	//start the simulator
	document.getElementById('fsmDeviceDesc').innerHTML = loadedDevice.notes;
	document.getElementById('fsmDeviceImage').src = loadedDevice.graphics;
	document.getElementById('fsmDeviceCurrentState').innerHTML = loadedDevice.stateNames[loadedState];

	//create state buttons
	var btns = "<ul>";
	for(currBtn=0; currBtn < loadedDevice.buttons.length; currBtn++)
	{
		btns = btns + "<li><a href=\"#simulator\" onclick=\"loadState(" + currBtn + ");\">" + loadedDevice.buttons[currBtn] + "</a></li>";

	}
	btns = btns + "</ul>"
	document.getElementById('fsmDeviceStates').innerHTML = btns;

	//generate transition table
	displayTransitionTable();
	//generate dot description
	displayDotDescription();

}


/* ******************************************************** */
/*  LOAD CUSTOM DEVICE SIMULATOR                            */
/* ******************************************************** */
function loadCustomDevice(devName,devCode,devNum) {

	//load device name and code
	document.getElementById('fsmDeviceCode').value = devCode;
	document.getElementById('fsmDeviceName').innerHTML = devName;


	//store current device in memory
	loadedDevice = eval("(" + devCode + ")");
	loadedDeviceNum = devNum;
	loadedState = loadedDevice.startState;

	//start the simulator
	document.getElementById('fsmDeviceDesc').innerHTML = loadedDevice.notes;
	document.getElementById('fsmDeviceImage').src = loadedDevice.graphics;
	document.getElementById('fsmDeviceCurrentState').innerHTML = loadedDevice.stateNames[loadedState];

	//create state buttons
	var btns = "<ul>";
	for(currBtn=0; currBtn < loadedDevice.buttons.length; currBtn++)
	{
		btns = btns + "<li><a href=\"#simulator\" onclick=\"loadState(" + currBtn + ");\">" + loadedDevice.buttons[currBtn] + "</a></li>";

	}
	btns = btns + "</ul>"
	document.getElementById('fsmDeviceStates').innerHTML = btns;

}

/* ******************************************************** */
/*  LOAD A STATE                                            */
/* ******************************************************** */
function loadState(actionNum) {

	//load new state
	loadedState = loadedDevice.fsm[loadedState][actionNum];

	//set the state graphic & current state name
	document.getElementById('fsmDeviceImage').src = loadedDevice.stateGraphics[loadedState];
	document.getElementById('fsmDeviceCurrentState').innerHTML = loadedDevice.stateNames[loadedState];
	

}

/* ******************************************************** */
/*  DEVICE SPEC CHANGED                                     */
/* ******************************************************** */
function fsmSpecChanged() {
	//show reset & reload buttons
	document.getElementById('fsmDeviceReload').style.display="block";
	document.getElementById('fsmDeviceReloadReset').style.display="inline";
	document.getElementById('fsmDeviceReloadUpdate').style.display="inline";
}

/* ******************************************************** */
/*  RESET DEVICE SPEC                                       */
/* ******************************************************** */
function fsmResetSpec() {
	//reload the device
	loadNewDevice(previousDeviceNum);
	document.getElementById('fsmDeviceReload').style.display="none";
	document.getElementById('fsmDeviceReloadReset').style.display="none";
	document.getElementById('fsmDeviceReloadUpdate').style.display="none";

}
/* ******************************************************** */
/*  UPDATE THE SIMULATION                                   */
/* ******************************************************** */
function fsmUpdateSim() {

	//if the spec has changed load a custom device
	if (previousDeviceNum == -1)
	{
		loadCustomDevice("Custom Simulator",document.getElementById('fsmDeviceCode').value,-1);
		document.getElementById('fsmDeviceReload').style.display="none";
		document.getElementById('fsmDeviceReloadReset').style.display="none";
		document.getElementById('fsmDeviceReloadUpdate').style.display="none";
	} else {
		loadCustomDevice("Custom Simulator (" + deviceName[loadedDeviceNum] + ")",document.getElementById('fsmDeviceCode').value,previousDeviceNum);
		document.getElementById('fsmDeviceReloadUpdate').style.display="none";
	}
	//toggle the elements
	toggleElements(1);



}

/* ******************************************************** */
/*  GENERATE STATE TRANSITION TABLE                         */
/* ******************************************************** */
function displayTransitionTable() {
    
    var sttable = "";
	var onlyOneResult = false;
	var currentActionCSSClass = "";

	//start of the table
    sttable = sttable + "<table>";
	sttable = sttable + "    <thead>";
	sttable = sttable + "        <tr>";
	sttable = sttable + "            <th>Actions</th>";
	sttable = sttable + "            <th>Current State</th>";
	sttable = sttable + "            <th>Next State</th>";
	sttable = sttable + "        </tr>";
	sttable = sttable + "    </thead>";
	sttable = sttable + "    <tbody>";

	//for each action
	for (currentAction=0; currentAction < loadedDevice.buttons.length; currentAction++ )
	{

		//for each state
		for (currentState=0; currentState < loadedDevice.fsm.length; currentState++)
		{
			//if the state is 0
			if (currentState==0)
			{
				//set this state
				state = loadedDevice.fsm[currentState][currentAction];
			//otherwise
			} else {
				//if the state is some other state
				if (state == loadedDevice.fsm[currentState][currentAction])
				{
					//there is only 1 result
					onlyOneResult = true;
				} else {
					//otherwise there is more than one result
					onlyOneResult = false;
					break;
				}
			}
		}

		//set the action highlight (using mod to make sure every other action is highlighted)
		var x = currentAction % 2;
		if (x != 0)
		{
			currentActionCSSClass = "highlight";
		} else {
			currentActionCSSClass = "";
		}
		

		//if there was only one result
		if (onlyOneResult)
		{
			//add the one result to the table
			sttable = sttable + "        <tr class=\"" + currentActionCSSClass + "\">";
			sttable = sttable + "            <td class=\"action\">" + loadedDevice.buttons[currentAction] + "</td>";
			sttable = sttable + "            <td><i>Any</i></td>";
			sttable = sttable + "            <td>" + loadedDevice.stateNames[state] + "</td>";
			sttable = sttable + "        </tr>";
		}
		//otherwise
		else
		{
			//for each state
			for (currentState=0; currentState < loadedDevice.fsm.length; currentState++)
			{
				//if the state is 0
				if (currentState == 0)
				{
					//add the state name spanning multiple columns
					sttable = sttable + "        <tr class=\"" + currentActionCSSClass + "\">";
					sttable = sttable + "            <td class=\"action\" rowspan=\"" + loadedDevice.fsm.length + "\">" + loadedDevice.buttons[currentAction] + "</td>";
				}
				//otherwise
				else
				{
					//just start a new row
					sttable = sttable + "        <tr class=\"" + currentActionCSSClass + "\">";
				}

				//add the current state and next state
				sttable = sttable + "            <td>" + loadedDevice.stateNames[currentState] + "</td>";
				sttable = sttable + "            <td>" + loadedDevice.stateNames[loadedDevice.fsm[currentState][currentAction]] + "</td>";
				sttable = sttable + "        </tr>";
			}
		}
		//reset only one result value
		onlyOneResult = false;

	}

	//close table
	sttable = sttable + "    </body>";
	sttable = sttable + "</table>";


	//output to appropriate div
	document.getElementById("fsmSystemDeviceSTT").innerHTML = sttable;

}

/* ******************************************************** */
/*  GENERATE DOT DESCRIPTION                                */
/* ******************************************************** */
function displayDotDescription() {

	//start the output
	var dotoutput;
	dotoutput = "";
	dotoutput = dotoutput + "digraph \"" + loadedDevice.modelType + "\" { ";
	dotoutput = dotoutput + "<br/>" + "node [shape=ellipse,fontname=Helvetica,fontsize=10];";
	dotoutput = dotoutput + "<br/>" + "edge [fontname=Helvetica,fontsize=10];";
	dotoutput = dotoutput + "<br/>" + "start->" + loadedDevice.startState + ";";
	dotoutput = dotoutput + "<br/>" + "start [label=\"\",style=filled,height=.1,shape=circle,color=black];<br/>";
	
	//for each state in the fsm
	for (currentState = 0; currentState < loadedDevice.fsm.length; currentState++)
	{
		//output the state name
		dotoutput = dotoutput  + currentState + " [label=\"" + loadedDevice.stateNames[currentState] + "\"];<br/>";
	}

	//for each state in the fsm
	for (currentState=0;currentState<loadedDevice.fsm.length;currentState++) 
	{
		//for each action in the state
		for (currentAction=0;currentAction<loadedDevice.fsm[currentState].length;currentAction++) 
		{	
			//if the current state doesn't equal the next state
			//i.e. if is not a self loop
			if (currentState != loadedDevice.fsm[currentState][currentAction])
			{
				//output the transition
				dotoutput = dotoutput +  currentState + "->" + loadedDevice.fsm[currentState][currentAction] + " [label=\"" + loadedDevice.buttons[currentAction] + "\"];<br/>";
			}
		}
	}
	
	//finish the output
	dotoutput = dotoutput  + "}";

	//output to appropriate div
	document.getElementById("fsmSystemDeviceDot").innerHTML = dotoutput;


}
