/**
 * @file TcpipServer.cpp
  * @author Tomas Urban
 * @version 0.3
 * @date 23/07/2009
 */
#include "TcpipServer.h"
#include <iostream>
#include <boost/thread/thread.hpp>
#include <signal.h>
#include "Logger/Logger.h"

using namespace std;

TcpipServer::TcpipServer(void) :
	m_hListeningSocket (SOCKET_CALL_ERROR)
{
}

TcpipServer::~TcpipServer(void)
{
}

void TcpipServer::Run(int nPort)
{
	signal(SIGINT, OnExit);

	Logger::Instance().LogDebug("Initializing TCP/IP connection...");
	
	// create socket and start listening
	struct sockaddr_in srvAddr;

	memset(&srvAddr, 0, sizeof srvAddr);
	srvAddr.sin_family = AF_INET;
	srvAddr.sin_addr.s_addr = INADDR_ANY;
	srvAddr.sin_port = htons(nPort);

	if ( (m_hListeningSocket = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_CALL_ERROR )
	{
		Logger::Instance().LogError("Error creating listening socket");
		return;
	}
	Logger::Instance().LogDebug("Listening socket successfully created");

	if (bind(m_hListeningSocket, reinterpret_cast<sockaddr*> (&srvAddr), sizeof(srvAddr)) != 0)
	{
		Logger::Instance().LogError("Error binding listening socket; the port is probably used by other process");
		return;
	}

	Logger::Instance().LogDebug("Listening socket successfully bound");

	if (listen(m_hListeningSocket, 0) != 0 )
	{
		Logger::Instance().LogError("Listening operation failed");
		return;		
	}

	std::string s = "Waiting for connection request on port ";
	s += boost::lexical_cast<std::string>(nPort);
	s += "...";
	Logger::Instance().LogInfo(s);

	struct sockaddr_in clientAddr;
	while (-1)
	{
		int nLen = sizeof(clientAddr);
		SOCKET_TYPE sock = accept(
			m_hListeningSocket, reinterpret_cast<sockaddr*>(&clientAddr), &nLen);
		if (sock == SOCKET_CALL_ERROR)
			break;
		Logger::Instance().LogInfo("Connection accepted");
		Logger::Instance().LogDebug("Starting socket thread...");
		ConnectionHandler handler(sock);
		boost::thread thread(handler);
	}
	OnExit();
}

TcpipServer::ConnectionHandler::ConnectionHandler(SOCKET_TYPE hSocket) :
	m_hSocket(hSocket)
{
}

void TcpipServer::ConnectionHandler::operator()()
{
	Logger::Instance().LogDebug("Socket thread started successfully");

	ConnectionController cc(m_hSocket);
	{
		boost::mutex::scoped_lock lock(TcpipServer::Instance().m_mutex);
		TcpipServer::Instance().m_lActiveControllers.push_back(&cc);
	}
	cc.Run();
	{
		boost::mutex::scoped_lock lock(TcpipServer::Instance().m_mutex);
		TcpipServer::Instance().m_lActiveControllers.remove(&cc);
	
		if (m_hSocket != SOCKET_CALL_ERROR)
		{
			CLOSE_SOCKET(m_hSocket);
			m_hSocket = SOCKET_CALL_ERROR;
		}
		Logger::Instance().LogDebug("Socket thread terminated");
		TcpipServer::Instance().m_operationEnded.notify_one();
	}
}

void TcpipServer::OnExit()
{
	if (m_hListeningSocket != SOCKET_CALL_ERROR)
	{
		{
			boost::mutex::scoped_lock lock(m_mutex);
			for(std::list<ConnectionController *>::iterator it = m_lActiveControllers.begin(); 
				it != m_lActiveControllers.end(); it++)
				(*it)->Stop();
		}
		// wait for threads to quit
		while (-1)
		{
			boost::mutex::scoped_lock cond(m_mutex);
			if (m_lActiveControllers.begin() == m_lActiveControllers.end())
				break;
			m_operationEnded.wait(cond);
		}
		CLOSE_SOCKET(m_hListeningSocket);
		m_hListeningSocket = SOCKET_CALL_ERROR;
		Logger::Instance().LogDebug("Listening socket closed");
	}
}

void TcpipServer::OnExit(int nSig)
{
	Instance().OnExit();
}
