#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <iostream>

#include "Monitor.h"
#include "SocketServer.h"
#include "MonitorConfiguration.h"
#include "SocketThreadMonitord.h"

#include "MonitorLogging.h"

#ifdef PLUGINS
#include "plugins/mplugin.h"
#include "PluginThread.h"
#endif
#ifdef WIN32
#include <monitord/win32/MonitorService.h>
#define usleep Sleep
#endif

using namespace std ;

void Monitor::CreateSocketServer(MonitorConfiguration *config)
{
	static SocketServer socketServer(config,0) ;

	static SocketServer fms32ProServer(config,1000) ;
	fms32ProServer.m_ServerModus = SocketThread::fms32pro ;

	static SocketServer crusaderServer(config,2000) ;
	crusaderServer.m_ServerModus = SocketThread::crusader ;

	socketServer.Start() ;
	fms32ProServer.Start() ;
	crusaderServer.Start() ;
}

void Monitor::Initialize (int argc, char *argv[])
{
   m_bWantStop=false;
   if ( memLockCreate( 12345, & s) < 0) {
   		ThrowMonitorException("memLockCreate failed") ;
    }

	m_MonitorConfig.ParseCommandline(argc,argv) ;
 	m_MonitorConfig.ReadConfiguration(m_MonitorConfig.m_ConfigFile) ;
	m_MonitorConfig.ParseCommandline(argc,argv) ;
}

Monitor m_monitor;

void initFileLogging()
{

	if (!(m_monitor.m_MonitorConfig.m_sLogfile=="screen"))
	{
		FILE* pFile = fopen(m_monitor.m_MonitorConfig.m_sLogfile.c_str(), "a");
		Output2FILE::Stream() = pFile;
		FILE_LOG(logINFO) << "logging started";
	} ;

	cout << "Loglevel: " << m_monitor.m_MonitorConfig.m_sLoglevel << endl ;
	FILELog::ReportingLevel() = FILELog::FromString(m_monitor.m_MonitorConfig.m_sLoglevel);
}

int main(int argc, char** argv)
{
	try {
		m_monitor.Initialize (argc, argv);
		if (true) initFileLogging() ;
	#ifdef WIN32
		/**
		 * Soll als Dienst ausgef�hrt werden ?
		 */

		try
		{
			if (m_monitor.m_MonitorConfig.m_service_uninstall == true) {
				FILE_LOG(logINFO) << PACKAGE_NAME << " service uninstalling";
				/* uninstall service from service control daemon */
				MonitorService *m_MonitorService = new MonitorService(&m_monitor);
				m_MonitorService->UnInstallService();
				delete m_MonitorService;
			} else if (m_monitor.m_MonitorConfig.m_service_install == true) {
				FILE_LOG(logINFO) << PACKAGE_NAME << " service installing";
				/* install service in service control daemon */
				MonitorService *m_MonitorService = new MonitorService(&m_monitor);
				m_MonitorService->InstallService();
				delete m_MonitorService ;
			} else {
				if (m_monitor.m_MonitorConfig.m_service_run == true) {
					FILE_LOG(logINFO) << PACKAGE_NAME << " as service startet";
					/* running monitor as windows service application */
					MonitorService *m_MonitorService = new MonitorService(&m_monitor);
					m_MonitorService->Run ();
					delete m_MonitorService ;
				} else {
	#endif
				/* running monitor as command line application */
				FILE_LOG(logINFO) << PACKAGE_STRING << " READY\r\n" ;
				m_monitor.MainLoop ();
	#ifdef WIN32
				}
			}
		} catch (MonitorServiceException(err))
		{
			// FIXME: Dienste koennen nicht nach cerr schreiben, da siehe
			// unsichtbar im Hintergrund laufen
			FILE_LOG(logERROR) << err.what() ;
		}
	#endif

	} catch (MonitorException(err))
	{
		FILE_LOG(logERROR) << err.what() ;
	}
}


