/***************************************************************************
                          clistenmanager.cpp  -  description
                             -------------------
    begin                : Sun Feb 29 2004
    copyright            : (C) 2004 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "clistenmanager.h"

#include <stdio.h>

#ifdef WIN32
#include <winsock2.h>
#else
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#endif

#include "dcos.h"
#include "dcobject.h"
#include "core/clisten.h"
#include "core/cmanager.h"
#include "cdownloadmanager.h"
#include "cconfig.h"

/* most of these messages are normal and just informative */
#include "dclib.h"

/** */
CListenManagerBase::CListenManagerBase()
{
	m_pListen = 0;
	m_nListenPort = 0;
	
	m_bCrypto = false;
	
	m_pCallback = 0;
}

/** */
CListenManagerBase::~CListenManagerBase()
{
	m_Mutex.Lock();
	
	if ( m_pCallback )
	{
		if ( CManager::Instance() )
			CManager::Instance()->Remove( m_pCallback );
		delete m_pCallback;
		m_pCallback = 0;
	}
	
	delete m_pListen;
	m_pListen = 0;
	
	m_Mutex.UnLock();
}

/** */
unsigned int CListenManagerBase::GetListenPort()
{
	unsigned int n;
	
	m_Mutex.Lock();
	
	n = m_nListenPort;
	
	m_Mutex.UnLock();
	
	return n;
}

/** */
void CListenManagerBase::StartListen()
{
	int n;

	delete m_pListen;
	m_pListen = 0;
	
	// reset listen port
	m_nListenPort = 0;

	// sanity check
	if ( CConfig::Instance() )
	{
		if ( m_bCrypto )
		{
			n = CConfig::Instance()->GetCryptoListenPort();
		}
		else
		{
			n = CConfig::Instance()->GetTCPListenPort();
		}
		
		if ( (n != 0) && (CConfig::Instance()->GetMode(true) == ecmACTIVE) )
		{
			if ( m_bCrypto )
			{
				DPRINTF("CCryptoListenManager");
			}
			else
			{
				DPRINTF("CListenManager");
			}
			DPRINTF("::StartListen: active mode port = %d\n",n);
			
			m_pListen = new CListen();
			
			// start listen
			if ( m_pListen->StartListen( n, CConfig::Instance()->GetListenHostString() ) == 0 )
			{
				m_pListen->SetCallBackFunction( CreateListenCallback() );
			
				m_nListenPort = n;
				m_sSocketError.Empty();
			}
			else
			{
				// print error message only one time
				if ( m_pListen->GetSocketError() != m_sSocketError )
				{
					m_sSocketError = m_pListen->GetSocketError();
					if ( m_bCrypto )
					{
						printf("CCryptoListenManager");
					}
					else
					{
						printf("CListenManager");
					}
					printf("::StartListen error: '%s'\n",m_sSocketError.Data());
				}
			}
		}
		else
		{
			if ( m_bCrypto )
			{
				DPRINTF("CCryptoListenManager");
			}
			else
			{
				DPRINTF("CListenManager");
			}
			DPRINTF("::StartListen: passive mode\n");
			if ( m_sSocketError.NotEmpty() )
			{
				m_sSocketError.Empty();
			}
		}
	}
}

/** */
int CListenManagerBase::ManagerCallback()
{
	m_Mutex.Lock();

	if ( CConfig::Instance() )
	{
		if ( m_pListen )
		{
			if ( CConfig::Instance()->GetMode(true) == ecmACTIVE )
			{
				if ( m_pListen->IsConnect() == -1 )
				{
					StartListen();
				}
				
				if ( m_bCrypto )
				{
					if ( CConfig::Instance()->GetCryptoListenPort() != m_nListenPort )
					{
						StartListen();
					}
				}
				else
				{
					if ( CConfig::Instance()->GetTCPListenPort() != m_nListenPort )
					{
						StartListen();
					}
				}
			}
			else
			{
				StartListen();
			}
		}
		else
		{
			if ( CConfig::Instance()->GetMode(true) == ecmACTIVE )
			{
				StartListen();
			}
		}
	}
	
	m_Mutex.UnLock();

	return 0;
}

/** */
int CListenManagerBase::ListenCallback( int handle )
{
	m_Mutex.Lock();

	if ( m_bCrypto )
	{
		DPRINTF("CCryptoListenManager");
	}
	else
	{
		DPRINTF("CListenManager");
	}
	DPRINTF(": incoming connection %d\n",handle);
	
	if ( CDownloadManager::Instance() )
	{
		CDownloadManager::Instance()->ListenCallbackHandler( handle, m_bCrypto );
	}
	else
	{
#ifdef WIN32
		closesocket(handle);
#else
		close(handle);
#endif
	}

	m_Mutex.UnLock();

	return 0;
}

/** */
CListenManager::CListenManager()
{
	m_bCrypto = false;
	
	m_pCallback = new CCallback0<CListenManager>( this, &CListenManager::ManagerCallback );
	CManager::Instance()->Add( m_pCallback );
}

/** */
CListenManager::~CListenManager()
{
	SetInstance(0);
}

/** */
_CCallback1<int> * CListenManager::CreateListenCallback()
{
	return new CCallback1<CListenManager, int>( this, &CListenManager::ListenCallback );
}

/** */
CCryptoListenManager::CCryptoListenManager()
{
	m_bCrypto = true;
	
	m_pCallback = new CCallback0<CCryptoListenManager>( this, &CCryptoListenManager::ManagerCallback );
	CManager::Instance()->Add( m_pCallback );
}

/** */
CCryptoListenManager::~CCryptoListenManager()
{
	SetInstance(0);
}

/** */
_CCallback1<int> * CCryptoListenManager::CreateListenCallback()
{
	return new CCallback1<CCryptoListenManager, int>( this, &CCryptoListenManager::ListenCallback );
}
