/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
 * 
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file COPYING that should have accompanied this file.
 * 
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "CClientListener.h"
#include "CClientProxy.h"
#include "CClientProxyUnknown.h"
#include "CPacketStreamFilter.h"
#include "IStreamFilterFactory.h"
#include "IDataSocket.h"
#include "IListenSocket.h"
#include "ISocketFactory.h"
#include "XSocket.h"
#include "CLog.h"
#include "IEventQueue.h"
#include "TMethodEventJob.h"

//
// CClientListener
//

CEvent::Type			CClientListener::s_connectedEvent = CEvent::kUnknown;

CClientListener::CClientListener(const CNetworkAddress& address,
				ISocketFactory* socketFactory,
				IStreamFilterFactory* streamFilterFactory) :
	m_socketFactory(socketFactory),
	m_streamFilterFactory(streamFilterFactory)
{
	assert(m_socketFactory != NULL);

	try {
		// create listen socket
		m_listen = m_socketFactory->createListen();

		// bind listen address
		LOG((CLOG_DEBUG1 "binding listen socket"));
		m_listen->bind(address);
	}
	catch (XSocketAddressInUse&) {
		delete m_listen;
		delete m_socketFactory;
		delete m_streamFilterFactory;
		throw;
	}
	catch (XBase&) {
		delete m_listen;
		delete m_socketFactory;
		delete m_streamFilterFactory;
		throw;
	}
	LOG((CLOG_DEBUG1 "listening for clients"));

	// setup event handler
	EVENTQUEUE->adoptHandler(IListenSocket::getConnectingEvent(), m_listen,
							new TMethodEventJob<CClientListener>(this,
								&CClientListener::handleClientConnecting));
}

CClientListener::~CClientListener()
{
	LOG((CLOG_DEBUG1 "stop listening for clients"));

	// discard already connected clients
	for (CNewClients::iterator index = m_newClients.begin();
								index != m_newClients.end(); ++index) {
		CClientProxyUnknown* client = *index;
		EVENTQUEUE->removeHandler(
							CClientProxyUnknown::getSuccessEvent(), client);
		EVENTQUEUE->removeHandler(
							CClientProxyUnknown::getFailureEvent(), client);
		EVENTQUEUE->removeHandler(
							CClientProxy::getDisconnectedEvent(), client);
		delete client;
	}

	// discard waiting clients
	CClientProxy* client = getNextClient();
	while (client != NULL) {
		delete client;
		client = getNextClient();
	}

	EVENTQUEUE->removeHandler(IListenSocket::getConnectingEvent(), m_listen);
	delete m_listen;
	delete m_socketFactory;
	delete m_streamFilterFactory;
}

CClientProxy*
CClientListener::getNextClient()
{
	CClientProxy* client = NULL;
	if (!m_waitingClients.empty()) {
		client = m_waitingClients.front();
		m_waitingClients.pop_front();
		EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(), client);
	}
	return client;
}

CEvent::Type
CClientListener::getConnectedEvent()
{
	return EVENTQUEUE->registerTypeOnce(s_connectedEvent,
							"CClientListener::connected");
}

void
CClientListener::handleClientConnecting(const CEvent&, void*)
{
	// accept client connection
	IStream* stream = m_listen->accept();
	if (stream == NULL) {
		return;
	}
	LOG((CLOG_NOTE "accepted client connection"));

	// filter socket messages, including a packetizing filter
	if (m_streamFilterFactory != NULL) {
		stream = m_streamFilterFactory->create(stream, true);
	}
	stream = new CPacketStreamFilter(stream, true);

	// create proxy for unknown client
	CClientProxyUnknown* client = new CClientProxyUnknown(stream, 30.0);
	m_newClients.insert(client);

	// watch for events from unknown client
	EVENTQUEUE->adoptHandler(CClientProxyUnknown::getSuccessEvent(), client,
							new TMethodEventJob<CClientListener>(this,
								&CClientListener::handleUnknownClient, client));
	EVENTQUEUE->adoptHandler(CClientProxyUnknown::getFailureEvent(), client,
							new TMethodEventJob<CClientListener>(this,
								&CClientListener::handleUnknownClient, client));
}

void
CClientListener::handleUnknownClient(const CEvent&, void* vclient)
{
	CClientProxyUnknown* unknownClient =
		reinterpret_cast<CClientProxyUnknown*>(vclient);

	// we should have the client in our new client list
	assert(m_newClients.count(unknownClient) == 1);

	// get the real client proxy and install it
	CClientProxy* client = unknownClient->orphanClientProxy();
	if (client != NULL) {
		// handshake was successful
		m_waitingClients.push_back(client);
		EVENTQUEUE->addEvent(CEvent(getConnectedEvent(), this));

		// watch for client to disconnect while it's in our queue
		EVENTQUEUE->adoptHandler(CClientProxy::getDisconnectedEvent(), client,
							new TMethodEventJob<CClientListener>(this,
								&CClientListener::handleClientDisconnected,
								client));
	}

	// now finished with unknown client
	EVENTQUEUE->removeHandler(CClientProxyUnknown::getSuccessEvent(), client);
	EVENTQUEUE->removeHandler(CClientProxyUnknown::getFailureEvent(), client);
	m_newClients.erase(unknownClient);
	delete unknownClient;
}

void
CClientListener::handleClientDisconnected(const CEvent&, void* vclient)
{
	CClientProxy* client = reinterpret_cast<CClientProxy*>(vclient);

	// find client in waiting clients queue
	for (CWaitingClients::iterator i = m_waitingClients.begin(),
							n = m_waitingClients.end(); i != n; ++i) {
		if (*i == client) {
			m_waitingClients.erase(i);
			EVENTQUEUE->removeHandler(CClientProxy::getDisconnectedEvent(),
							client);
			delete client;
			break;
		}
	}
}
