#include"WalkTree.h"
#include <cctype>

//Note: Overloaded functions share the same name but are distinguished by the number and type of their parameters. 
//Note: The compiler chooses the correct version to call based on the arguments you pass.

//Tree::WalkTree(Node * pParent, unordered_map<string, double> &dict)
//Note: Takes 2 arguments, 2nd an unordered_map double
//Note: Walks and fills a map of doubles
void Tree::WalkTree(Node * pParent, unordered_map<string, double> &dict)
{ 
	if (pParent->getNodeName() == "DISABLE") {
		return;
	}
	nodes.push_back(pParent);
	Node * pChild;
	for (int i = 0; i < (pParent->CountChildren()); i++) {
		pChild = pParent->NextUnmarkedChild();
		//if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")); function checks all cases
		if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")) {
			continue;
		}
		if (pChild != 0) {
			pChild->isVisited() = true;
			WalkTree(pChild, dict);
		}
		//If (pChild->getNodetype() == "TextNode"); Checking if Node is a text node type, with string characters
		//Note: Node("Area_m2") followed by Node("136900", nodetype="TextNode")
		if (pChild->getNodetype() == "TextNode") {
			string key = pParent->getNodeName();
			double value = atof(pChild->getNodeName().c_str());
			dict.insert(pair<string, double>(key, value));
		}
	}
}

//Tree::WalkTree(Node * pParent, unordered_map<string, string> &dict)
//Note: Takes 2 arguments, 2nd an unordered_map string
//Note: Walks and fills a map of strings
void Tree::WalkTree(Node * pParent, unordered_map<string, string> &dict)
{
	//if (equalsIgnoreCase(pParent->getNodeName(), "DISABLE")); function checks all cases
	if (equalsIgnoreCase(pParent->getNodeName(), "DISABLE")) {
		return;
	}
	nodes.push_back(pParent);
	Node * pChild;
	for (int i = 0; i < (pParent->CountChildren()); i++) {
		pChild = pParent->NextUnmarkedChild();

		//if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")); function checks all cases
		if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")) {
			continue;
		}
		if (pChild != 0) {
			pChild->isVisited() = true;
			WalkTree(pChild, dict);
		}

		//If (pChild->getNodetype() == "TextNode"); Checking if Node is a text node type, with string characters
		//Note: Node("Area_m2") followed by Node("136900", nodetype="TextNode")
		if (pChild->getNodetype() == "TextNode") {
			string key = pParent->getNodeName();
			string value = pChild->getNodeName();
			value.erase(0, 1);
			dict.insert(pair<string, string>(key, value));
		}
	}
}

//Tree::WalkTree(Node* pParent, unordered_map<string, double>& dict, unordered_map<string, string>& strdicts)
//Note: Takes 2 arguments, 2nd and 3rd unordered_map double and string
//Note: Walks and fills both maps with doubles and vectors at once
void Tree::WalkTree(Node* pParent, unordered_map<string, double>& dict, unordered_map<string, string>& strdicts)
{
	//if (equalsIgnoreCase(pParent->getNodeName(), "DISABLE")); function checks all cases
	if (equalsIgnoreCase(pParent->getNodeName(), "DISABLE")) {
		return;
	}

	nodes.push_back(pParent);
	Node* pChild;

	for (int i = 0; i < pParent->CountChildren(); i++) {
		pChild = pParent->NextUnmarkedChild();
		//if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")); function checks all cases
		if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")) {
			continue;
		}

		if (pChild != nullptr) {
			pChild->isVisited() = true;
			WalkTree(pChild, dict, strdicts);
		}
		//If (pChild->getNodetype() == "TextNode"); Checking if Node is a text node type, with string characters
		//Note: Node("Area_m2") followed by Node("136900", nodetype="TextNode")
		if (pChild->getNodetype() == "TextNode") {
			string key = pParent->getNodeName();
			string rawValue = pChild->getNodeName();

			// Try to parse as double
			char* endptr = nullptr;
			double numValue = strtod(rawValue.c_str(), &endptr);

			// Full parse = valid double
			if (endptr != nullptr && *endptr == '\0') {
				dict.insert({ key, numValue });
			}
			else {
				// Handle as string, optionally remove leading character if needed
				// Remove leading character if it is not part of the value
				if (!rawValue.empty()) {
					rawValue.erase(0, 1);  // If this was originally for some formatting convention
				}
				strdicts.insert({ key, rawValue });
			}
		}
	}
}


