
#include <typeinfo>
#include <iostream>

#ifdef WIN32
#define usleep Sleep
#include <windows.h>
#endif


#include "mysql/mysql.h"
#include "mplugin.h"
#include "../MonitorLogging.h"

using namespace std ;

enum fieldsource {mysql=0,resultset=1} ;

typedef struct
{
	string value ;
	fieldsource source ;
} FieldInfo ;

typedef map<string,FieldInfo*> MappingInfo ;
typedef pair <string,FieldInfo*> PairMapping ;

// Class PlugInFun inherits from PlugIn
// and shows the world when one is created/destroyed
// and supplies a Show() method that announces its name


class MonitorPlugInMySQL : public MonitorPlugIn
{
 public:
 	MYSQL m_mysql ;
 	std::string hostname ;
 	std::string username ;
 	std::string password ;
 	std::string database ;
 	unsigned int port ;
 	bool m_bConnected ;

 	MappingInfo fmsMapping ;
 	MappingInfo zveiMapping ;
 	MappingInfo pocsagMapping ;

 	std::string fmsTable;
 	std::string zveiTable;
 	std::string pocsagTable;

	MonitorPlugInMySQL()
	{
 		m_bConnected=false ;
	}

	virtual ~MonitorPlugInMySQL()
	{
	}

	virtual void Show()
	{
		FILE_LOG(logINFO) << "MonitorMySQLPlugin successfully loaded" ;
	}

	std::string escape_string(std::string text)
	{
		const unsigned int MAXLEN=255 ;
		char result[MAXLEN+1] ;

		memset(result,0,MAXLEN+1) ;
		mysql_real_escape_string(&m_mysql,result,text.c_str(),text.size()>MAXLEN ? MAXLEN : text.size()) ;
		return string(result) ;
	}

	virtual bool processResult(class ModuleResultBase *pRes)
	{
		FILE_LOG(logDEBUG) << "mysql: processing Result..."  ;

		if (m_bConnected==false)
		{
			return false ;
		}

		int pingcounter=0 ;
		while (mysql_ping(&m_mysql) && pingcounter<100)
		{
			usleep(100) ;
			pingcounter++ ;
			FILE_LOG(logINFO) << "mysql connection lost ... trying reconnect"  ;
		}

		if (mysql_ping(&m_mysql))
		{
			FILE_LOG(logERROR) << " unable to reconnect to mysql database" ;
			return false ;
		}

		if (pingcounter>0)
		{
			FILE_LOG(logINFO) << "mysql: connection re-established"  ;
		}


		if ((*pRes)["typ"]=="fms")
		{
			std::string insertString ;
			insertString = createInsertString(pRes,fmsMapping,fmsTable);
			mysql_query(&m_mysql,insertString.c_str()) ;
		} else if ((*pRes)["typ"]=="pocsag")
		{
			std::string insertString ;
			insertString = createInsertString(pRes,pocsagMapping,pocsagTable);
			mysql_query(&m_mysql,insertString.c_str()) ;
		}else if ((*pRes)["typ"]=="zvei")
		{
			std::string insertString;
			insertString = createInsertString(pRes,zveiMapping,zveiTable);
			mysql_query(&m_mysql,insertString.c_str()) ;
		}


		return true ;
	}

	virtual bool initProcessing(class MonitorConfiguration* configPtr,XMLNode config)
	{
		mysql_init(&m_mysql) ;

		#if (MYSQL_VERSION_ID>50013)
		/* for mysql V5.0.3+ is auto_reconnect disabled by default*/
		my_bool reconnect = 1;
		mysql_options(&m_mysql, MYSQL_OPT_RECONNECT, &reconnect);
		#endif

		hostname=getNodeText(config,"hostname","localhost") ;
		username=getNodeText(config,"username","root") ;
		password=getNodeText(config,"password","rootpw") ;
		database=getNodeText(config,"database","demo") ;
		port=getNodeInt(config,"port",3306) ;
		std::string logFile=getNodeText(config,"logfile","screen") ;
		std::string logLevel=getNodeText(config,"loglevel","INFO") ;
		#ifdef WIN32
		if (!(logFile=="screen"))
		{
			FILE* pFile = fopen(logFile.c_str(), "a");
			Output2FILE::Stream() = pFile;

		}
		FILELog::ReportingLevel() = FILELog::FromString(logLevel);
		FILE_LOG(logINFO) << "logging started";
		#endif

		// Parameter parsen
		parseParameter(config) ;
		if (mysql_real_connect(	&m_mysql,
								hostname.c_str(),
								username.c_str(),
								password.c_str(),
								database.c_str(),
								port,
								NULL,		// Unix Socket
								0			// Options
								) ==NULL)
		{
			FILE_LOG(logERROR) << "Could not connect to database \"" << database << "\" on host " << hostname << " with username=\"" << username << "\""  ;
			m_bConnected=false ;
		} else
		{
			FILE_LOG(logINFO) << "successfully connected to mysql database " << database  ;
			m_bConnected=true ;
		}
		return m_bConnected ;
	} ;

