////////////////////////////////////////////////////////////////////////////////
//    Scorched3D (c) 2000-2003
//
//    This file is part of Scorched3D.
//
//    Scorched3D is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    Scorched3D is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Scorched3D; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
////////////////////////////////////////////////////////////////////////////////


#include <common/OptionEntry.h>
#include <common/Defines.h>
#include <XML/XMLFile.h>
#include <stdio.h>
#include <map>

OptionEntry::OptionEntry(std::list<OptionEntry *> &group,
						 const char *name,
						 const char *description,
						 unsigned int data) :
	name_(name),
	description_(description),
	data_(data)
{
	group.push_back(this);
}

OptionEntry::~OptionEntry()
{

}

bool OptionEntryHelper::addToArgParser(std::list<OptionEntry *> &options,
									   ARGParser &parser)
{
	std::list<OptionEntry *>::iterator itor;
	for (itor = options.begin();
		itor != options.end();
		itor++)
	{
		if (!(*itor)->addToArgParser(parser)) return false;
	}

	return true;
}

bool OptionEntryHelper::writeToBuffer(std::list<OptionEntry *> &options,
									  NetBuffer &buffer)
{
	std::list<OptionEntry *>::iterator itor;
	for (itor = options.begin();
		itor != options.end();
		itor++)
	{
		if (!(*itor)->writeToBuffer(buffer)) return false;
	}

	return true;
}

bool OptionEntryHelper::readFromBuffer(std::list<OptionEntry *> &options,
									   NetBufferReader &reader)
{
	std::list<OptionEntry *>::iterator itor;
	for (itor = options.begin();
		itor != options.end();
		itor++)
	{
		if (!(*itor)->readFromBuffer(reader)) return false;
	}

	return true;
}

bool OptionEntryHelper::writeToFile(std::list<OptionEntry *> &options,
									char *filePath)
{
	FileLines file;

	file.addLine("<options source=\"Scorched3D\">");
	std::list<OptionEntry *>::iterator itor;
	for (itor = options.begin();
		itor != options.end();
		itor++)
	{
		OptionEntry *entry = (*itor);

		file.addLine("  <!-- %s -->", entry->getDescription());
		file.addLine("  <option>");
		file.addLine("    <name>%s</name>", entry->getName());
		file.addLine("    <value>%s</value>", entry->getValueAsString());
		file.addLine("    <defaultvalue>%s</defaultvalue>", entry->getDefaultValueAsString());
		file.addLine("  </option>");
	}
	file.addLine("</options>");

	if (!file.writeFile(filePath)) return false;

	return true;
}

bool OptionEntryHelper::readFromFile(std::list<OptionEntry *> &options,
									 char *filePath)
{
	// Parse the XML file
	XMLFile file;
	if (!file.readFile(filePath))
	{
		dialogMessage("Scorched3D Options", 
			"ERROR: Failed to parse file \"%s\"\n"
			"%s",
			filePath,
			file.getParserError());		
		return false;
	}

	// return true for an empty file
	if (!file.getRootNode()) return true;

	// Create a map from string name to existing options
	// So we can find the named option when parsing the file
	std::map<std::string, OptionEntry *> entryMap;
	std::list<OptionEntry *>::iterator itor;
	for (itor = options.begin();
		itor != options.end();
		itor++)
	{
		OptionEntry *entry = (*itor);
		std::string name = entry->getName();
		_strlwr((char *) name.c_str());

		entryMap[name] = entry;
	}

	// Itterate all of the options in the file
	std::list<XMLNode *>::iterator childrenItor;
	for (childrenItor = file.getRootNode()->getChildren().begin();
		childrenItor != file.getRootNode()->getChildren().end();
		childrenItor++)
	{
		XMLNode *currentNode = (*childrenItor);
		XMLNode *nameNode = currentNode->getNamedChild("name");
		XMLNode *valueNode = currentNode->getNamedChild("value");

		if (!nameNode)
		{
			dialogMessage(
				"Scorched3D Options", 
				"ERROR: Failed to parse file \"%s\". No name node",
				filePath);
			return false;			
		}
		if (!valueNode)
		{
			dialogMessage("Scorched3D Options", 
				   "ERROR: Failed to parse file \"%s\". No value node",
					filePath);
			return false;			
		}

		std::string name = nameNode->getContent();
		std::string value = valueNode->getContent();
		_strlwr((char *) name.c_str());
		
		std::map<std::string, OptionEntry *>::iterator finditor =
			entryMap.find(name);
		if (finditor == entryMap.end())
		{
			dialogMessage("Scorched3D Options",
				"ERROR: Failed to parse file \"%s\". Cannot find option \"%s\"",
				filePath, name.c_str());
			return false;					
		}

		if (!(*finditor).second->setValueFromString(value.c_str()))
		{
			dialogMessage("Scorched3D Options",
				"ERROR: Failed to parse file \"%s\"\n"
				"Failed to set option \"%s\" with \"%s\"",
				filePath, name.c_str(), value.c_str());
			return false;		
		}	
	}

	return true;
}

OptionEntryInt::OptionEntryInt(std::list<OptionEntry *> &group,
							   const char *name,
							   const char *description,
							   unsigned int data,
							   int value) :
	OptionEntry(group, name, description, data), 
	value_(value), defaultValue_(value)
{
	
}

OptionEntryInt::~OptionEntryInt()
{

}