//Tree::WalkTree(Node * pParent, vector<vector<unordered_map<string, double> > > &vofomaps, vector<vector<unordered_map<string, string> > > &vofomapstring)
//Note: Takes 3 arguments, 2nd and 3rd row col unordered_map of double and string respectively
//Note: Walks and fills both 2D vectors of maps with doubles and vectors at once
void Tree::WalkTree(Node * pParent, vector<vector<unordered_map<string, double> > > &vofomaps, vector<vector<unordered_map<string, string> > > &vofomapstring)
{
	//if (equalsIgnoreCase(pParent->getNodeName(), "DISABLE")); function checks all cases
	if (equalsIgnoreCase(pParent->getNodeName(), "DISABLE")) {
		return;
	}
	nodes.push_back(pParent);
	Node* pChild;
	Node* pGrandChild;

	for (int i = 0; i < pParent->CountChildren(); i++) {
		pChild = pParent->GetChild(i);
		//if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")); function checks all cases
		if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")) {
			continue;
		}

		vector<unordered_map<string, double>> temp;
		vector<unordered_map<string, string>> tempS;

		for (int j = 0; j < pChild->CountChildren(); ++j) {
			pGrandChild = pChild->GetChild(j);
			//if (equalsIgnoreCase(pGrandChild->getNodeName(), "DISABLE")); function checks all cases
			if (equalsIgnoreCase(pGrandChild->getNodeName(), "DISABLE")) {
				continue;
			}

			unordered_map<string, double> tDict;
			unordered_map<string, string> sDict;
			WalkTree(pGrandChild, tDict, sDict);
			temp.push_back(tDict);
			tempS.push_back(sDict);
		}

		vofomaps.push_back(temp);
		vofomapstring.push_back(tempS);
	}
}

//Tree::WalkTree(Node * pParent)
//Note: Takes 1 argument
//Note: Traverses tree without filling doubles or vectors
void Tree::WalkTree(Node * pParent)
{
	//if (equalsIgnoreCase(pParent->getNodeName(), "DISABLE")); function checks all cases
	if (equalsIgnoreCase(pParent->getNodeName(), "DISABLE")) {
		return;
	}
	nodes.push_back(pParent);
	Node * pChild;
	for (int i = 0; i < (pParent->CountChildren()); i++) {
		pChild = pParent->NextUnmarkedChild();
		//if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")); function checks all cases
		if (equalsIgnoreCase(pChild->getNodeName(), "DISABLE")) {
			continue;
		}
		if (pChild != 0) {
			pChild->isVisited() = true;
			WalkTree(pChild);
		}
	}
}

//------------------------<returns a vector of nodes for elements with given tag name>---------------
vector<Node *> Tree::GetElementByTagName(string Element)
{
	ClearMarks();
	ClearNodes();
	Elements.clear();
	WalkTree(this->GetRoot());
	vector<Node *>::iterator itr = nodes.begin();
	int i = 0;
	while (itr != nodes.end()) {
		if (nodes[i]->getNodeName() == Element && nodes[i]->getNodetype() == "Element")
			Elements.push_back(nodes[i]);
		itr++;                 // itr is used to iterate as using i will generate warning
		i++;                   // i is used to access element
	}
	if (Elements.size() == 0) {
		cout << "No Element with the specified tag name can be found in the tree" << endl;
	}
	return this->Elements;
}

//-------------------<Removes All Children for the given parent>---------------------------------- 
void Tree::RemoveChild(Node *pParent)
{
	while (pParent->CountChildren() != 0) {
		delete pParent->GetChild(0);
		pParent->RemoveChild(0);
	}
}

