/*
** SellaNMS
** $Id: confignode.cxx,v 1.3 2005/08/05 22:05:20 sella Exp $
** Copyright (c) 2001-2005 Digital Genesis Software, LLC. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
*/

static char rcsid[] = "$Id: confignode.cxx,v 1.3 2005/08/05 22:05:20 sella Exp $";

#include "confignode.h"
#include "util.h"
#include <errno.h>
#include <stdio.h>
#include <stl/string>

#undef _GLIBCPP_CONCEPT_CHECKS

ConfigNode::ConfigNode() {
	pthread_mutex_init(&configMutex, NULL);
}

ConfigNode::ConfigNode(ConfigNode const &src) {
	pthread_mutex_init(&configMutex, NULL);
	*this = src;
}

ConfigNode::~ConfigNode() {
	clear();

	while (EBUSY == pthread_mutex_destroy(&configMutex)) {
		LMSG("ConfigNode mutex is locked. Waiting for unlock.");
		usleep(USLEEP_NORMLOOP_TIME);
	}
}

void ConfigNode::clear() {
	pthread_mutex_lock(&configMutex);

	this->nodes.clear();

	pthread_mutex_unlock(&configMutex);
}

bool ConfigNode::empty() {
	return this->nodes.empty();
}

void ConfigNode::print(unsigned int depth, bool flag) {
	node_iter_t it;

	pthread_mutex_lock(&configMutex);

	for (it = nodes.begin(); it != nodes.end(); ++it) {
		for (unsigned i = 0; i < depth && flag != true; i++) {
			printf("   ");
		}
		if (it->second.nodes.size() > 1) {
			printf("%s {\n", it->first.c_str());
			it->second.print(depth + 1);
			for (unsigned i = 0; i < depth; i++) {
				printf("   ");
			}
			printf("}\n");
		} else if (it->second.nodes.size() == 1) {
			printf("%s ", it->first.c_str());
			it->second.print(depth, true);
		} else {
			printf("%s;\n", it->first.c_str());
		}
	}

	pthread_mutex_unlock(&configMutex);
}

RESULT ConfigNode::addNode(const string key, const ConfigNode* node) {
	RESULT result = SUCCESS;
	pair<string, ConfigNode> node_pair(key, *node);

	pthread_mutex_lock(&configMutex);

	for (node_iter_t it = nodes.begin(); it != nodes.end(); ++it) {
		if (!key.compare(it->first)) {
			nodes.erase(it);
			break;
		}
	}
	nodes.insert(nodes.end(), node_pair);

	pthread_mutex_unlock(&configMutex);

	return result;
}

ConfigNode const& ConfigNode::operator=(ConfigNode const &src) {
	pthread_mutex_lock(&configMutex);

	nodes = src.nodes;

	pthread_mutex_unlock(&configMutex);

	return *this;
}

/* Returns a pointer to the internal ConfigNode(). NOT NEW MEMORY! */
ConfigNode* ConfigNode::findRequestedNode(const string& request) {
	bool escape = false;
	long start = -1, end = -1;
	string str;
	string req = request.c_str();
	ConfigNode *node;
	slist<pair<string, ConfigNode> > *nodes;

	pthread_mutex_lock(&configMutex);

	node = this;
	nodes = &(this->nodes);

	for (unsigned i = 0; i < req.size(); i++) {
		if (req[i] == '\\') {
			escape = true;
			req = req.erase(i, 1);
		}

		if (start == -1) {
			if (req[i] != ' ') {
				start = i;
			}
		}

		if (start != -1 && req[i] == ' ' && !escape) {
			end = i;
		} else if (i + 1 >= req.size()) {
			end = i + 1;
		}	

		if (start != -1 && end != -1) {
			node = NULL;
			str = req.substr(start, end - start).c_str();
			for (slist<pair<string, ConfigNode> >::iterator it = nodes->begin(); it != nodes->end(); ++it) {
				if (!it->first.compare(str)) {
					node = &(it->second);
					nodes = &(it->second.nodes);
					break;
				}
			}

			if (node == NULL) {
				break;
			}

			start = -1;
			end = -1;
		}

		escape = false;
	}

	pthread_mutex_unlock(&configMutex);

	return node;
}

ConfigNode* ConfigNode::getNode(const string& request) {
	ConfigNode *node, *ret_node;

	node = findRequestedNode(request);
	if (node) {
		ret_node = new ConfigNode(*node);
	} else {
		ret_node = NULL;
	}

	return ret_node;
}

vector<ConfigNode>* ConfigNode::getNodes(const string& request) {
	vector<ConfigNode>* nodes = NULL;
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
		nodes = new vector<ConfigNode>;
		for (node_iter_t it = node->nodes.begin(); it != node->nodes.end(); ++it) {
			nodes->push_back(it->second);
		}
	}

	return nodes;
}

string* ConfigNode::getValue(const string& request, unsigned long index) {
	string *value = NULL;
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
		value = new string(node->nodes.front().first);
	}

	return value;
}

vector<string>* ConfigNode::getValues(const string& request) {
	vector<string>* values = NULL;
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
		values = new vector<string>;
		for (node_iter_t it = node->nodes.begin(); it != node->nodes.end(); ++it) {
			values->push_back(it->first);
		}
	}

	return values;
}

