updateProgress();

function Database(metaXmlDocument) {

    this.getProjectTitle = getProjectTitle;
	this.getStructures = getStructures;
	this.getSteps = getSteps;
	this.setSteps = setSteps;
	this.getStepById = getStepById;
	this.getStepByTitle = getStepByTitle;
	this.getContentById = getContentById;
	this.getGlobalProperty = getGlobalProperty;
	this.getMetaXml = getMetaXml;
	this.getListpageEntries = getListpageEntries;
	this.getListpageEntry = getListpageEntry;
	this.getErrorStep = getErrorStep;
	//this.getTreeNodes = getTreeNodes;
	this.shutdown = shutdown;
	this.getStructuresOfLevel = getStructuresOfLevel;
	this.findFirstStepInStructure = findFirstStepInStructure;
		
	var structures = null;
	var steps = null;
	var extraSteps = null;
	var treemodel = null;
	var treeNodes = new Array();
	var level = 0;
	var zindex = 1000;
	
	var currentRootStructure = null;
	
    this.getStructureById = getStructureById;
    
    var visitedSteps = new Array();
	
	
	var listpageArray = new Array();
	init();
	
	var stepXmlDocument = null;
	 		
	function init() {
		
		if (standardapi) {
			var visitedStepsString = standardapi.getUserData("visited-locations");
			if (visitedStepsString) {
				visitedSteps = visitedStepsString.split(",");
			}
			
		}
		
		// make structures
		//Utilities.coalesceText(metaXmlDocument); // this makes CDATA-Sections readable
		
		
		structures = new Array();
		steps = new Array();
		extraSteps = new Array();
		
		
		// make an error step
		var errorStep = new CTStep("errorstep", "Normal_Step", "Error", "none", true, false, true, 0, false);
	    var errorMessage = new Text("errormessage", 100, 100, 400, 200, false, true, false, "an error occured!", "scroll");
		errorStep.addContent(errorMessage);	 
		errorStep.loaded = true;   
	    extraSteps.push(errorStep);	    
	    
	    var metaTag = metaXmlDocument.getElementsByTagName("meta")[0];
		var xParser = new XParser(metaXmlDocument);
		
		treeNodes = new Array(); 
		var rootStructure = new Structure("root", "root", 0, "root", 0, "", true, 0, 0, false, false, false, 0, true);
		
		
		structures.push(rootStructure);
		//addStructureTreeNode(rootStructure);
		
		attachStructures(metaTag, xParser, rootStructure);
	    
	    
		for (var i = 0; i < visitedSteps.length; i++) {
			var myStep = findStepById(visitedSteps[i]);
			if (myStep) {
				myStep.visited = true;
			}
		}
	    
		listpageArray["faq"] = createListpageArray("faq");
		listpageArray["help"] = createListpageArray("help");
		listpageArray["glossary"] = createListpageArray("glossary");
		
	}
	
	function attachStructures(node, xParser, currentStructure) {
		var childNodes = node.childNodes;
		var position = 0;
		for (var i = 0; i < childNodes.length; i++) {
			if (childNodes[i].nodeName == "structure" || childNodes[i].nodeName == "step") {
				if (childNodes[i].nodeName == "structure") {
					var myStructure = addStructure(childNodes[i], xParser, position);
					structures.push(myStructure);
					if (currentStructure) {
						currentStructure.addStructure(myStructure);
					}
					var temp = currentStructure;
					
					currentStructure = myStructure;
					position++;
					attachStructures(childNodes[i], xParser, currentStructure);
					currentStructure = temp;
				}
				else {
					var myStep = addStep(childNodes[i], xParser);
					var stepArray = steps; 
        	        
					var structure = getStructureById(myStep.structureId);
        	        if (structure.visible) {
			        	if (myStep.hidden) {
			        		stepArray = extraSteps;
			        	}
			        }
			        else {
			        	stepArray = extraSteps;
			        }
			        stepArray.push(myStep);
			        structure.addStep(myStep);
			        
					
					
				}
				
			}
			
		}
	}
	
	function fixPositions(structure) {
		var children = new Array();
		var position = 1;
		
		for (var i = 0; i < structures.length; i++) {
	    	if (structures[i].parentId == structure.id) {
	    		structures[i].position = position;
	    		children.push(structures[i]);
	    		position++;
	    	}
	    }
	    
	    for (var i = 0; i < children.length; i++) {
	    	fixPositions(children[i]);
	    }
	    	
	}
	
	function getStructuresOfLevel(level) {
		var myStructures = new Array();
		for (var i = 0; i < structures.length; i++) {
	    	if (structures[i].level == level) {
	    		myStructures.push(structures[i]);
	    	}
	    }
	    return myStructures;
	}
	
	function findFirstStepInStructure(structure) {
		var myStep = null;
		var mySteps = structure.getSteps();
		if (mySteps.length > 0) {
			myStep = mySteps[0];
		}
		else {
			var structures = structure.getStructures();
			for (var i = 0; i < structures.length; i++) {
				myStep = findFirstStepInStructure(structures[i]);
				if (myStep) break;
			}
		}
		return myStep;
	}
	
	/*
	function getTreeNodes(tree, rootId) {
		zindex = 1000;
		
		rootId = "3ee64ac6-bb98-47cc-be9f-ee6afd6e9645";
		
		if (!rootId) rootId = "root";
		var myTreeNodes = new Array();
		
		var rootStructure = getStructureById(rootId);
		var diffLevel = rootStructure.level;
		var rootNode = createTreeNode(rootStructure, 0, null, tree.id);
		myTreeNodes.push(rootNode);
		
		myTreeNodes = collectStructures(rootStructure, rootNode, myTreeNodes, tree.id);
		
		for (var i = 0; i < myTreeNodes.length; i++) {
			myTreeNodes[i].setTree(tree);
			var level = myTreeNodes[i].getLevel();
			myTreeNodes[i].setLevel(level - diffLevel);
		}
		
		//alert(myTreeNodes.length);
		return myTreeNodes;
	}
	
	function createTreeNode(structure, position, parentNode, treeId) {
		var treeNode = new TreeNode(treeId + "_" + structure.id, structure.title, structure.level, position, zindex--, parentNode);
		treeNode.setStructure(structure);
		treeNode.setVisited(structure.visited);
		return treeNode;
	}
	
	function collectStructures(structure, rootNode, myTreeNodes, treeId) {
		var children = structure.getStructures();
		for (var i = 0; i < children.length; i++) {
			var treeNode = createTreeNode(children[i], i, rootNode, treeId);
			myTreeNodes.push(treeNode);
			myTreeNodes = collectStructures(children[i], treeNode, myTreeNodes, treeId);
		}
		return myTreeNodes;		
	
	}
	*/
	
	/*
	function getTreeNodes(tree, rootId) {
		var myTreeNodes = new Array();
		
		//alert("get tree nodes, rootId: " + rootId);
		
		if (!rootId) {
			for (var i = 0; i < treeNodes.length; i++) {
				treeNodes[i].setTree(tree);	
				myTreeNodes.push(treeNodes[i]);
				
			}
			return myTreeNodes;
		}
		
		alert(rootId);	
			
		var rootStructure = getStructureById(rootId);
		rootStructure.position = 0;
		rootStructure.type = "root";
		rootStructure.parentId = "";
		
		//var rootStructure = new Structure("", "root", 0, "root", 0, "");
		
		var rootNode = new TreeNode("tree_" + rootId, "root", 0, 0, 0, null);
		rootNode.setTree(tree);
		rootNode.setStructure(rootStructure);
		myTreeNodes.push(rootNode);	
		
		var currentRootId = rootId;
		var add = false;
		for (var i = 0; i < treeNodes.length; i++) {
			if (treeNodes[i].parent && (treeNodes[i].parent.id == "tree_" + rootId || treeNodes[i].parent.id == "tree_" + currentRootId)) {
				add = true;
			}
			else {
				add = false;
			}
			if (add) {
				var newNode = new TreeNode(treeNodes[i].id, treeNodes[i].title, treeNodes[i].level - rootStructure.level, treeNodes[i].position, treeNodes[i].zindex, rootNode);
				newNode.setTree(tree);
				currentRootId = treeNodes[i].getStructure().id;
				myTreeNodes.push(newNode);
			}
		}
		
		//alert("lenght: " + myTreeNodes.length);
		return myTreeNodes;
	}
	*/
	
	function addStructure(node, xParser, position) {
		var title = node.getElementsByTagName("title")[0].firstChild.nodeValue;
		
		var id = node.getAttribute("id");
		var type = node.getAttribute("type");	
		var parentId = node.getAttribute("structureId");
		var level = parseInt(node.getAttribute("level"));
        var backwardEnabled = node.getAttribute("backwardEnabled") == "true";
        var timeLimit = parseInt(node.getAttribute("timeLimit"));
        var timeLimitAlert = parseInt(node.getAttribute("timeLimitAlert"));
        var hideNavigationTree = node.getAttribute("hideNavigationTree") == "true";
        var mixQuestions = false;
        var mixAnswers = node.getAttribute("mixAnswers") == "true";
        var numberOfQuestions = node.getAttribute("numberOfQuestions");
        var visible = node.getAttribute("visible") == "true";
           
        var structure = new Structure(id, title, position, type, level, parentId, backwardEnabled, timeLimit, timeLimitAlert, hideNavigationTree, mixQuestions, mixAnswers, numberOfQuestions, visible);
	   	//structures.push(structure);
	   	
	   	return structure;
	}
	
	function addStep(stepNode, xParser) {
		var title = stepNode.getElementsByTagName("title")[0].firstChild.nodeValue;
        var id = stepNode.getAttribute("id");
        var type = stepNode.getAttribute("type");
        var structureId = stepNode.getAttribute("structureId");
        var mandatory = stepNode.getAttribute("mandatory") == "true";
        var excluded = stepNode.getAttribute("excluded") == "true";
        var hidden = stepNode.getAttribute("hidden") == "true";
        var mixAnswers = stepNode.getAttribute("mixAnswers") == "true";
        var tries = parseInt(stepNode.getAttribute("tries"));
        var isSingleStep = false;
        if (title.indexOf("#") == 0) {
        	isSingleStep = true;
        }
        
        var step = new CTStep(id, type, title, structureId, mandatory, excluded, hidden, tries, mixAnswers);
       	step.singleStep = isSingleStep;
       	
       	
        if (type == "Score") {
        	var result = xParser.evaluate("evaluate", stepNode);
        	
        	for (var n = 0; n < result.length; n++) {
        		var id = result[n].firstChild.nodeValue;
        		step.addEvaluation(id);
        	}
        	
        }
        /*
        var structure = getStructureById(structureId);
        
        if (structure.visible) {
        	if (step.hidden) {
        		extraSteps.push(step);
        	}
        	else {
        		steps.push(step);
        	}
        }
        else {
        	extraSteps.push(step);
        }
        
        structure.addStep(step);
        */
        return step;
	}
	
	
		
	function addStructureTreeNode(structure, position) {
		var treeNode = new TreeNode("mytree_" + structure.id, structure.title, structure.level, position, zindex--, getNode(structure.parentId));
		treeNode.setStructure(structure);
		treeNode.setVisited(structure.visited);
		treeNodes.push(treeNode);
		return treeNode;
	}
	
	function addStepTreeNode(step, parent, position) {
		var title = step.title;
		var treeNode = new TreeNode("mytree_" + step.id, title, parent.level + 1, position, zindex--, getNode(parent.id));
		treeNode.setStructure(step);
		treeNode.setVisited(step.visited);
		treeNodes.push(treeNode);
		return treeNode;
	}
	
	function getNode(id) {
		var myNode = null;
		var length = treeNodes.length;
		var id = "mytree_" + id;
		for (var i = 0; i < length; i++) {
			if (treeNodes[i].id == id) {
				myNode = treeNodes[i];
				break;
			}
		}
		return myNode;
	}
	
	
	
	
	function getErrorStep(message) {
		var errorStep = getStepById("errorstep");
		var errorMessage = errorStep.getContentById("errormessage");
		if (errorMessage) {
			errorMessage.value = message;
		}
		return errorStep;
	}
	
	function createListpageArray(type) {
		var listpageXmlDocument = null;
		var array = new Array();
		listpageXmlDocument = utils.getXmlDocument("data/" + type + ".xml");
		if (listpageXmlDocument != null) {
			//Utilities.coalesceText(listpageXmlDocument); // this makes CDATA-Sections readable
			var allEntries = listpageXmlDocument.getElementsByTagName("entry");
		    for (var i = 0; i < allEntries.length; i++) {
				var id = allEntries[i].getAttribute("id");
	            if (allEntries[i].getElementsByTagName("key")[0].firstChild) {
					var key = allEntries[i].getElementsByTagName("key")[0].firstChild.nodeValue + "";
					var value = allEntries[i].getElementsByTagName("value")[0].firstChild.nodeValue + "";
					var entry = new ListpageEntry(id, key, value, false);
					array.push(entry);
				}
			}
		}
		return array;
	}
	
	function getStructures() {
		return structures;
	}
	
	function getStructureById(id) {
	    var structure = null;
	    var length = structures.length;
	    for (var i = 0; i < length; i++) {
	        if (structures[i].id + "" == id + "") {
	            structure = structures[i];
	            break;
	        }
	    }
	    return structure;
	}
	
	
	function findStepById(id) {
	    var myStep = null;
	    var stepsLength = steps.length;
	    for (var i = 0; i < stepsLength; i++) {
	        if (steps[i].id == id) {
	            myStep = steps[i];
	            break;
	        }
	    }
	    if (!myStep) {
	    	stepsLength = extraSteps.length;
	    	for (var i = 0; i < stepsLength; i++) {
		        if (extraSteps[i].id == id) {
		            myStep = extraSteps[i];
		            break;
		        }
	    	}
	    }
	    	    
	    return myStep;
	}
	
	
	function getStepById(id) {
	    var myStep = findStepById(id);
	    if (!myStep) return null;
	    
	    if (!myStep.loaded) {
	    	myStep.load();
	    }
	    return myStep;
	}
	
	function getStepByTitle(title) {
		var title = title.toLowerCase();
		var myStep = null;
	    var stepsLength = steps.length;
	    for (var i = 0; i < stepsLength; i++) {
	        if (steps[i].title == title) {
	            myStep = steps[i];
	            break;
	        }
	    }
	    if (!myStep) {
	    	stepsLength = extraSteps.length;
	    	for (var i = 0; i < stepsLength; i++) {
		        if (extraSteps[i].title.toLowerCase() == title) {
		            myStep = extraSteps[i];
		            break;
		        }
	    	}
	    }
	    
	    if (!myStep) return null;
	    
	    if (!myStep.loaded) {
	    	myStep.load();
	    }
	    return myStep;
	}
		
	function getContentById(id) {
	    var myContent = null;
	    var stepsLength = steps.length;
	    for (var i = 0; i < stepsLength; i++) {
	        myContent = steps[i].getContentById(id);
	        if (myContent != null) break;
	    }
	    if (myContent == null) {
	    	stepsLength = extraSteps.length;
		    for (var i = 0; i < stepsLength; i++) {
		        myContent = extraSteps[i].getContentById(id);
		        if (myContent != null) break;
		    }
	    }
	    return myContent;
	}
	
	function getSteps() {
		return steps;
	}
	
	function setSteps(_steps) {
		steps = _steps;
	}
			
	function getMetaXml() {
		return metaXmlDocument;
	}
	
	function getProjectTitle() {
	    var title = metaXmlDocument.getElementsByTagName("project-title")[0].firstChild.nodeValue;
		return title;
	}
	
	function getGlobalProperty(name) {
	    return null;
	}
	
	function getListpageEntries(type) {
		return listpageArray[type];
	}
	
	function getListpageEntry(id, type) {
		var myArray = listpageArray[type];
		var myEntry = null;
		var length = myArray.length;
		for (var i = 0; i < length; i++) {
			var entry = myArray[i];
			if (entry.id == id) {
				myEntry = entry;
				break;
			}
		}
		return myEntry;
	}
	
	function shutdown() {
		visitedSteps = new Array();
		for (var i = 0; i < steps.length; i++) {
			if (steps[i].visited) {
				visitedSteps.push(steps[i].id);
			}
		}
		if (standardapi) {
			standardapi.setUserData("visited-locations", visitedSteps + "");
		}
	}
    
}

