#include "SocketThreadMonitord.h"
#include "config.h"
#include "Monitor.h"
#include <algorithm>
#include <iostream>

#include "MonitorLogging.h"

const std::string welcomeString="100;" +std::string(PACKAGE_STRING)+ " READY\r\n";
const std::string authenticateString="101:001\r\n" ;
const std::string illegalFormatString="101:004\r\n" ;

const std::string CAPABILITY_MONITORD_VERSION="0020" ;
const std::string CAPABILITY_MONITORD_PROTOCOLVERSION="0004" ;

using namespace std;

SocketThreadMonitord::SocketThreadMonitord(MonitorConfiguration *config, int LOCKNUM, int PortNum)
	: SocketThread(config, LOCKNUM, PortNum, monitord)
{
}

SocketThreadMonitord::~SocketThreadMonitord()
{
}

void SocketThreadMonitord::sayWelcome()
{
	say(welcomeString) ;
}

bool SocketThreadMonitord::parseCommand()
{
	m_paramCount=0 ;
	std::string parserTemp =m_CommandBuffer ;
	std::string item ; // Aktueller Teil zwischen den Doppelpunkten
	size_t len = 0 ;      // Laenge bis zum naechsten Doppelpunkt
	int itemCount=0;   // Wievielter Parameter ?
	bool lastItem=false ; // letzter Eintrag ? (fuer CR/LF interessant)
	bool illegalCommand=false ;
	int i ;

	for (i=0;i<MAX_PARAMS;i++)
	{
		m_cmdParam[i].clear() ;
	}

	// übergebene Zeichenkette in die einzelnen Parameter zerlegen. Kommentare am Ende ( Durch ";" abgetrennt ) entfernen

	len=parserTemp.find_last_of(";", 0) ;

	if (len!=std::string::npos) // Kein ";" gefunden ? - sonst abschneiden
	{
		parserTemp=parserTemp.substr(0,len) ;
	}
	do
	{
		len=parserTemp.find(":", 0) ;

		if (len==std::string::npos) // Kein ":" gefunden
		{
			if (!parserTemp.empty())
			{
				// CR/LF entfernen
				// TODO: Hier eigentlich noch auf CRLF prüfen !
				lastItem=true ;
				len=parserTemp.length(); // war: len=parserTemp.length()-2;
			}
		}

		if (len!= std::string::npos)
		{
			item=parserTemp.substr(0,len) ;

			if (lastItem)
			{
				parserTemp.erase(0,parserTemp.length()) ;
			} else {
				parserTemp.erase(0,len+1) ;
			}
			switch (itemCount)
			{
				case 0: // 3stelliges Kommando zwingend zu beginn !
						if (len!=3) {
							illegalCommand=true ;
						}
						m_cmdString=item ;
						break ;
				default:
						if (itemCount < MAX_PARAMS)
						{
							m_cmdParam[itemCount-1]=item ;
						}
						break;
			}
			itemCount++ ;
		}

		if (illegalCommand)
		{
			len=std::string::npos ;
			m_paramCount=0 ;
		} else
		{
			m_paramCount=itemCount ;
		}

	} while ((len!= std::string::npos) && (itemCount<=MAX_PARAMS));

	return true ;
}

void SocketThreadMonitord::processInput()
{
	bool isProcessed=false ;
	if (parseCommand())	{
		int cmd ;

		try {
		LOG_DEBUG("command from client: " << m_cmdString)
			cmd = convertToInt(m_cmdString) ;
		}
		catch (std::runtime_error err)
		{
			LOG_ERROR("Fehler bei der Cmd Konvertierung: " << m_cmdString) 
			cmd=-1 ;
			say(illegalFormatString) ;
		}

		// Bevor wir pruefen, ob eine Anmeldung vorliegt gibt es zwei Kommandos,
		// die trotzdem ausgefuehrt werden: 299 (bye) und 220 (Login)
		switch (cmd)
		{
			case 299:
					isProcessed=true ;
					say ("199\r\n") ; // Logout
					doLogout() ;
					break ;
			case 220:
					isProcessed=true ;
					checkLogin() ;
					break ;
			case 210: // Inquiery
					isProcessed=true ;
					tellCapabilites() ;
					break ;
			/*
			case 999: // Stop Server
					say ("going down...\r\n") ;
					m_monitor.m_bWantStop=true ;
			*/
			default:
					// Nichts tun ...
					break ;
		}

		if (isProcessed==false)
		{
			if (!m_authenticated)
			{
				say (authenticateString) ;
			} else
			{
				// Sonst alle anderen Kommandos jetzt pruefen ...

				switch (cmd)
				{
					case 202: // Keepalive
						say ("100\r\n") ; // OK
						break ;
					case 203: // ChannelInfo
						tellChannels() ;
						break ;
					case 204: // Record
						say ("101:005\r\n") ; // 005:Not implemented
						// auskommentiert, weil die Recording-Funktionalität nicht stabil/sicher ist.
						// Rückmeldung an den Client: not implemented
						// startRecording() ;
						break ;
					default:
						say(illegalFormatString);
					break;
				}
			}
		}
	} else {
		say(illegalFormatString) ;
	}
}