bool OptionEntryInt::writeToBuffer(NetBuffer &buffer)
{
	buffer.addToBuffer(value_);
	return true;
}

bool OptionEntryInt::readFromBuffer(NetBufferReader &reader)
{
	return reader.getFromBuffer(value_);
}

const char *OptionEntryInt::getValueAsString()
{
	static char value[256];
	sprintf(value, "%i", value_);
	return value;
}

const char *OptionEntryInt::getDefaultValueAsString()
{
	static char value[256];
	sprintf(value, "%i", defaultValue_);
	return value;
}

bool OptionEntryInt::setValueFromString(const char *string)
{
	int val;
	if (sscanf(string, "%i", &val) != 1) return false;
	return setValue(val);
}

int OptionEntryInt::getValue() 
{
	return value_;
}

bool OptionEntryInt::setValue(int value)
{
	value_ = value;
	return true;
}

bool OptionEntryInt::setIntArgument(int value)
{
	return setValue(value);
}

bool OptionEntryInt::addToArgParser(ARGParser &parser)
{
	char name[256], description[1024];
	sprintf(name, "-%s", getName());
	sprintf(description, "%s (Default : %s)",
			(char *) getDescription(), 
			(char *) getDefaultValueAsString());

	parser.addEntry(name , this, description);
	return true;
}

OptionEntry::OptionType OptionEntryInt::getType()
{
	return OptionEntryIntType;
}

OptionEntryBoundedInt::OptionEntryBoundedInt(std::list<OptionEntry *> &group,
											 const char *name,
											 const char *description,
											 unsigned int data,
											 int value,
											 int minValue, int maxValue) :
	OptionEntryInt(group, name, description, data, value), 
	minValue_(minValue), maxValue_(maxValue)
{
	
}

OptionEntryBoundedInt::~OptionEntryBoundedInt()
{
	
}

bool OptionEntryBoundedInt::setValue(int value)
{
	if (value < minValue_ || value > maxValue_) return false;
	return OptionEntryInt::setValue(value);
}

OptionEntryBool::OptionEntryBool(std::list<OptionEntry *> &group,
								 const char *name, 
								 const char *description, 
								 unsigned int data,
								 bool value) :
	OptionEntry(group, name, description, data), 
	value_(value), defaultValue_(value)
{
	
}

OptionEntryBool::~OptionEntryBool()
{
	
}

bool OptionEntryBool::writeToBuffer(NetBuffer &buffer)
{
	buffer.addToBuffer(value_?1:0);
	return true;
}

bool OptionEntryBool::readFromBuffer(NetBufferReader &reader)
{
	int val;
	if (!reader.getFromBuffer(val)) return false;
	value_ = (val==1);
	return true;
}

const char *OptionEntryBool::getDefaultValueAsString()
{
	return (defaultValue_?"on":"off");
}

const char *OptionEntryBool::getValueAsString()
{
	return (value_?"on":"off");
}

bool OptionEntryBool::setValueFromString(const char *string)
{
	if (stricmp("on", string) == 0)
	{
		value_ = true;
		return true;
	}
	if (stricmp("off", string) == 0)
	{
		value_ = false;
		return true;
	}
	return false;
}

bool OptionEntryBool::setValue(bool value)
{
	value_ = (value?1:0);
	return true;
}

bool OptionEntryBool::getValue()
{
	return ((value_==0)?false:true);
}

bool OptionEntryBool::setBoolArgument(bool value)
{
	return setValue(value);
}

bool OptionEntryBool::addToArgParser(ARGParser &parser)
{
	char name[256], description[1024];
	sprintf(name, "-%s", getName());
	sprintf(description, "%s (Default : %s)",
			(char *) getDescription(), 
			(char *) getDefaultValueAsString());

	parser.addEntry(name , this, description);
	return true;
}

OptionEntry::OptionType OptionEntryBool::getType()
{
	return OptionEntryBoolType;
}

OptionEntryString::OptionEntryString(std::list<OptionEntry *> &group,
									 const char *name, 
									 const char *description, 
									 unsigned int data,
									 const char *value) :
	OptionEntry(group, name, description, data),
	value_(value), defaultValue_(value)
{
	
}

OptionEntryString::~OptionEntryString()
{

}

bool OptionEntryString::writeToBuffer(NetBuffer &buffer)
{
	buffer.addToBuffer(value_);
	return true;
}

bool OptionEntryString::readFromBuffer(NetBufferReader &reader)
{
	return reader.getFromBuffer(value_);
}

const char *OptionEntryString::getValueAsString()
{
	return value_.c_str();
}

const char *OptionEntryString::getDefaultValueAsString()
{
	return defaultValue_.c_str();
}

bool OptionEntryString::setValueFromString(const char *string)
{
	return setValue(string);
}

const char *OptionEntryString::getValue() 
{
	return value_.c_str();
}

bool OptionEntryString::setValue(const char *value)
{
	value_ = value;
	return true;
}

bool OptionEntryString::setStringArgument(const char* value)
{
	return setValue(value);
}

bool OptionEntryString::addToArgParser(ARGParser &parser)
{
	char name[256], description[1024];
	sprintf(name, "-%s", getName());
	sprintf(description, "%s (Default : %s)",
			(char *) getDescription(), 
			(char *) getDefaultValueAsString());

	parser.addEntry(name , this, description);
	return true;
}

OptionEntry::OptionType OptionEntryString::getType()
{
	return OptionEntryStringType;
}
