#include "stdafx.h"
#include "config.h"
#include "tinyxml/tinyxml.h"
#include "util.h"

SConfig::SConfig()
{
	streamAnalysis = false;
	logSync = false;
	hostFilter = false;
	statsCollector = false;
}

SConfig GetConfig(bool reload)
{
	SCOPED_GLOBAL_LOCK;

	static SConfig c;
	static bool firstTime = true;
	if (!firstTime && !reload)
		return c;
	firstTime = false;
	string filename = "config.xml";
	TiXmlDocument doc(filename.c_str());
	if (!doc.LoadFile())
		throw std::runtime_error("failed opening " + filename);
	TiXmlHandle hdl( &doc );
	TiXmlHandle config = hdl.FirstChild("config");
	if (!config.Element())
		throw std::runtime_error("no config node in " + filename);
#define FLAG(n) \
	if (TiXmlElement * pElem = config.FirstChild(#n).Element()) \
	{ \
		int i; \
		pElem->Attribute("enable", &i); \
		c.n = (i != 0); \
	}
#include "flags.h"
#undef FLAG

	TiXmlElement* filter_list=config.FirstChild("hostFilterList").ToElement();

	if(filter_list)
	{
		TiXmlNode* child=filter_list->FirstChildElement("host");
		for(;child;child=child->NextSibling("host"))
		{
			const char * name=child->ToElement()->Attribute("name");
			c.hosts.push_back(name);
		}
	}

	string policiesFilename = config.FirstChild("policies").ToElement()->Attribute("filename");
	if ( !c.policiesXml.LoadFile(policiesFilename.c_str()) )
		throw std::runtime_error("failed loading " + policiesFilename);

	string schedulerFilename = config.FirstChild("scheduler").ToElement()->Attribute("filename");
	if ( !c.schedulerXml.LoadFile(schedulerFilename.c_str()) )
		throw std::runtime_error("failed loading " + schedulerFilename);

	string entitySchedulerFilename = config.FirstChild("entityScheduler").ToElement()->Attribute("filename");
	if ( !c.entitySchedulerXml.LoadFile(entitySchedulerFilename.c_str()) )
		throw std::runtime_error("failed loading " + entitySchedulerFilename);

	const char* s = config.FirstChild("samplingThreshold").ToElement()->Attribute("value");
	float samplingThreshold = s ? atof(s) : 0.0f;
	c.samplingThreshold = samplingThreshold > 1.0 ? 1.0 : samplingThreshold < 0.0 ? 0.0 : samplingThreshold;

	return c;
}

void WriteConfig( SConfig config )
{
	TiXmlDocument doc;
	TiXmlNode * pElem = doc.LinkEndChild( new TiXmlElement("config") );
#define FLAG(n) \
	{ \
	TiXmlElement * pV = new TiXmlElement(#n); \
	pV->SetAttribute("enable", config.n? 1 : 0); \
	pElem->LinkEndChild(pV); \
	}
#include "flags.h"
#undef FLAG
	TiXmlElement * pHosts = new TiXmlElement("hostFilterList");
	
	for(size_t i=0;i<config.hosts.size();++i)
	{
		TiXmlElement * pHost = new TiXmlElement("host");
		pHost->SetAttribute("name", config.hosts[i].c_str());
		pHosts->LinkEndChild(pHost);
	}
	pElem->LinkEndChild(pHosts);

	if (!doc.SaveFile("config.xml"))
		throw std::runtime_error("Failed writing config.xml");
}

class CConfigurator : public IServlet
{
public:
	virtual void Get( CHTTPOutput * pOut, const ParamsMap& )
	{
		try
		{
			pOut->Write("<html><body>");
			SConfig config = GetConfig();
#define FLAG(n) \
	pOut->Write( "<a href=\"/setflags.htm?%s=%s\">%s: %s</a><br/>", #n, config.n? "false" : "true", #n, config.n? "on" : "off" );
#include "flags.h"
#undef FLAG
			pOut->Write("</html></body>");
		}
		catch (std::exception& e)
		{
			pOut->Write("Exception generating page: %s", e.what());
		}
	}
};

class CSetFlag : public CConfigurator
{
public:
	CSetFlag( bool value, bool (SConfig::*where), const string& name ) : m_value(value), m_where(where), m_name(name)
	{
	}

	void Get( CHTTPOutput * pOut, const ParamsMap& p)
	{
		try
		{
			SConfig config = GetConfig();
			(config.*m_where) = m_value;
			WriteConfig(config);
		}
		catch (std::exception& e)
		{
			pOut->Write("%s: failed setting value<br/>", m_name.c_str());
			pOut->Write("%s<br/>", e.what());
		}
		CConfigurator::Get(pOut,p);
	}

private:
	bool m_value;
	bool (SConfig::*m_where);
	string m_name;
};


class CConfigFlagSet : public CConfigurator
{
public:
	void Get( CHTTPOutput * pOut, const ParamsMap& p)
	{
		try
		{
			SConfig config = GetConfig();
			for(ParamsMap::const_iterator it=p.begin(); it!=p.end();++it)
			{

#define FLAG(n) \
				if(it->first == #n)\
					config.n = it->second=="true";
#include "flags.h"
#undef FLAG
			}
			WriteConfig(config);
		}
		catch (std::exception& e)
		{
			pOut->Write("CConfigFlagSet : failed setting value<br/>");
			pOut->Write("%s<br/>", e.what());
		}
		CConfigurator::Get(pOut,p);
	}

};

void RegisterConfigPages( CHTTPD * p )
{
	p->Register("/config.htm", new CConfigurator);
	p->Register("/setflags.htm", new CConfigFlagSet);
#define FLAG(n) \
	p->Register("/set-" + string(#n) + "-true.htm", new CSetFlag(true, &SConfig::n, #n)); \
	p->Register("/set-" + string(#n) + "-false.htm", new CSetFlag(false, &SConfig::n, #n)); 
#include "flags.h"
#undef FLAG
}