void Monitor::MainLoop()
{
	// Soundkarte initialisieren
	InitSndCard() ;

	/********************************************************/

	static SocketServer socketServer(&m_MonitorConfig) ;
	socketServer.Start() ;
	FILE_LOG(logINFO) << "monitord socketserver started" ;


	static SocketServer fms32ProServer(&m_MonitorConfig,1000) ;
	fms32ProServer.m_ServerModus=SocketThread::fms32pro ;
	fms32ProServer.Start() ;
	FILE_LOG(logINFO) << "fms32pro socketserver started" ;

	static SocketServer crusaderServer(&m_MonitorConfig,2000) ;
	crusaderServer.m_ServerModus=SocketThread::crusader ;
	crusaderServer.Start() ;
	FILE_LOG(logINFO) << "crusader socketserver started" ;


	/*******************************************************/

	#ifdef PLUGINS
	//GetPluginsManager().loadPlugin("plugins/.libs/libmplugin_mysql-0.dll",NULL);
	GetPluginsManager().loadPluginsFromConfigNode(&m_MonitorConfig.m_configDataPlugins);
	FILE_LOG(logINFO) << "PluginManager started" ;
	#endif
	/*********************************************************/

	while (!m_bWantStop)
	{
		/**
		 * Wer sich fragt, wo eigentlich denn die Arbeit gemacht wird:
		 * Die drei SocketServer sind eigenstaendige Threads. Die bedienen
		 * die TCP/IP Verbindungen und laufen unabhaegig.
		 *
		 * Die eigentliche (Ton) Auswertung erfolgt in jeweilse einem Thread
		 * pro Soundkarte. Diese Threads werden im InitSndCard gestartet.
		 *
		 * Dann gibt es noch den GlobalDispatcher. Er ist auch ein eigenstaendiger
		 * Thread. Er wird von dem Auswerten mit ResultSets versorgt. Die Auswerter
		 * haben damit Ihren Teil erledigt.
		 * Der Dispatcher verteilt dann die Results an alle Sockets und Plugins (ohne die
		 * Auswerter zu blockieren)
		 *
		 */

		usleep(100);
		// Wie man sieht: hier gibt es im Moment nichts zu tun :-
	}

	FILE_LOG(logINFO) << PACKAGE_NAME << " shutting down..."  ;
	StopSndCard() ;

	FILE_LOG(logINFO) << "stopping socketserver monitord";
	socketServer.m_bWantStop=true ;
	FILE_LOG(logINFO) << "stopping socketserver FMS32";
	fms32ProServer.m_bWantStop=true ;
	FILE_LOG(logINFO) << "stopping socketserver Crusader";
	crusaderServer.m_bWantStop=true ;

	usleep(1000) ;
	m_SignalStopped.SetSignal() ;
	usleep(500) ;
	FILE_LOG(logINFO) << "all done. " << PACKAGE_NAME << " exiting";
}

void Monitor::InitSndCard()
{
	unsigned int cardnum;
	for (cardnum=0;cardnum<4;cardnum++)
	{
		if (m_MonitorConfig.m_sndConfig[cardnum].iAktiv==1)
		{
			FILE_LOG(logINFO) << "starting soundcard #" << cardnum  ;
			m_sndIn[cardnum].initDecoderModules(cardnum,&m_MonitorConfig) ;
			m_sndIn[cardnum].m_SoundIn.setDevice(m_MonitorConfig.m_sndConfig[cardnum].sDevice, 22050) ;

			#ifdef PLUGINS
			  m_sndIn[cardnum].loadPlugins(m_MonitorConfig.m_sndConfig[cardnum].configChannel[0],m_MonitorConfig.m_sndConfig[cardnum].configChannel[1]) ;
			#endif
			m_sndIn[cardnum].m_SoundIn.Start() ;
			FILE_LOG(logINFO) << "soundcard #" << cardnum << "started"  ;
		}
	}
}


void Monitor::StopSndCard()
{
	unsigned int cardnum;
	for (cardnum=0;cardnum<4;cardnum++)
	{
		if (m_MonitorConfig.m_sndConfig[cardnum].iAktiv==1)
		{
			FILE_LOG(logINFO) << "stopping soundcard# " << cardnum  ;
			m_sndIn[cardnum].m_SoundIn.Stop() ;
			FILE_LOG(logINFO) << "soundcard #" << cardnum<< " halted."  ;
		}
	}
}