	virtual bool quitProcessing() {return true;} ;

	void parseParameter(XMLNode config)
	{
		XMLNode mappingNode ;

		int nMapping=config.nChildNode("mapping");

		for (int num=0; num<nMapping ; ++num)
		{
			if (!((mappingNode=config.getChildNode("mapping",num))).isEmpty())
			{
				std::string typ=mappingNode.getAttribute("typ") ;

				if (typ=="fms")
				{
					readMappings(mappingNode,fmsMapping,fmsTable) ;
				} else if (typ=="pocsag")
				{
					readMappings(mappingNode,pocsagMapping,pocsagTable) ;
				} else if (typ=="zvei")
				{
					readMappings(mappingNode,zveiMapping,zveiTable) ;
				}
			}
		}
	}

	void readMappings(XMLNode config,MappingInfo &mappingInfo,std::string &dbTable)
	{
		XMLNode mappingNode ;
		std::string table=getNodeText(config,"table","status") ;
		std::string name, source, value ;

		FILE_LOG(logDEBUG) << " reading mapping info " ;
		FILE_LOG(logDEBUG) << "table="<< table  ;

		dbTable=table;
		FieldInfo *pFieldInfo ;

		int nMapping=config.nChildNode("field");

		for (int num=0; num<nMapping ; ++num)
		{
			if (!((mappingNode=config.getChildNode("field",num))).isEmpty())
			{

				name=mappingNode.isAttributeSet("name") ? mappingNode.getAttribute("name") : "" ;
				source=mappingNode.isAttributeSet("source") ? mappingNode.getAttribute("source") : "" ;
				value= mappingNode.getText() ;
				if (name.empty()) ThrowMonitorException("MySQL Konfiguration: Kein Feldname vergeben ! ") ;
				if (value.empty()) ThrowMonitorException("MySQL Konfiguration: Kein Feldwert vergeben ! ") ;

				FILE_LOG(logDEBUG) << "Feld: " << name << " / " << source << ":" << value  ;
				pFieldInfo=new FieldInfo ;
				pFieldInfo->value=value ;
				if (source=="mysql")
				{
					pFieldInfo->source=mysql ;
				} else
				{
					pFieldInfo->source=resultset ;
				}
				mappingInfo.insert( PairMapping(name,pFieldInfo));
			}
		}
	}

	std::string createInsertString(class ModuleResultBase *pRes,MappingInfo mapping,std::string table)
	{
		std::string insertString="";
			std::string valueString="" ;
			MappingInfo::iterator i ;
			FieldInfo* pFieldInfo ;
			std::string fieldName="" ;

			insertString=" insert into " + table +  " (" ;
			for (i=mapping.begin();i!=mapping.end();i++)
			{
				fieldName= i->first ;
				pFieldInfo= i->second ;

				FILE_LOG(logDEBUG) << "field:" << fieldName << " | value=" << pFieldInfo->value  ;
				if (i!=mapping.begin())
				{
					insertString+="," ;
					valueString+="," ;
				}

				insertString+=fieldName ;
				if (pFieldInfo->source==mysql)
				{
					valueString+= pFieldInfo->value ;
				} else
				{
					valueString+="\"" +escape_string((*pRes)[pFieldInfo->value])+"\"" ;
				}
			}
			insertString+=") values (" ;
			insertString+=valueString + ")" ;

			FILE_LOG(logDEBUG) << "Insertstring:" << insertString ;
			return insertString ;
	}

};

//
// The PlugInFunFactory class inherits from PlugInFactory
// and creates a PlugInFun object when requested.
//


class MonitorPlugInMySQLFactory : public MonitorPlugInFactory
{
 public:
	MonitorPlugInMySQLFactory()
	{
	}

	~MonitorPlugInMySQLFactory()
	{
	}

	virtual MonitorPlugIn * CreatePlugIn()
	{
		return new MonitorPlugInMySQL() ;
	}
};


//
// The "C" linkage factory0() function creates the PlugInFunFactory
// class for this library
//

DLL_EXPORT void * factory0( void )
{
	return new MonitorPlugInMySQLFactory;
}
