/*global tov_config, Calculator, Project, DOMParser, window, ActiveXObject, DataLoadNonPassedReasons  */

var DataLoadNonPassedReasons = {
	"CalculationRateChanged": 0,
	"GroupDeleted": 1
};

function XMLBuffer()
{
    var pretty_print = false;
	var xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
	var element_stack = [ ];
	
	var attribute_parser = function(attributes) {
		var a_str = "";
		for (var key in attributes) {
			if(attributes.hasOwnProperty(key) === true) {
				a_str += " " + key + "=\""+ attributes[key] +"\"";
			}
		}
		return a_str;
	};
	
	this.begin = function(tag, attributes) {
		this.write("<" + tag + attribute_parser(attributes) +">");
		element_stack.push(tag);
	};
	
	this.end = function() {
		var tag = element_stack.pop();
		this.write("</" + tag + ">");
	};
	
	this.add = function(tag, value, attributes) {
		var a_str = attribute_parser(attributes);
		if (value === null) {
			this.write("<" + tag + a_str +" />");
		} else {
			this.write("<" + tag + a_str +">"+ value +"</"+ tag +">");
		}
	};
	
	this.write = function(code) {
	    var indents = "";
	    if (pretty_print) {
		    for (var i = 0; i < element_stack.length; i++) {
			    indents += "\t";
		    }
	    }
		xml += indents + code;
		if (pretty_print) xml += "\n";
	};
	
	this.get_xml = function() {
		return xml;
	};
}

