/* 
 * tsworker.c: ts streaming worker thread
 *
 * See the README file for copyright information and how to reach the author.
 *
 */

#include <vdr/tools.h>

#include "tools/socket.h"
#include "tools/select.h"

#include "tsworker.h"

//////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////

cTSWorker *cTSWorker::m_Instance = NULL;

cTSWorker::cTSWorker(void)
		: cThread("[ffnetdev] TS streamer")
{
	m_Active = false;
	
	m_StreamClient = NULL;
}

cTSWorker::~cTSWorker() {
	if (m_Active) Stop();
}

void cTSWorker::Init(cStreamDevice *StreamDevice, int tsport ) {
	if (m_Instance == NULL) {
		m_Instance = new cTSWorker;
		m_Instance->m_StreamDevice = StreamDevice;
		m_Instance->TSPort = tsport;		
		m_Instance->Start();
	}
}

void cTSWorker::Exit(void) {
	if (m_Instance != NULL) {
		m_Instance->Stop();
		DELETENULL(m_Instance);
	}
}

void cTSWorker::Stop(void) {
	m_Active = false;
	Cancel(3);
}

void cTSWorker::CloseStreamClient(void) {
	m_Instance->close_Streamclient_request = true;
#ifdef DEBUG
	fprintf(stderr, "[ffnetdev] Streamer: Closing of TS-streaming client socket requested.\r\n");
#endif
}

void cTSWorker::Action(void) {
	cTBSelect select;
	cTBSocket m_StreamListen;
        
	const char* m_ListenIp = "0.0.0.0";
	uint m_StreamListenPort = TSPort;
	
	m_StreamClient 	= new cTBSocket;
	
	m_Active		= true;
	have_Streamclient	= false;

        if (!m_StreamListen.Listen(m_ListenIp, m_StreamListenPort, 1)) { // ToDo JN place to allow more connections/clients!
		esyslog("[ffnetdev] Streamer: Couldn't listen %s:%d: %s", m_ListenIp, m_StreamListenPort, strerror(errno));
		m_Active = false;
	} 
	else
	        isyslog("[ffnetdev] Streamer: Listening on port %d", m_StreamListenPort);
        
	while (m_Active) {
		select.Clear();
		
		if (have_Streamclient==false)
			select.Add(m_StreamListen, false);
		else {
			select.Add(*m_StreamClient, true);      //select for writing fd
			select.Add(*m_StreamClient, false);	//select for reading fd
		}

		int numfd;
		/* React on status change of any of the above file descriptor */
		if ((numfd=select.Select(1000)) < 0) {
			if (!m_Active) // Exit was requested while polling
				continue;
			esyslog("[ffnetdev] Streamer: Fatal error, ffnetdev exiting: %s", strerror(errno));
			m_Active = false;
			continue;
		}
		
		
		//DEBUG
		/*		
		fprintf(stderr, "[ffnetdev] Streamer: Num_FD TS: %d", numfd);
		
		if (select.CanRead(m_StreamListen) || select.CanWrite(m_StreamListen)) 
			fprintf (stderr, "m_StreamListen can act.\n");
		if (select.CanRead(*m_StreamClient) || select.CanWrite(*m_StreamClient)) 
			fprintf (stderr, "m_StreamClient can act.\n");
		*/	
		
		/* Accept connecting streaming clients	*/
		if ( (have_Streamclient==false)&&select.CanRead(m_StreamListen) ) {
			if (m_StreamClient->Accept(m_StreamListen)) {
				isyslog("[ffnetdev] Streamer: Accepted client %s:%d",
					m_StreamClient->RemoteIp().c_str(), m_StreamClient->RemotePort());
				have_Streamclient = true;
			} 
			else {
				esyslog("[ffnetdev] Streamer: Couldn't accept : %s", strerror(errno));
				have_Streamclient = false;
				m_Active = false;
				continue;
			}
		}

		
		/* Check for closed streaming client connection */		
		if (have_Streamclient==true) { 
			if (close_Streamclient_request==true) {
				close_Streamclient_request = false;
				have_Streamclient = false;
				if ( m_StreamClient->Close() ) {
#ifdef DEBUG
					fprintf(stderr, "[ffnetdev] Streamer: Client socket closed successfully.\n");
#endif
					isyslog("[ffnetdev] Streamer: Client socket closed.");
				}
				else
					{
#ifdef DEBUG
					   fprintf(stderr, "[ffnetdev] Streamer: Error closing client socket.\n");
#endif
					   esyslog("[ffnetdev] Streamer: Error closing connection.");
					   m_Active=false;
					   continue;
					} 
					  
			}
			
			if ( select.CanWrite(*m_StreamClient) ) {
				int count=0;
				//if ( (m_StreamDevice->Get(count)) !=NULL ) {
				//	m_StreamDevice->Del(count);
				//	fprintf(stderr,"[ffnetdev] Streamer: deleting %d bytes.\n",count);
				//}

      				uchar *buffer = m_StreamDevice->Get(count);
				//fprintf(stderr,"[ffnetdev] Streamer: Got %d bytes from ringbuffer.\n",count);
				
				int written=0;
				if (buffer!=NULL) {
					if ( !(written=m_StreamClient->Write(buffer, count)) ) {
#ifdef DEBUG
						fprintf(stderr, "[ffnetdev] Streamer: Client closed connection.\n");
#endif
						isyslog("[ffnetdev] Streamer: Connection closed: client %s:%d",
							 m_StreamClient->RemoteIp().c_str(), m_StreamClient->RemotePort());
						have_Streamclient=false;
						m_StreamClient->Close();
					} 
					m_StreamDevice->Del(written);
				}
				
			}
			
			if ( select.CanRead(*m_StreamClient) )
				if ( m_StreamClient->Read(NULL, 1)==0 ) {
					fprintf(stderr, "[ffnetdev] Streamer: Client closed connection.\n");
					isyslog("[ffnetdev] Streamer: Connection closed: client %s:%d",
						 m_StreamClient->RemoteIp().c_str(), m_StreamClient->RemotePort());
					have_Streamclient=false;
					m_StreamClient->Close();
				}
				
		} 
		else {  
			/* simply discard all data in ringbuffer */
			int count=0;
			if ( (m_StreamDevice->Get(count)) !=NULL ) {
				m_StreamDevice->Del(count);
#ifdef DEBUG
				fprintf (stderr, "[ffnetdev] Streamer: Bytes not sent, but deleted from ringbuffer: %d\n",count);
#endif
			}
		}
		cCondWait::SleepMs(3);

	} // while(m_Active)

}