/* Helper Functions */

bool ConfigNode::isNode(const string &request) {
	return (findRequestedNode(request) == NULL) ? false : true;
}

bool ConfigNode::assignNode(const string &request, ConfigNode &dst, const ConfigNode &def) {
	return assignNode(request, dst, &def);
}

bool ConfigNode::assignNode(const string &request, ConfigNode &dst, const ConfigNode *def) {
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node) {
		dst = *node;
		return true;
	}

	if (def) {
		dst = *def;
	}
	
	return false;
}

bool ConfigNode::assignNode(const string &request, ConfigNode *&dst, const ConfigNode &def) {
	return assignNode(request, dst, &def);
}

bool ConfigNode::assignNode(const string &request, ConfigNode *&dst, const ConfigNode *def) {
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node) {
#if 1
		if (dst) {
			/* Assume its pre-allocated. */
			*dst = *node;
		} else {
			dst = new ConfigNode(*node);
		}
#else
		dst = new ConfigNode(*node);
#endif
		return true;
	}

	if (def) {
#if 1
		if (dst) {
			/* Assume its pre-allocated. */
			*dst = *def;
		} else {
			dst = new ConfigNode(*def);
		}
#else
		dst = new ConfigNode(*def);
#endif
	} else {
		dst = NULL;
	}
	
	return false;
}

bool ConfigNode::assignValue(const string &request, unsigned long &dst, const unsigned long &def) {
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
		dst = strtoul(node->nodes.front().first.c_str(), NULL, 10);
		return true;
	}

	dst = def;

	return false;
}

bool ConfigNode::assignValue(const string &request, unsigned long &dst, const unsigned long &def, const unsigned long &min) {
	if (assignValue(request, dst, def)) {
		if (dst < min) {
			dst = def;
		}
		return true;
	}

	return false;
}

bool ConfigNode::assignValue(const string &request, unsigned long &dst, const unsigned long &def, const unsigned long &min, const unsigned long &max) {
	if (assignValue(request, dst, def, min)) {
		if (dst > max) {
			dst = def;
		}
		return true;
	}

	return false;
}

bool ConfigNode::assignValue(const string &request, unsigned long &dst, const unsigned long &def, const unsigned long &min, const unsigned long &max, const long &mult) {
	if (assignValue(request, dst, def, min, max)) {
		dst *= mult;
		return true;
	}
	dst *= mult;

	return false;
}

bool ConfigNode::assignValue(const string &request, long &dst, const long &def) {
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
		dst = strtol(node->nodes.front().first.c_str(), NULL, 10);
		return true;
	}

	dst = def;

	return false;
}

bool ConfigNode::assignValue(const string &request, string &dst, const string& def) {
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
		dst = node->nodes.front().first;
		return true;
	}

	dst = def;

	return false;
}

bool ConfigNode::assignValue(const string &request, string *&dst, const string& def) {
	return assignValue(request, dst, &def);
}

bool ConfigNode::assignValue(const string &request, string *&dst, const string* def) {
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
#if 1
		if (dst) {
			/* Assume its pre-allocated. */
			*dst = node->nodes.front().first;
		} else {
			dst = new string(node->nodes.front().first);
		}
#else
		dst = new string(node->nodes.front().first);
#endif
		return true;
	}

	if (def) {
#if 1
		if (dst) {
			/* Assume its pre-allocated. */
			*dst = *def;
		} else {
			dst = new string(*def);
		}
#else
		dst = new string(*def);
#endif
	} else {
		dst = NULL;
	}

	return false;
}

bool ConfigNode::assignValues(const string &request, vector<string> &dst) {
	vector<string> empty;

	return assignValues(request, dst, empty);
}

bool ConfigNode::assignValues(const string &request, vector<string> &dst, const vector<string> &def) {
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
		dst.clear();
		for (node_iter_t it = node->nodes.begin(); it != node->nodes.end(); ++it) {
			dst.push_back(it->first);
		}
		return true;
	}

	dst = def;

	return false;
}

bool ConfigNode::assignValues(const string &request, vector<string> *&dst, const vector<string> &def) {
	return assignValues(request, dst, &def);
}

bool ConfigNode::assignValues(const string &request, vector<string> *&dst, const vector<string> *def) {
	ConfigNode *node;

	node = findRequestedNode(request);
	if (node && !node->nodes.empty()) {
#if 1
		if (dst) {
			/* Assume its pre-allocated. */
			dst->clear();
		} else {
			dst = new vector<string>;
		}
#else
		dst = new vector<string>;
#endif
		for (node_iter_t it = node->nodes.begin(); it != node->nodes.end(); ++it) {
			dst->push_back(it->first);
		}
		return true;
	}

	if (def) {
#if 1
		if (dst) {
			/* Assume its pre-allocated. */
			dst->clear();
			*dst = *def;
		} else {
			dst = new vector<string>(*def);
		}
#else
		dst = new vector<string>(*def);
#endif
	} else {
		dst = NULL;
	}

	return false;
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim600: fdm=marker
** vim: noet ts=3 sw=3
*/