function TreeRawOutput(project) {
	var xml_buffer = new XMLBuffer();

	var render_setting = function(obj, setting_id) {
		var setting = obj.get_setting(setting_id);

		xml_buffer.begin("setting", {"name": setting_id});
			// xml_buffer.add("name", setting_id);
			xml_buffer.add("value", setting.get_active_value());
		
			xml_buffer.add("type", setting.get_type());
			xml_buffer.add("label", "<![CDATA[" + setting.get_label() + "]]>");
			
			xml_buffer.begin("labels");
				var labels = setting.get_labels();
				for (var key in labels) {
					if(labels.hasOwnProperty(key) === true) {
						xml_buffer.add("label", "<![CDATA[" + labels[key] + "]]>");
					}
				}
			xml_buffer.end();
		
		xml_buffer.end();
	};
	
	var render_settings = function(obj) {
		xml_buffer.begin("setting_groups");
			var groups = obj.get_setting_groups();
			for (var group_key in groups) {
				if(groups.hasOwnProperty(group_key) === true) {
					xml_buffer.begin("group");
						xml_buffer.add("label", "<![CDATA[" + groups[group_key].label + "]]>");
					
						xml_buffer.begin("settings");
							var settings = groups[group_key].settings;
							
							for (var setting_key in settings) {
								if(settings.hasOwnProperty(setting_key) === true) {
									xml_buffer.add("setting", null, {name: settings[setting_key]});
								}
							}
						xml_buffer.end();
					xml_buffer.end();
				}
			}
		xml_buffer.end();
		
		xml_buffer.begin("settings");
			var all_settings = obj.get_local_settings();
			for (var key in all_settings) { 
				if(all_settings.hasOwnProperty(key) === true) {
					render_setting(obj, key); 
				}
			}
		xml_buffer.end();
	};
	
	var render_subconstruction = function(subconstruction, index, result) {
		if(result === undefined || result.costs === undefined || result.lifetime === undefined) { return; }
		xml_buffer.begin("subconstruction", {id: index});
			xml_buffer.add("name", subconstruction.get_name());

			xml_buffer.add("cost", round_cost(subconstruction.get_cost()));
			
			xml_buffer.add("lifetime_correction", subconstruction.get_lifetime_correction());
			xml_buffer.add("lifetime_calculated", result.lifetime.calculated);
			xml_buffer.add("lifetime_corrected", result.lifetime.corrected);
			xml_buffer.add("lifetime_standard", result.lifetime.standard);
			
			xml_buffer.add("total_cost", round_cost(result.costs.total_cost));
			
			xml_buffer.add("work_cost", round_cost(subconstruction.get_work_cost()));
			
			xml_buffer.add("maintainance_percent_correction",
						   round_percent(subconstruction.get_maintainance_percent_correction()));
			xml_buffer.add("maintainance_percent_corrected",
						round_percent(result.costs.maintainance_corrected));
			xml_buffer.add("maintainance_cost",
						round_cost(result.costs.maintainance_cost));
			xml_buffer.add("maintainance_percent_default",
						round_percent(result.costs.maintainance_default));
				
			xml_buffer.add("element", subconstruction.get_active_element_id(), 
				{label: subconstruction.get_active_element().name} );
		xml_buffer.end();
	};
	
	var render_construction = function(construction) {
		var result = { others: [] };
		var calculator = new Calculator(construction);
		result = calculator.calculate_construction();
		
		if(result === undefined) { return; }
		xml_buffer.begin("construction", {"id": construction.get_id()});			
			xml_buffer.add("name", construction.get_name());
			xml_buffer.add("cost", round_cost(construction.get_cost()));
			
			xml_buffer.add("lifetime_correction", construction.get_lifetime_correction());
			xml_buffer.add("lifetime_calculated", result.main.lifetime.calculated);
			xml_buffer.add("lifetime_corrected", result.main.lifetime.corrected);
			xml_buffer.add("lifetime_standard", result.main.lifetime.standard);
			
			xml_buffer.add("total_cost", round_cost(result.main.total_current_cost));
			
			xml_buffer.add("extra_cost", round_percent(construction.get_extra_cost()));
		    
		    if (construction.should_show_comment())
				xml_buffer.add("comment", "<![CDATA[" + construction.get_comment() + "]]>");
		    else
				xml_buffer.add("comment", "");
			
			xml_buffer.add("yearly_cost", round_cost(result.main.yearly_cost));
			
			xml_buffer.begin("subconstructions");
				var subconstructions = construction.get_subconstructions();
				for (var subcon_key in subconstructions) {
					if(subconstructions.hasOwnProperty(subcon_key) === true) {
						render_subconstruction(subconstructions[subcon_key], subcon_key,
											   result.others[subcon_key]);
					}
				}
			xml_buffer.end();
		xml_buffer.end();
	};

	// TODO: Edit the namespace
	xml_buffer.begin("project", {"xmlns": "http://bo3.traxim.dk", "version": "1.0"});
	
		// Informations
		xml_buffer.begin("informations");
			var fields = project.export_general_information();
			for (var key in fields) {
				if(fields.hasOwnProperty(key) === true) {
					xml_buffer.add("information", fields[key].value, {id:fields[key].id});
				}
			}
		xml_buffer.end();
		
		render_settings(project);
	
		xml_buffer.begin("groups");
			var groups = project.list_groups();
			for (var group_key in groups) {
				if(groups.hasOwnProperty(group_key) === true) {
					var group = project.get_group(groups[group_key].id);
					if (group.get_constructions().length > 0) {
						// Settings
						xml_buffer.begin("group");
							xml_buffer.add("id", group.get_id());
						    xml_buffer.add("name", group.get_name());
							
							render_settings(group);
						
							// Constructions
							xml_buffer.begin("constructions");
								var constructions = group.get_constructions();
								for (var con_key in constructions) {
									if(constructions.hasOwnProperty(con_key) === true) {
										render_construction(constructions[con_key], con_key);
									}
								}
							xml_buffer.end();
					
							// Construction comparison
							var comp = new Comparison(group).analyze();
							if (comp != undefined) {							
								xml_buffer.begin("comparison");
										xml_buffer.add("min", round_cost(comp.constructions.difference.min.cost), {"construction":comp.constructions.difference.min.construction.get_id()});
										xml_buffer.add("max", round_cost(comp.constructions.difference.max.cost), {"construction":comp.constructions.difference.max.construction.get_id()});
										xml_buffer.add("shortest_lifetime", comp.constructions.shortest_lifetime);
										xml_buffer.add("nowvalue", round_cost(comp.nowvalue));
								xml_buffer.end();
							}
						
						xml_buffer.end();
					}
				}
			}
		xml_buffer.end();
	
	xml_buffer.end();

	return xml_buffer.get_xml();
}