void SocketThreadMonitord::tellCapabilites()
{
	std::string hexOS ;
	//std::string hexChannelname;
	std::string hexProgramName ;
	std::string hexProgramVersion ;
	std::string hexModules ;

	#ifdef WIN32
		convertStringToHex("WINDOWS",hexOS) ;
	#else
		convertStringToHex("LINUX",hexOS) ;
	#endif

	hexProgramName=convertStringToHex(PACKAGE_NAME) ;
	//hexProgramVersion=convertStringToHex(CAPABILITY_MONITORD_VERSION) ;
	//hexModules=convertStringToHex(std::string("REC")) ;

	say ("111:1:" + hexProgramName + "\r\n") ;
	say ("111:2:" + hexOS+ "\r\n") ;
	say ("111:3:" + CAPABILITY_MONITORD_VERSION+ "\r\n") ;
	say ("111:4:" + CAPABILITY_MONITORD_PROTOCOLVERSION+ "\r\n") ;
	say (std::string("111:5:") + "\r\n") ;
	say (std::string("111:0") + "\r\n") ;
}

void SocketThreadMonitord::tellChannels()
{
#if 1 /* FIXME */
// FIXME done: monitord_config scheinbar wieder "richtig drin", es kommen jedenfalls Antworten gemäß der Konfiguration... mdi 29032011
	std::string hexString ;
	int summe ;

	for (int i=3; i>=0;i--)
	{
		if (m_MonitorConfiguration->m_sndConfig[i].iAktiv==1)
		{
			convertStringToHex(m_MonitorConfiguration->m_sndConfig[i].sChannelName0,hexString) ;
			summe=	 	  1*(m_MonitorConfiguration->m_sndConfig[i].iZVEI[0])
						+ 2*(m_MonitorConfiguration->m_sndConfig[i].iFMS[0])
						+ 4*(m_MonitorConfiguration->m_sndConfig[i].iPOC512[0])
						+ 8*(m_MonitorConfiguration->m_sndConfig[i].iPOC1200[0])
						;
			say ("103:" + convertIntToString(2*i+1) + ":" + hexString + ":" + convertIntToString(summe) +"\r\n") ;

			convertStringToHex(m_MonitorConfiguration->m_sndConfig[i].sChannelName1,hexString) ;
			summe=	 	  1*(m_MonitorConfiguration->m_sndConfig[i].iZVEI[1])
						+ 2*(m_MonitorConfiguration->m_sndConfig[i].iFMS[1])
						+ 4*(m_MonitorConfiguration->m_sndConfig[i].iPOC512[1])
						+ 8*(m_MonitorConfiguration->m_sndConfig[i].iPOC1200[1])
						;
			say ("103:" + convertIntToString(2*i) + ":" + hexString + ":" + convertIntToString(summe) +"\r\n") ;
		}
	}
#endif
}


