/*
 *  $Id: server.c,v 1.5 2007/04/02 10:32:34 schmirl Exp $
 */

#include "server/server.h"
#include "server/componentVTP.h"
#include "server/componentHTTP.h"
#include "server/setup.h"

#include <vdr/tools.h>
#include <tools/select.h>
#include <string.h>
#include <errno.h>

cSVDRPhosts StreamdevHosts;

cStreamdevServer         *cStreamdevServer::m_Instance = NULL;
cList<cServerComponent>   cStreamdevServer::m_Servers;
cList<cServerConnection>  cStreamdevServer::m_Clients;

cStreamdevServer::cStreamdevServer(void):
		cThread("streamdev server"),
		m_Active(false)
{
	Start();
}

cStreamdevServer::~cStreamdevServer() 
{
	Stop();
}

void cStreamdevServer::Initialize(void) 
{
	if (m_Instance == NULL) {
		if (StreamdevServerSetup.StartVTPServer)  Register(new cComponentVTP);
		if (StreamdevServerSetup.StartHTTPServer) Register(new cComponentHTTP);

		m_Instance = new cStreamdevServer;
	}
}

void cStreamdevServer::Destruct(void) 
{
	DELETENULL(m_Instance);
}

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

void cStreamdevServer::Register(cServerComponent *Server) 
{
	m_Servers.Add(Server);
}

void cStreamdevServer::Action(void) 
{
	m_Active = true;

	/* Initialize Server components, deleting those that failed */
	for (cServerComponent *c = m_Servers.First(); c;) {
		cServerComponent *next = m_Servers.Next(c);
		if (!c->Initialize())
			m_Servers.Del(c);
		c = next;
	}
			
	if (m_Servers.Count() == 0) {
		esyslog("ERROR: no streamdev server activated, exiting");
		m_Active = false;
	}

	cTBSelect select;
	while (m_Active) {
		select.Clear();

		/* Ask all Server components to register to the selector */
		for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c))
			select.Add(c->Socket(), false);
		
		/* Ask all Client connections to register to the selector */
		for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s))
		{
			select.Add(s->Socket(), false);
			if (s->HasData())
				select.Add(s->Socket(), true);
		}

		int sel;
		do
		{
			sel = select.Select(400);
			if (sel < 0 && errno == ETIMEDOUT) {
				// check for aborted clients
				for (cServerConnection *s = m_Clients.First(); s; s = m_Clients.Next(s)) {
					if (s->Abort())
						sel = 0;
				}
			}
		} while (sel < 0 && errno == ETIMEDOUT && m_Active);

		if (!m_Active)
			break;
		if (sel < 0) {
			esyslog("fatal error, server exiting: %m");
			break;
		}
	
		/* Ask all Server components to act on signalled sockets */
		for (cServerComponent *c = m_Servers.First(); c; c = m_Servers.Next(c)){
			if (sel && select.CanRead(c->Socket())) {
				cServerConnection *client = c->Accept();
				m_Clients.Add(client);

				if (m_Clients.Count() > StreamdevServerSetup.MaxClients) {
					esyslog("streamdev: too many clients, rejecting %s:%d",
					        client->RemoteIp().c_str(), client->RemotePort());
					client->Reject();
				} else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
					esyslog("streamdev: client %s:%d not allowed to connect",
					        client->RemoteIp().c_str(), client->RemotePort());
					client->Reject();
				} else 
					client->Welcome();
			}
		}

		/* Ask all Client connections to act on signalled sockets */
		for (cServerConnection *s = m_Clients.First(); s;) {
			bool result = true;

			if (sel && select.CanWrite(s->Socket()))
				result = s->Write();

			if (sel && result && select.CanRead(s->Socket()))
				result = s->Read();

			result &= !s->Abort();
			
			cServerConnection *next = m_Clients.Next(s);
			if (!result) {
				isyslog("streamdev: closing streamdev connection to %s:%d", 
				        s->RemoteIp().c_str(), s->RemotePort());
				s->Close();
				m_Clients.Del(s);
			}
			s = next;
		}
	}
	
	while (m_Clients.Count() > 0) {
		cServerConnection *s = m_Clients.First();
		s->Close();
		m_Clients.Del(s);
	}

	while (m_Servers.Count() > 0) {
		cServerComponent *c = m_Servers.First();
		c->Destruct();
		m_Servers.Del(c);
	}

	m_Active = false;
}