//--------------------<Display Complete tree starting from parent, uses Node's DisplayNode()>-------------
void Tree::DisplayTree(Node * pParent)
{
	ClearMarks();
	ClearNodes();
	WalkTree(pParent);
	vector<Node *>::iterator itr = nodes.begin();
	int i = 0;
	while (itr != nodes.end()) {
		if (nodes[i]->getNodetype() == "Element")   // comment this to display FULL TREE including document, comments as DFS traversal
			nodes[i]->DisplayNode();
		i++;
		itr++;
	}
	ClearMarks();
}

//--------<Clear marks from every node, in the tree, as marking scheme is used for traversal>-----------------
void Tree::ClearMarks()
{
	vector<Node *>::iterator itr = nodes.begin();
	int i = 0;
	while (itr != nodes.end()) {
		nodes[i]->isVisited() = false;
		itr++;
		i++;
	}
}

//--------<Clear the nodes, private member of tree>-----------------------------------------
void Tree::ClearNodes()
{
	nodes.clear();
}

//-------------------------<Sets the root of the tree>---------------------------------------
void Tree::setRoot(Node * root)
{
	this->root = root;
}

//-------------------------<returns the root of a tree>-------------------------------------
Node * Tree::GetRoot()
{
	if (this->root != NULL) {
		return this->root;
	}
	else {
		cout << "Root is set to NULL" << endl;
		return 0;
	}
}

//-------------------------<removes the element with the specified tag name--------------------
void Tree::RemoveElement(string TagName)
{
	Tree::GetElementByTagName(TagName);
	vector<Node *>::iterator itr = Elements.end();
	while (itr != Elements.begin()) {
		itr--;
		if ((*itr) != NULL) {
			Tree::Remove(*itr);
		}
	}
	Elements.clear();
}

//--------<Removes the node, from the heap using the Node destructor, uses RemoveChild()>----------------
void Tree::Remove(Node* pChild)
{
	Tree::ClearMarks();
	Tree::ClearNodes();
	Tree::WalkTree(this->GetRoot());
	vector<Node *>::iterator itr = nodes.begin();
	int i = 0;
	while (itr != nodes.end()) {
		for (int j = 0; j<nodes[i]->CountChildren(); j++) {
			if (nodes[i]->GetChild(j) == pChild) {
				delete nodes[i]->GetChild(j);
				nodes[i]->RemoveChild(j);
				break;
			}
		}
		i++;
		itr++;
	}
}

//--------------------------<returns the node ptr to the element with specified id>------------------
Node * Tree::GetElementById(string value)
{
	value = "\"" + value + "\"";
	Tree::WalkTree(this->GetRoot());
	vector<Node *>::iterator itr = nodes.begin();
	int i = 0;
	while (itr != nodes.end()) {
		for (int j = 0; j<nodes [i]->CountAttributes(); j++) {
			if (((nodes[i]->GetAttributes(j)).first == "id") || ((nodes[i]->GetAttributes(j)).first == "ID") || ((nodes[i]->GetAttributes(j)).first == "Id"))
			{
				if ((nodes[i]->GetAttributes(j)).second == value) {
					this->NodeWithId = nodes[i];
					ClearMarks();
					ClearNodes();
					return NodeWithId;
				}
			}
		}
		i++;
		itr++;
	}
	return 0;
}

//-----------------------------<Adds the child to the parent Node ,and sets nodetype as Element>------
void Tree::AddChild(string TagName, Node *parent)
{
	try {
		if (parent != 0) {
			Node *NewChild = new Node("Element", TagName);
			parent->AddChild(NewChild);
		}
		else {
			cout << "Unable to process the null parent node" << endl;
		}
	}
	catch (exception e) {
		cout << e.what() << endl;
	}
}

//equalsIgnoreCase(const string& a, const string& b); helper function to compare two strings case-insensitively
//Note: Only transforms local copy of element, and not actual element used later in model 
bool Tree::equalsIgnoreCase(const string& a, const string& b) {
	string a_upper = a;
	string b_upper = b;
	transform(a_upper.begin(), a_upper.end(), a_upper.begin(), ::toupper);
	transform(b_upper.begin(), b_upper.end(), b_upper.begin(), ::toupper);
	return a_upper == b_upper;
}