void SocketThreadMonitord::startRecording(int seconds, int channel)
{
	int tempSeconds=0;
	int tempChannel=0;

	bool bErrConversion=false ;
	try
	{
		tempChannel=convertToInt(m_cmdParam[0]) ;
		tempSeconds=convertToInt(m_cmdParam[1]) ;
	} catch (BadConversion e)
	{
		bErrConversion=true ;
	}

	if (bErrConversion==false)
	{
		seconds=tempSeconds ;
		channel=tempChannel ;
	}

	int sndCardNum =channel / 2 ;
	int sndCardLeftRight=channel % 2 ;

	LOG_DEBUG( "Starte Aufnahme mit" << seconds << " Sekunden" << " auf Karte " << sndCardNum << ", Kanal=" << sndCardLeftRight << endl)
	std::string command=std::string("RECORD:" + convertIntToString(seconds)+ ":") + convertIntToString(channel) ;
#if 0 /* FIXME muß noch umstrukturiert werden */
	std::string resultString =m_monitor.m_sndIn[sndCardNum].PluginCommand(sndCardLeftRight,command,this);

	if (resultString=="not accepted")
	{
		say ("101:009\r\n") ;
	};

	if (resultString=="not implemented")
	{
		say ("101:005\r\n") ;
	};
#endif
}
void SocketThreadMonitord::checkLogin()
{


	// if (m_authenticated==false)
	// Auskommentiert: Auch bei freigeschalteter IP noch eine
	// Anmeldung zulassen. Auth-Status wird bei falschem Benutzernamen aber
	// nicht zurückgesetzt. Dient eher für Filter u.ä. um ggf. höhere Rechte
	// zu erlangen. Nur: Was antwortet man dem Client dann, wenn er das falsche PW ausgibt ?
	//
	{
		if (m_paramCount>3)
		{
			std::string loginname,password ;
			std::string protocol ;
			try {
				HexToString(0,loginname) ;
				HexToString(1,password) ;
				protocol= m_cmdParam[2] ; //HexToString(2,protocol) ;
			}
			catch (std::runtime_error err)
			{
				return ;
			}

			if (protocol==CAPABILITY_MONITORD_PROTOCOLVERSION)
			{
				if (m_MonitorConfiguration->IsValidLogin(loginname,password,m_sClientIP))
				{
					m_authenticated=true ;
					m_loginname=loginname ;
					LOG_INFO("login accepted (user allowed): " << m_loginname << " from ip " << m_sClientIP)
					say ("100\r\n") ; // Login OK
				} else {
					say ("101:003\r\n") ; // Benutzername falsch
				 	LOG_INFO("login denied: " << m_loginname << " from ip " << m_sClientIP)
				}
			} else {
				say ("101:008\r\n") ; // Falsche Protokollversion
				LOG_INFO("login denied, incorrect protocol version: " << protocol << " from ip: " << m_sClientIP << " with username: " << loginname)
			}
		} else {
			LOG_INFO("login with too few arguments detected")
			say ("101:004;TOO FEW ARGUMENTS\r\n") ; // Fehler: Anfrage nicht verstanden !
		}
	}

	/*
	} else {
		say ("100\r\n") ; // Login OK
	}
	*/

}



std::string SocketThreadMonitord::createFMSOutputString(ModuleResultBase Result)
{
	std::string socketText ;
	std::string text ;

	convertStringToHex(Result["textuebertragung"],text) ;

	socketText = std::string("310")
				 + ":" + Result["timestamp"]
				 //+ ":" + Result["servernamehex"]
				 //+ ":" + Result["channelnamehex"]
				 + ":" + Result["channelnum"]
				 + ":" + Result["fmskennung"]
				 + ":" + Result["status"]
				 + ":" + Result["baustufe"]
				 + ":" + Result["richtung"]
				 + ":" + Result["tki"] ;

				 if (text.size()>0)
				 {
				    socketText+= ":" + text ;
				 } ;

	 std::transform (	socketText.begin(),
	 					socketText.end(),
	 					socketText.begin(),
             			(int(*)(int)) toupper);

	return socketText ;
}

std::string SocketThreadMonitord::createZVEIOutputString(ModuleResultBase Result)
{
	std::string socketText="" ;
	std::string hexText;

	convertStringToHex(Result["text"],hexText) ;

	socketText = std::string("300")
			 + ":" + Result["timestamp"]
			 //+ ":" + Result["servernamehex"]
			 //+ ":" + Result["channelnamehex"]
			 + ":" + Result["channelnum"]
			 + ":" + Result["zvei"]
			 + ":" + Result["weckton"]
			 + ":" + hexText ;
			 ;
			std::transform (socketText.begin(), socketText.end(), socketText.begin(),
   (int(*)(int)) toupper);

	return socketText ;
}

std::string SocketThreadMonitord::createPOCSAGOutputString(ModuleResultBase Result)
{
	std::string socketText="" ;
	std::string hexText;

	convertStringToHex(Result["text"],hexText) ;

	socketText = std::string("320")
			 + ":" + Result["timestamp"]
			 //+ ":" + Result["servernamehex"]
			 //+ ":" + Result["channelnamehex"]
			 + ":" + Result["channelnum"]
			 + ":" + Result["ric"]
			 + ":" + Result["sub"]
			 + ":" + hexText ;
			 ;
			 std::transform (socketText.begin(), socketText.end(), socketText.begin(),
              (int(*)(int)) toupper);
    return socketText ;
}