/**
* Load xml and return a new project based on the xml.
* @param xml string.
* @return a project object.
*/
function XMLLoader(xml) {
	var xml_to_dom = function() {
		var parser = undefined;
		if (window.DOMParser) {
			parser = new DOMParser();
			return parser.parseFromString(xml, "text/xml");
		} else {
			parser = new ActiveXObject("Microsoft.XMLDOM");
			parser.async = "false";
			parser.loadXML(xml);
			return parser;
		}
	};
	
	/**
	* Returns the direct elemens under head with a given tag name.
	*/
	var d_elements_by_tag_name = function(dom, head) {
		var elements = [ ];
		
		var child_nodes = dom.childNodes;
		for (var i = 0; i < child_nodes.length; i++) {
			if (child_nodes[i].nodeType === 1 && child_nodes[i].tagName === head) {
				elements.push(child_nodes[i]);
			}
		}
		return elements;
	};
	
	var get_first_child = function(dom, tag_name) {
		return d_elements_by_tag_name(dom, tag_name)[0];
	};
	
	var get_element_content = function(element) {
		if (element.textContent != undefined) return element.textContent;
		else return element.text;
	};
	
	var load_settings = function(target, setting_dom) {
		var settings = d_elements_by_tag_name(get_first_child(setting_dom, "settings"), "setting");

		for (var i = 0; i < settings.length; i++) {
			// var name = get_first_child(settings[i], "name").textContent;
			var name = settings[i].getAttribute("name");
			
			var value = get_element_content(get_first_child(settings[i], "value"));
			var type = get_element_content(get_first_child(settings[i], "type"));
			
			try {
				if (type != "static")
					target.get_setting(name).set_active_value(parseInt(value));
				else
					target.get_setting(name).set_active_value(parseFloat(value));
			} catch (e) { }
		}
	};
	
	var dom = xml_to_dom();
	var project_tag = get_first_child(dom, "project");
	if (project_tag == undefined)
		throw "Project tag not found.";

	var project = new Project(tov_config);
	var new_calculation_rate = project.get_setting("calculation_rate").get_active_value();	

	// General case information
	var information = d_elements_by_tag_name(get_first_child(project_tag, "informations"),
											"information");
	for (var key = 0; key < information.length; key++) {
		var element = information[key];
		project.set_general_information(element.attributes.getNamedItem("id").nodeValue,
										get_element_content(element));
	}
	
	// Global settings
	load_settings(project, project_tag);
	
	// Groups
	var all_created = true;
	var groups = d_elements_by_tag_name(get_first_child(project_tag, "groups"), "group");
	for (var i = 0; i < groups.length; i++) {
		var group_dom = groups[i];
		// Create group
		var group_id = get_element_content(get_first_child(group_dom, "id"));
		if (!project.has_group(group_id)) {
			all_created = false;
			continue;
		}

		var group = project.get_group(group_id);
		
		// Settings
		load_settings(group, group_dom);
		
		// Constructions
		var constructions = d_elements_by_tag_name(get_first_child(group_dom, "constructions"),
												"construction");
		for (var j = 0; j < constructions.length; j++) {
			var construction_dom = constructions[j];
			
			var construction = undefined;
			var construction_id = construction_dom.attributes.getNamedItem("id").nodeValue;
			construction = group.add_construction(construction_id);

			construction.set_name(get_element_content(get_first_child(construction_dom, "name")));
						
			construction.set_cost(parseInt(get_element_content(get_first_child(construction_dom, "cost")), 10));
			construction.set_extra_cost(
				parseFloat(get_element_content(get_first_child(construction_dom, "extra_cost"))) );
			
			construction.set_comment(get_element_content(get_first_child(construction_dom, "comment")));
			
			var calculator = new Calculator(construction);
			
			var subconstructions = construction.get_subconstructions();
			var subconstructions_tags = d_elements_by_tag_name(
											get_first_child(construction_dom, "subconstructions"),
											"subconstruction");
			for (var k = 0; k < subconstructions_tags.length; k++) {
				var subconstruction_dom = subconstructions_tags[k];
				
				var id = subconstruction_dom.attributes.getNamedItem("id").nodeValue;
				var subconstruction = subconstructions[id];

				subconstruction.set_cost(parseInt(
					get_element_content(get_first_child(subconstruction_dom, "cost")), 10));
				subconstruction.set_work_cost(
					parseInt(get_element_content(get_first_child(subconstruction_dom, "work_cost")), 10));
				subconstruction.set_active_element(
					parseInt(get_element_content(get_first_child(subconstruction_dom, "element")), 10));

				subconstruction.set_name(get_element_content(get_first_child(subconstruction_dom, "name")));
				
				// Lifetime + maintainance correction
				desired_lifetime = parseInt(get_element_content(get_first_child(subconstruction_dom,
						"lifetime_corrected")), 10);
				actual_lifetime = undefined;
				try { actual_lifetime = calculator.calculate_single_lifetime(subconstruction).calculated; } catch(e2) {}
				if (actual_lifetime === undefined) { actual_lifetime = 0; }
				
				if (desired_lifetime != actual_lifetime) {
					var correction_lifetime = desired_lifetime - actual_lifetime;
					subconstruction.set_lifetime_correction(correction_lifetime);
					var previous_correction_lifetime = parseInt(
						get_element_content(get_first_child(subconstruction_dom, "lifetime_correction")), 10);
					if (correction_lifetime != previous_correction_lifetime) {
						construction.set_comment(construction.get_comment() +
												 "\nLevetidskorrektion ænret fra "+ previous_correction_lifetime
												 +" til "+ correction_lifetime +".");
					}
				}
				
				var desired_maintainance_percent = parseFloat(get_element_content(get_first_child(subconstruction_dom,
					"maintainance_percent_corrected")));
				var actual_maintainance_percent =
					subconstruction.get_data_facts().std_maintainance_percent;

				if (desired_maintainance_percent != actual_maintainance_percent) {
					var maintainance_correction = round_percent(desired_maintainance_percent -
																actual_maintainance_percent);
					subconstruction.set_maintainance_percent_correction(maintainance_correction);
					var previous_maintainance_correction = parseFloat(get_element_content(get_first_child(
																		  subconstruction_dom,
																		  "maintainance_percent_correction")));
					if (maintainance_correction != previous_maintainance_correction) {
						construction.set_comment(construction.get_comment() +
												 "\nVedligeholdelses korrektion ændret fra "+
												 previous_maintainance_correction +" til " +
												 maintainance_correction + ".");
					}
				}
			}
			
			// Find lifetime correction
			var desired_lifetime = parseInt(
						get_element_content(get_first_child(construction_dom, "lifetime_corrected")), 10);
			
			var actual_lifetime = undefined;
			try { actual_lifetime = calculator.calculate_single_lifetime(construction).calculated; } catch(e1) {}
			if (actual_lifetime === undefined) { actual_lifetime = 0; }
			
			if (desired_lifetime != actual_lifetime) {
				var correction_lifetime = desired_lifetime - actual_lifetime;
				construction.set_lifetime_correction(correction_lifetime);
				var previous_correction_lifetime = parseInt(
					get_element_content(get_first_child(construction_dom, "lifetime_correction")), 10);
				if (correction_lifetime != previous_correction_lifetime) {
					construction.set_comment(construction.get_comment() +
											 "\nLevetidskorrektion ændret fra "+ previous_correction_lifetime
											 +" til "+ correction_lifetime +".");
				}
			}
		}
	}
	
	// Calculation rate correction / old rate
	var ret = {
		"passed": true,
		"reasons": [ ],
		"project": project
	};
	
	var current_calculation_rate = project.get_setting("calculation_rate").get_active_value();
	if (new_calculation_rate != current_calculation_rate) {
		ret.passed = false;
		ret.reasons.push(DataLoadNonPassedReasons.CalculationRateChanged);
		ret.new_calculation_rate = new_calculation_rate;
		ret.current_calculation_rate = current_calculation_rate;
	}
	if (all_created != true) {
		ret.reasons.push(DataLoadNonPassedReasons.GroupDeleted);
	}
	
	return ret;
};

function round_percent(percent) {
	return Math.round(parseFloat(percent) * 1000) / 1000;
}

function round_cost(cost) {
	return Math.round(parseInt(cost));
}

