#include <typeinfo>
#include <iostream>
#include <time.h>
#include "mpluginAudio.h"
#include "../../config.h"
#include "../convert.h"
#include "../MonitorExceptions.h"
#include "../MonitorLogging.h"

#ifdef LIB_ASOUND
	#include <lame/lame.h>
#endif

#ifndef WIN32
	typedef short SHORT ;
	#include <dlfcn.h>
#else
	#include <windows.h>
#endif

using namespace std ;

class MonitorAudioPlugInRecorder : public MonitorAudioPlugIn
{
 public:
 	#ifdef LIB_ASOUND
 	/**
 	 *
 	 */
 	 lame_global_flags *gfp ;

	#endif

	MonitorAudioPlugInRecorder()
	{
		useLame=false ;
		m_channelNum=0 ;
	}

	virtual ~MonitorAudioPlugInRecorder()
	{
		#ifdef LIB_ASOUND
			lame_close(gfp) ;
			gfp=NULL ;
		#endif

	}

	virtual bool InitAudioProcessing(class MonitorConfiguration* configPtr, XMLNode config, int channelNum)
	{
		std::string logFile="screen" ;
		std::string logLevel="DEBUG" ;
		XMLNode parameters ;
		m_channelNum=channelNum;

		if (!((parameters=config.getChildNode("parameters"))).isEmpty())
		{
			m_filePath=getNodeText(parameters,"path","./") ;
			logFile=getNodeText(parameters,"logfile","screen") ;
			logLevel=getNodeText(parameters,"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

		#ifdef LIB_ASOUND

		try {
			FILE_LOG(logINFO) << "enabling lame mp3 support"  ;
			FILE_LOG(logINFO) << "Using LIB_ASOUND Version: " << get_lame_version() ;

			gfp=lame_init() ;
			if (gfp==NULL)
			{
				ThrowMonitorException("Error initializing lame library!") ;
			}

			lame_set_bWriteVbrTag(gfp,0) ; // Kein ID3 Tag
			lame_mp3_tags_fid(gfp,NULL) ;
			lame_set_num_channels(gfp,1) ;
			lame_set_in_samplerate(gfp,22050) ;
			lame_set_brate(gfp,32) ;
			lame_set_mode(gfp,MONO) ;
			lame_set_quality(gfp,7) ;

			int retCode = lame_init_params(gfp) ;
			if (retCode<0)
			{
				ThrowMonitorException("LIB_ASOUND init failed") ;
			}else{
				useLame=1 ;
			}
		} catch (MonitorException e)
		{
			FILE_LOG(logERROR) << e.what() ;
		}
		#endif


		return true ;
	}

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

	virtual void WriteRAWToFile (FILE *pFile,SHORT buffer[],unsigned int bufferSize)
	{
		size_t len=fwrite(buffer, sizeof(SHORT), bufferSize,pFile);
		if (len!=bufferSize)
		{
			FILE_LOG(logERROR) << "Fehler beim Schreiben: " << len << " statt " << bufferSize  ;
			// Fehler beim Schreiben ?
		}
	}

	virtual void WriteMP3ToFile (FILE *pFile,unsigned char buffer[],unsigned int bufferSize)
	{
		size_t len=fwrite(buffer, sizeof(unsigned char), bufferSize,pFile);
		if (len!=bufferSize)
		{
			FILE_LOG(logERROR) << "Fehler beim Schreiben: " << len << " statt " << bufferSize  ;
			// Fehler beim Schreiben ?
		}
	}

	virtual void ProcessAudio(float *buffer, int length)
	{
		const int MAXTEMP=20000 ;
		int jobID=0 ;
		int retCode=0 ; // LIB_ASOUND encoding return code
		static SHORT tempBuf[MAXTEMP] ;
		static unsigned char mp3Buffer[2*MAXTEMP] ;
		unsigned int tempBufCounter=0 ;
		FILE* pFile=NULL ;
		time_t startTime ;

		if (length>MAXTEMP)
		{
			ThrowMonitorException ("Audiobuffer size exceeds temporary buffer size!") ;
		}

		//if (m_bRecording)
		{
			for (int i=0;i<length;i++)
			{
				tempBuf[tempBufCounter++]=(SHORT)((*buffer)*32768.0) ;
				buffer++;
			}

			#ifdef LIB_ASOUND

				if (useLame==1)
				{
					retCode=lame_encode_buffer(gfp,tempBuf,NULL,length,mp3Buffer,0) ;
					if (retCode<0)
					{
						ThrowMonitorException("Error while lame (mp3) encoding") ;
					}
				}
			#endif

			for (jobID=0;jobID<MAXAUDIOCLIENTS;jobID++)
			{
				pFile=(FILE*) getCustomValue(jobID);
				unsigned long seconds = getInfo(jobID) ;
				SocketThread* pClient=getClient(jobID) ;
				startTime=getTime1(jobID) ;

				if ((pFile) && (pClient))
				{
					//
					if (useLame==1)
					{
						WriteMP3ToFile(pFile,mp3Buffer,retCode) ;
					} else {
						WriteRAWToFile(pFile,tempBuf,tempBufCounter) ;
					}
					time_t curTime=time(NULL) ;
					if ( (curTime-startTime)>(time_t) seconds)
					{
						StopRecording(jobID) ;
					}
				}
			}
		}
	}

	void StartRecording(int jobID,std::string fname,int Sekunden)
	{
		FILE_LOG(logINFO) << "Starte Aufnahme .."  ;

		// Aufnahmedatei erzeugen
		FILE_LOG(logINFO) << "Starte mit dauer:" << Sekunden  ;
		if (fname.size()>0)
		{
			// Dateiname wurde vorgegeben
		} else
		{
			fname="kein_datename" ;
		}

		FILE * f_File = fopen(fname.c_str(), "wb");

		if(f_File==NULL) {
			FILE_LOG(logERROR) << "File ist NICHT erstellt" ;
		}
		else
		{
			FILE_LOG(logERROR) << "File ist erstellt"<< fname  ;
		}

		// Aufnahme starten, Daten beim Job hinterlegen
		updateClient(jobID,
						(unsigned long) f_File,
						Sekunden,
						fname,
						time(NULL)) ;

		addThreadMessage(jobID,std::string("104:" +convertIntToString(m_channelNum)+ ":1:") + convertStringToHex(fname)) ;
	}

	void StopRecording(int jobID)
	{
		FILE_LOG(logINFO) << "Aufnahme beendet"  ;

		FILE* pFile=(FILE*) getCustomValue(jobID) ;
		std::string fname=getInfoText(jobID) ;

		fclose(pFile) ;

		addThreadMessage(jobID,std::string("104:" +convertIntToString(m_channelNum)+ ":0:") + convertStringToHex(fname)) ;
		clearClient(jobID) ;
	}

	virtual std::string DoCommand(std::string command, SocketThread* pClient)
	{
		std::string resultString="" ;
		std::string fname ;
		int Sekunden=0 ;

		FILE_LOG(logINFO) << "Kommando: " << command ;
		parseCommand(command) ;

		for (int i=0;i<10;i++)
		{
			FILE_LOG(logINFO) << "Param " << i << ":" << m_param[i] ;
		}

		if (m_param[0]=="RECORD") //RECORD:<zeit>
		{
			if (m_paramCount>1)
			{
				try
				{
					Sekunden=convertToInt(m_param[1]) ;
					FILE_LOG(logINFO) << "Sekunden als param:" << m_param[1] ;
					FILE_LOG(logINFO) << "Sekunden als int:" << Sekunden  ;
				} catch (BadConversion e)
				{
					FILE_LOG(logERROR) << "Fehler bei der Datenkonvertierung " << m_param[1]  ;
					Sekunden=60 ;
				}
			}

			int jobID=addClient(pClient) ; //< Auftraggeber, fuer callback mit Result
			FILE_LOG(logINFO) << "jobID=" << jobID  ;

			// Laeuft mit der jobID schon eine Aufzeichnung ?
			// Wenn, dann keine neue starten, sondern nur verlaengern

			long SekundenAlterJob = getInfo(jobID) ;
			FILE_LOG(logINFO) << "Zeitdauer alte Aufnahme:" << SekundenAlterJob  ;
			if (SekundenAlterJob>0)
			{
				// Nur die Zeit anpassen
				time_t curTime=time(NULL) ;
				time_t startTime=getTime1(jobID) ;

				FILE_LOG(logDEBUG) << "Zeiten curTime+Sekunden:" << curTime+Sekunden  ;
				FILE_LOG(logDEBUG) << "Zeiten startTime+SekundenAlterJob:" << startTime+SekundenAlterJob  ;
				if (curTime+Sekunden>startTime+SekundenAlterJob)
				{
					fname=getInfoText(jobID) ;
					Sekunden=curTime+Sekunden-startTime ;
					FILE_LOG(logINFO) << "Aufnahme wird auf " << Sekunden << "verlaengert " ;
					updateClient(jobID,getCustomValue(jobID),Sekunden,getInfoText(jobID),startTime) ;
					addThreadMessage(jobID,std::string("104:" +convertIntToString(m_channelNum)+ ":2:") + convertStringToHex(fname)) ;
				}
			} else {
				time_t m_startTime=time(NULL) ;
				struct tm *strTime = localtime(&m_startTime) ;

				char zeitString[60] ;
				sprintf(zeitString,"%04d%02d%02d%02d%02d%02d",
										1900+strTime->tm_year,
										1+strTime->tm_mon,
										strTime->tm_mday,
										strTime->tm_hour,
										strTime->tm_min,
										strTime->tm_sec) ;
				FILE_LOG(logDEBUG) << zeitString  ;

				if (m_paramCount<2)
				{
					m_param[2]="X" ;
				}

				fname=	m_filePath
						+ std::string(zeitString)
						+ "_"
						+ m_param[2]
						+ "_"+ convertIntToString(jobID) ;

				cout << fname << endl ;
				if (useLame==1)
				{
					fname+=".mp3"; ;
				} else
				{
					fname+=".raw"; ;
				}

				StartRecording(jobID,fname,Sekunden) ;
			}
		} ;
		resultString="OK" ;

		return (resultString) ;
	}

protected:
	std::string m_filePath ;
	int useLame ;
	int m_channelNum ;

};



class MonitorAudioPlugInRecorderFactory : public MonitorAudioPlugInFactory
{
 public:
	MonitorAudioPlugInRecorderFactory()
	{
	}

	~MonitorAudioPlugInRecorderFactory()
	{
	}

	virtual MonitorAudioPlugIn * CreatePlugIn()
	{
		return new MonitorAudioPlugInRecorder;
	}
};


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

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