//
// This file is part of the aMule Project.
//
// Copyright (c) 2003-2009 aMule Team ( admin@amule.org / http://www.amule.org )
// Copyright (c) 2002 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// 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.
//
// This program 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, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//

#include "UploadQueue.h"	// Interface declarations

#include <protocol/Protocols.h>
#include <protocol/ed2k/Client2Client/TCP.h>
#include <common/Macros.h>
#include <common/Constants.h>

#include <cmath>

#include "Types.h"		// Do_not_auto_remove (win32)

#ifdef __WXMSW__
	#include <winsock.h>	// Do_not_auto_remove
#else
	#include <sys/types.h>	// Do_not_auto_remove
	#include <netinet/in.h>	// Do_not_auto_remove
	#include <arpa/inet.h>	// Do_not_auto_remove
#endif

#include "ServerConnect.h"	// Needed for CServerConnect
#include "KnownFile.h"		// Needed for CKnownFile
#include "Packet.h"		// Needed for CPacket
#include "ClientTCPSocket.h"	// Needed for CClientTCPSocket
#include "SharedFileList.h"	// Needed for CSharedFileList
#include "updownclient.h"	// Needed for CUpDownClient
#include "amule.h"		// Needed for theApp
#include "Preferences.h"
#include "ClientList.h"
#include "Statistics.h"		// Needed for theStats
#include "Logger.h"
#include <common/Format.h>
#include "UploadBandwidthThrottler.h"
#include "GuiEvents.h"		// Needed for Notify_*

// ADUNANZA BEGIN
#ifndef KADCONSTANTS_H
#include <protocol/kad/Constants.h>
#endif //KADCONSTANTS_H
// ADUNANZA END

//TODO rewrite the whole networkcode, use overlapped sockets

CUploadQueue::CUploadQueue()
{
	m_nLastStartUpload = 0;

	lastupslotHighID = true;
// ADUNANZA BEGIN
	m_allowKicking = true; // patch forum.amule.org
	// mod Adu
	// Emanem
	// Inizializzo il numero di clients Adu nella waiting queue
	m_AduClientsNum = 0;
	// fine mod Adu
// ADUNANZA END
}


// Mr Hyde test
class CAduClientTypeChooser
{
	protected:
		uint32 m_current;

	private:
		inline void doswitch() { m_current = ((m_current == ADUNANZA_FASTWEB) ? ADUNANZA_EXTERN : ADUNANZA_FASTWEB); }

	protected:
		CAduClientTypeChooser() : m_current(ADUNANZA_EXTERN) {};
		~CAduClientTypeChooser() {};

		inline uint32 get() const {return m_current;}
		inline uint32 getNext() const { return ((m_current == ADUNANZA_FASTWEB) ? ADUNANZA_EXTERN : ADUNANZA_FASTWEB); }


		void advance(uint32 desired) {
			switch (desired)
			{
				case ADUNANZA_NONE:
					return;

				case ADUNANZA_ANY:
					return;

				default:
					doswitch();
					return;
			}
		}

		void setCurrent(const CUpDownClient* pCurrent)
		{
			if (!pCurrent) return;
			m_current = (pCurrent->IsAdunanzA() ? ADUNANZA_FASTWEB : ADUNANZA_EXTERN);
		}
};


// Mr Hyde: la patch originale prevedeva anche il parametro
//	 uint32 tC
// che pero' non era usato.
// Rimosso.
void CUploadQueue::AddUpNextClient(CUpDownClient* directadd)
{
	CClientPtrList::iterator toadd = m_waitinglist.end();
	CClientPtrList::iterator toaddlow = m_waitinglist.end();
	
	uint32_t bestscore = 0;
	uint32_t bestlowscore = 0;
// ADUNANZA BEGIN
	// Mod Adu
	// lupz
	// capisco cosa voglio
	uint32 typeClient = CAdunanzAUtilities::AduNextClient();
	// uint32 typeClient = theApp->pUploadSlotsMng->next();

	bool bDontAdd(false);
	//AddDebugLogLineM(false, logClient, CFormat(wxT("voglio: %u")) % typeClient);
	if (typeClient == ADUNANZA_NONE)
	{
		//return;
		bDontAdd = true;
	}
// ADUNANZA END

	CUpDownClient* newclient;
	// select next client or use given client
	if (!directadd) {
// ADUNANZA BEGIN
		// mod Adu
		// Emanem
		// Risistemo il valore temporaneo del numero di clients
		// AdunanzA
		uint32 _tmp_m_AduClientsNum = 0;
		// fine mod Adu
// ADUNANZA END
		// Track if we purged any clients from the queue, as to only send one notify in total
		bool purged = false;
		
		CClientPtrList::iterator it = m_waitinglist.begin();
		for (; it != m_waitinglist.end(); ) {
			CClientPtrList::iterator tmp_it = it++;
			CUpDownClient* cur_client = *tmp_it;

			// clear dead clients
			if ( (::GetTickCount() - cur_client->GetLastUpRequest() > MAX_PURGEQUEUETIME) || !theApp->sharedfiles->GetFileByID(cur_client->GetUploadFileID()) ) {
				purged = true;
				cur_client->ClearWaitStartTime();
				RemoveFromWaitingQueue(tmp_it);
				if (!cur_client->GetSocket()) {
					if(cur_client->Disconnected(wxT("AddUpNextClient - purged"))) {
						cur_client->Safe_Delete();
						cur_client = NULL;
					}
				}
				continue;
			} 

// ADUNANZA BEGIN
// Codice originale un po' strano: fa comunque il find e SOLO DOPO si chiede se cir_client e' banned e, in quel caso, salta al prox.
// Insomma, se cur_client e' banned perde IMHO tempo a fare un find inutile!			
#if 0
			suspendlist::iterator it2 = std::find( suspended_uploads_list.begin(),
			                                      suspended_uploads_list.end(),
			                                      cur_client->GetUploadFileID() );
			if (cur_client->IsBanned() || it2 != suspended_uploads_list.end() ) { // Banned client or suspended upload ?
			        continue;
			}
#else
			if (cur_client->IsBanned()) {
			       	continue; // skip banned client
			}
			suspendlist::iterator it2 = std::find( suspended_uploads_list.begin(),
			                                      suspended_uploads_list.end(),
			                                      cur_client->GetUploadFileID() );
			if (it2 != suspended_uploads_list.end() ) { // current client is also a "suspended upload", skip it
			        continue;
			}
#endif
			// a questo punto posso controllare, al momento, quanta roba ho in coda di attesa
			size_t theWaitSize = m_waitinglist.size();
// ADUNANZA END			

			// finished clearing
			
			uint32_t cur_score = cur_client->GetScore(true);
// ADUNANZA BEGIN
			// Codice AdunanzA
			// Emanem	18:15	4/4/2004
			//
			// In questo caso, in base al tipo di typeClient
			// in input si sceglie su che clients fare la "classifica"
			/// di priorita'
			bool bIsCurrentAdunanzA(cur_client->IsAdunanzA());	
			// nel caso incremento il contatore temporaneo
			if (bIsCurrentAdunanzA) {// ADUFLAGS 
				_tmp_m_AduClientsNum++;
			}
			// Se il tipo di client non e' disponibile
			// allora prendo il primo che mi capita

			if (typeClient == ADUNANZA_FASTWEB) {

				// Voglio un client ADU/FASTWEB
				if (m_AduClientsNum == 0) {
					// Non mi risulta che ci siano client adu/fastweb
					// ne prendo uno qualsiasi
					typeClient = ADUNANZA_ANY;
				}
			} 
			else if (typeClient == ADUNANZA_EXTERN) {
				// Voglio un client *NON* ADU/FASTWEB
				if (m_AduClientsNum >= theWaitSize) {
					// Non mi risulta che ci siano client *NON* adu/fastweb
					// ne prendo uno qualsiasi
					typeClient = ADUNANZA_ANY;
				}
			}

			bool bHandleThisClient(false);
			switch(typeClient) {
				case ADUNANZA_EXTERN:
					bHandleThisClient = !bIsCurrentAdunanzA; // ADUFLAGS
					break;
				case ADUNANZA_FASTWEB:
					bHandleThisClient = bIsCurrentAdunanzA; // ADUFLAGS
					break;

				case ADUNANZA_ANY:
					// Va bene un client qualsiasi
					bHandleThisClient = true;
					break;
				default:
					bHandleThisClient = false; // in questo caso non devo fare niente! (e' NONE)
					break;
			}

			// test
			if (bDontAdd) bHandleThisClient = false;

			if (bHandleThisClient) {
// ADUNANZA END
			if (cur_score > bestscore) {
				bestscore = cur_score;
				toadd = tmp_it;
			} else {
				cur_score = cur_client->GetScore(false);
				if ((cur_score > bestlowscore) && !cur_client->m_bAddNextConnect){
					bestlowscore = cur_score;
					toaddlow = tmp_it;
				}
			}
// ADUNANZA BEGIN			
			} // if (bHandleThisClient)
// ADUNANZA END
		}

// ADUNANZA BEGIN
		// mod Adu
		// Emanem
		// risistemo il numero di clients AdunanzA
		m_AduClientsNum = _tmp_m_AduClientsNum;
		// VOLUTO COMM m_AduClientsNum = _bis_m_AduClientsNum;
// ADUNANZA END

		// Update the count on GUI if any clients were purged
		if (purged) {
			Notify_ShowQueueCount(m_waitinglist.size());
		}

		if (bestlowscore > bestscore){
			newclient = *toaddlow;
			newclient->m_bAddNextConnect = true;
		}

		if (toadd == m_waitinglist.end()) {
			return;
		}
		
		newclient = *toadd;
		lastupslotHighID = true; // VQB LowID alternate
		RemoveFromWaitingQueue(toadd);
		Notify_ShowQueueCount(m_waitinglist.size());
	} else {
		//prevent another potential access of a suspended upload

		suspendlist::iterator it = std::find( suspended_uploads_list.begin(),
		                                      suspended_uploads_list.end(),
		                                      directadd->GetUploadFileID() );
		if ( it != suspended_uploads_list.end() ) {
			return;
		} else {
			newclient = directadd;
		}
	}

	if (IsDownloading(newclient)) {
		return;
	}

	// tell the client that we are now ready to upload
	if (!newclient->IsConnected()) {
		newclient->SetUploadState(US_CONNECTING);
		if (!newclient->TryToConnect(true)) {
			return;
		}
	} else {
		CPacket* packet = new CPacket(OP_ACCEPTUPLOADREQ, 0, OP_EDONKEYPROT);
		theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
		AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_ACCEPTUPLOADREQ to ") + newclient->GetFullIP() );
		newclient->SendPacket(packet,true);
		newclient->SetUploadState(US_UPLOADING);
	}
	newclient->SetUpStartTime();
	newclient->ResetSessionUp();

	theApp->uploadBandwidthThrottler->AddToStandardList(m_uploadinglist.size(), newclient->GetSocket());
	m_uploadinglist.push_back(newclient);
// ADUNANZA BEGIN
	// Mod Adu
	// lupz
	if (newclient->IsAdunanzA()) {
		aduuploadinglist.push_back(newclient);
	}
		// *** INSERIRE QUI CODICE PER RIPARTITORE BANDA

	time_t nowTime = time(NULL);

	if (thePrefs::ADU_GetUpExtFull() && !newclient->IsAdunanzA() && (thePrefs::ADU_GetExtFullWaitTime() < nowTime)) {
		time_t tempotest = nowTime - thePrefs::ADU_GetExtFullWaitTime() + thePrefs::ADU_GetMaxExtWaitTime();
		// uint64 ExtSentPartial = (theStats::GetSessionSentBytes() - thePrefs::ADU_GetExtAdapterSentBytes()) / 1024;
		uint64 ExtSentPartial = ((theStats::GetSessionSentBytes() - thePrefs::ADU_GetExtAdapterSentBytes()) >> 10);

		uint64 partial =  (ExtSentPartial / tempotest);
		if (partial > 10) {
			partial -= 10;
		}

		partial = std::max<uint64>(partial, ADUNANZA_MIN_BW_TROLLER);


		if (thePrefs::ADU_GetRipBanda()) {
			if (partial < thePrefs::ADU_GetValueRipBandaBAK()) {
				AddDebugLogLineM(false, logClient, CFormat(wxT("(DBG Mr Hyde) ValueRipBanda set to %u ")) % partial);
				thePrefs::ADU_SetOnlyValueRipBanda(partial); // NOTA DI Mr Hyde: _SOLO_ ValueRipBanda, il ValueRipBandaBAK deve rimanere immutato
				CAdunanzAUtilities::CalcolaRatio(false);
			}

		}

		thePrefs::ADU_SetCheckExtFull(true);
	}

	
	if ((GetUploadCount() == CAdunanzAUtilities::TotSlots()) && (GetAduUploadCount() == 0) && (!newclient->IsAdunanzA())) {
		thePrefs::ADU_SetUpExtFull(true);
		if (thePrefs::ADU_GetCheckExtFull()) {
			thePrefs::ADU_SetExtFullWaitTime(thePrefs::ADU_GetMaxExtWaitTime() + nowTime);
			thePrefs::ADU_SetCheckExtFull(false);
			thePrefs::ADU_SetExtAdapterSentBytes(theStats::GetSessionSentBytes());
		}
	}
	else {
		thePrefs::ADU_SetUpExtFull(false);
		thePrefs::ADU_SetCheckExtFull(true);
	}


	// Fine mod Adu
// ADUNANZA END
	theStats::AddUploadingClient();

	// Statistic
	CKnownFile* reqfile = (CKnownFile*) newclient->GetUploadFile();
	if (reqfile) {
		reqfile->statistic.AddAccepted();
	}
	Notify_UploadCtrlAddClient(newclient);
}

void CUploadQueue::Process()
{
// ADUNANZA BEGIN
#if 0
	if (AcceptNewClient() && !m_waitinglist.empty()) {
		m_nLastStartUpload = ::GetTickCount();
		AddUpNextClient();
	}
#else
	// aggiorno conteggio attuale slot (da ottimizzare per evitare di fare milioni di volte gli stessi calcoli)
	if (theApp && theApp->pUploadSlotsMng) theApp->pUploadSlotsMng->update(m_uploadinglist);

/*	
	bool bExhaustedUploadBandwidth(false);
	{
		uint32 maxAllowedUpload_KB_sec = thePrefs::GetMaxUpload();

		// effettuo il controllo solo se:
		// - l'utente h messo un limite alla banda in upload
		if (maxAllowedUpload_KB_sec != UNLIMITED) {
			// ho un limite di banda in up
			// controllo di non averlo già raggiunto
			uint32 currentUpload_KB_sec = (uint32) (theStats::GetUploadRate() / 1024.0); // non posso usare lo shift perche' usa double
			if (currentUpload_KB_sec >= maxAllowedUpload_KB_sec) {
				bExhaustedUploadBandwidth = true;
			}
		}
	}
*/	
	// di seguito anche backport da aMule SVN per il kicking
	// in caso di slot completamente occupati
	//
	// Check if someone's waiting, if there is a slot for him, 
	// or if we should try to free a slot for him
	uint32 tick = GetTickCount();

/*
	if ( bExhaustedUploadBandwidth) {
		m_allowKicking = true;
	}
	// se la waitinglist e' vuota o e' passato meno di un secondo dall'ultima volta
	// NON permetto il kicking di client
	else*/ 
	if (m_waitinglist.empty() || tick - m_nLastStartUpload < 1000) {
		m_allowKicking = false;
	} else if (/*(CAdunanzAUtilities::AduNextClient() != ADUNANZA_NONE) &&*/ (m_uploadinglist.size() < GetMaxSlots())) {
		// se non ho ancora esaurito gli slot e posso fare upload NON permetto il kicking
		m_allowKicking = false;
		m_nLastStartUpload = tick;
		AddUpNextClient();
	// All slots taken, try to free one
	} else {
		m_allowKicking = true;
	}
	

#endif
// ADUNANZA END

	// The loop that feeds the upload slots with data.
	CClientPtrList::iterator it = m_uploadinglist.begin();
// ADUNANZA BEGIN
	uint64 aduSentBytes      = 0;
       	uint64 fakeSentBytes     = 0;
       	uint64 aduFiberSentBytes = 0;
// ADUNANZA END
	while (it != m_uploadinglist.end()) {
		// Get the client. Note! Also updates pos as a side effect.
		CUpDownClient* cur_client = *it++;
		
		// It seems chatting or friend slots can get stuck at times in upload.. This needs looked into..
		if (!cur_client->GetSocket()) {
			RemoveFromUploadQueue(cur_client, true);
			if(cur_client->Disconnected(_T("CUploadQueue::Process"))){
				cur_client->Safe_Delete();
			}
		} else {
// ADUNANZA BEGIN
#if 0
			cur_client->SendBlockData();
#else
			uint64 loc_sentBytes = cur_client->SendBlockData();

			if (cur_client->IsAdunanzA()) { // ADUFLAGS
				aduSentBytes += loc_sentBytes;
				if (cur_client->IsFastwebFiber()) { // ADUFLAGS
					aduFiberSentBytes += loc_sentBytes;
				}
			}
			fakeSentBytes += loc_sentBytes;
#endif
// ADUNANZA END
		}
	}

	// Save used bandwidth for speed calculations
	uint64 sentBytes = theApp->uploadBandwidthThrottler->GetNumberOfSentBytesSinceLastCallAndReset();
	(void)theApp->uploadBandwidthThrottler->GetNumberOfSentBytesOverheadSinceLastCallAndReset();

	// Update statistics
	if (sentBytes) {
// ADUNANZA BEGIN
#if 0
		theStats::AddSentBytes(sentBytes);
#else
		if (fakeSentBytes == 0) {
			theStats::AddSentBytes(sentBytes, 0, 0);
		} else {
			float ripFactor = ((float) sentBytes) / (float) fakeSentBytes;
			// if (sentBytes != fakeSentBytes) {
			//	std::clog << "Real " << sentBytes << " fake " << fakeSentBytes << " ripfactor " << ripFactor << std::endl;
			// }
			uint64 riparted_aduSentBytes      = (uint64) (ripFactor*(float)aduSentBytes);
			uint64 riparted_aduFiberSentBytes = (uint64) (ripFactor*(float)aduFiberSentBytes);
			theStats::AddSentBytes(sentBytes, // Mr Hyde Test fakeSentBytes,
		        	               riparted_aduSentBytes,
		                	       riparted_aduFiberSentBytes);
		}
#endif
// ADUNANZA END

	}
}

// ADUNANZA BEGIN
// Patch da forum.amule.org per upload
uint16 CUploadQueue::GetMaxSlots() const
{
	uint16 nMaxSlots = 0;
	float kBpsUpPerClient = (float)thePrefs::GetSlotAllocation();
	if (thePrefs::GetMaxUpload() == UNLIMITED) {
		float kBpsUp = theStats::GetUploadRate() / 1024.0f;
		nMaxSlots = (uint16)(kBpsUp / kBpsUpPerClient) + 2;
	} else {
		if (thePrefs::GetMaxUpload() >= 10) {
			nMaxSlots = (uint16)floor((float)thePrefs::GetMaxUpload() / kBpsUpPerClient + 0.5);
				// floor(x + 0.5) is a way of doing round(x) that works with gcc < 3 ...
			if (nMaxSlots < MIN_UP_CLIENTS_ALLOWED) {
				nMaxSlots=MIN_UP_CLIENTS_ALLOWED;
			}
		} else {
			nMaxSlots = MIN_UP_CLIENTS_ALLOWED;
		}
	}
	if (nMaxSlots > MAX_UP_CLIENTS_ALLOWED) {
		nMaxSlots = MAX_UP_CLIENTS_ALLOWED;
	}
	return nMaxSlots;
}
// ADUNANZA END


// ADUNANZA BEGIN
// backport: metodo rimosso
#if 0	
bool CUploadQueue::AcceptNewClient()
{
	// check if we can allow a new client to start downloading from us
	if (::GetTickCount() - m_nLastStartUpload < 1000 || m_uploadinglist.size() >= MAX_UP_CLIENTS_ALLOWED) {
		return false;
	}

	float kBpsUpPerClient = (float)thePrefs::GetSlotAllocation();
	float kBpsUp = theStats::GetUploadRate() / 1024.0f;
	if (thePrefs::GetMaxUpload() == UNLIMITED) {
		if (m_uploadinglist.size() < ((uint32)((kBpsUp)/kBpsUpPerClient)+2)) {
			return true;
		}
	} else {
		uint16 nMaxSlots = 0;
// ADUNANZA BEGIN
#if 0
		if (thePrefs::GetMaxUpload() >= 10) {
			nMaxSlots = (uint16)floor((float)thePrefs::GetMaxUpload() / kBpsUpPerClient + 0.5);
				// floor(x + 0.5) is a way of doing round(x) that works with gcc < 3 ...
			if (nMaxSlots < MIN_UP_CLIENTS_ALLOWED) {
				nMaxSlots=MIN_UP_CLIENTS_ALLOWED;
			}
		} else {
			nMaxSlots = MIN_UP_CLIENTS_ALLOWED;
		}
#else
		nMaxSlots = GetMaxSlots();
#endif
// ADUNANZA END
		if (m_uploadinglist.size() < nMaxSlots) {
			return true;
		}
	}
	return false;
}
#endif
// ADUNANZA END

// ADUNANZA BEGIN
/*
bool CUploadQueue::AcceptNewClient(bool bIsAduClient)
{

	uint32 typeClient = CAdunanzAUtilities::AduNextClient();
	// uint32 typeClient = theApp->pUploadSlotsMng->next();

	switch (typeClient) {
		case ADUNANZA_NONE:
			return false;
			break;
		case ADUNANZA_FASTWEB:
			if (!bIsAduClient) {
				return false;
			}
			break;
		case ADUNANZA_EXTERN:
			if (!bIsAduClient) {
				return false;
			}
			break;

		default:
			break;
	}

	return AcceptNewClient();
}
*/
// ADUNANZA END


CUploadQueue::~CUploadQueue()
{
	wxASSERT(m_waitinglist.empty());
	wxASSERT(m_uploadinglist.empty());
}


bool CUploadQueue::IsOnUploadQueue(const CUpDownClient* client) const
{
	return std::find(m_waitinglist.begin(), m_waitinglist.end(), client)
		!= m_waitinglist.end();
}


bool CUploadQueue::IsDownloading(CUpDownClient* client) const
{
	return std::find(m_uploadinglist.begin(), m_uploadinglist.end(), client)
		!= m_uploadinglist.end();
}	


CUpDownClient* CUploadQueue::GetWaitingClientByIP_UDP(uint32 dwIP, uint16 nUDPPort, bool bIgnorePortOnUniqueIP, bool* pbMultipleIPs)
{
	CUpDownClient* pMatchingIPClient = NULL;
	
	int cMatches = 0;
	
	CClientPtrList::iterator it = m_waitinglist.begin();
	for (; it != m_waitinglist.end(); ++it) {
		CUpDownClient* cur_client = *it;
		
		if ((dwIP == cur_client->GetIP()) && (nUDPPort == cur_client->GetUDPPort())) {
			return cur_client;
		} else if ((dwIP == cur_client->GetIP()) && bIgnorePortOnUniqueIP) {
			pMatchingIPClient = cur_client;
			cMatches++;
		}
	}

	if (pbMultipleIPs) {
		*pbMultipleIPs = cMatches > 1;
	}
	
	if (pMatchingIPClient && cMatches == 1) {
		return pMatchingIPClient;	
	} else {
		return NULL;
	}
}


void CUploadQueue::AddClientToQueue(CUpDownClient* client)
{
// ADUNANZA BEGIN
// Mod Adu
// Emanem
// Questa mod serve a supportare piu' di 50 clients se
// connessi con un low-id
#if 0
	if (theApp->serverconnect->IsConnected() && theApp->serverconnect->IsLowID() && !theApp->serverconnect->IsLocalServer(client->GetServerIP(),client->GetServerPort()) && client->GetDownloadState() == DS_NONE && !client->IsFriend() && theStats::GetWaitingUserCount() > 50) {
#else
	if (theApp->serverconnect->IsConnected() &&
	    (theApp->serverconnect->IsLowID()) &&
	    !theApp->serverconnect->IsLocalServer(client->GetServerIP(),client->GetServerPort()) &&
	    (client->GetDownloadState() == DS_NONE) &&
	    !client->IsFriend() && 
	    (theStats::GetWaitingUserCount() > theApp->glob_prefs->GetQueueSize()) ) {
#endif
// ADUNANZA END
		// Well, all that issues finish in the same: don't allow to add to the queue
		return;
	}

	if ( client->IsBanned() ) {
		return;
	}

	client->AddAskedCount();
	client->SetLastUpRequest();

	// Find all clients with the same user-hash
	CClientList::SourceList found = theApp->clientlist->GetClientsByHash( client->GetUserHash() );

	CClientList::SourceList::iterator it = found.begin();
	while (it != found.end()) {
		CUpDownClient* cur_client = *it++;
		
		if ( IsOnUploadQueue( cur_client ) ) {
			if ( cur_client == client ) {
// ADUNANZA BEGIN
				// Stefano Picerno: aggiunto && (uploadinglist.GetCount() < AduGetMaxUploadSlots())
#if 0
				if ( client->m_bAddNextConnect && ( ( m_uploadinglist.size() < thePrefs::GetMaxUpload() ) || ( thePrefs::GetMaxUpload() == UNLIMITED ) ) ) {
					if (lastupslotHighID) {
						client->m_bAddNextConnect = false;
						RemoveFromWaitingQueue(client, true);
						AddUpNextClient(client);
						lastupslotHighID = false; // LowID alternate
						return;
					}
				}
#else
/*
				if ( client->m_bAddNextConnect && ( ( m_uploadinglist.size() < thePrefs::GetMaxUpload() ) || ( thePrefs::GetMaxUpload() == UNLIMITED ) ) && (m_uploadinglist.size() < CAdunanzAUtilities::AduGetMaxUploadSlots()) ) {
					if (lastupslotHighID) {
						client->m_bAddNextConnect = false;
						RemoveFromWaitingQueue(client, true);
						AddUpNextClient(client);
						lastupslotHighID = false; // LowID alternate
						return;
					}
				}
*/
				// This is where LowID clients get their upload slot assigned.
				// They can't be contacted if they reach top of the queue, so they are just marked for uploading.
				// When they reconnect next time AddClientToQueue() is called, and they get their slot
				// through the connection they initiated.
				// Since at that time no slot is free they get assigned an extra slot,
				// so then the number of slots exceeds the configured number by one.
				// To prevent a further increase no more LowID clients get a slot, until 
				// - a HighID client has got one (which happens only after two clients 
				//   have been kicked so a slot is free again)
				// - or there is a free slot, which means there is no HighID client on queue
				if (client->m_bAddNextConnect) {
					uint16 maxSlots = GetMaxSlots();
					if (lastupslotHighID) {
						maxSlots++;
					}
					if (m_uploadinglist.size() < maxSlots) {
						client->m_bAddNextConnect = false;
						RemoveFromWaitingQueue(client);
						AddUpNextClient(client);
						lastupslotHighID = false; // LowID alternate
						return;
					}
				}

#endif
// ADUNANZA END

				client->SendRankingInfo();
				Notify_QlistRefreshClient(client);
				return;

			} else {
				// Hash-clash, remove unidentified clients (possibly both)
				
				if ( !cur_client->IsIdentified() ) {
					// Cur_client isn't identifed, remove it
					theApp->clientlist->AddTrackClient( cur_client );

					RemoveFromWaitingQueue( cur_client );
					if ( !cur_client->GetSocket() ) {
						if (cur_client->Disconnected( wxT("AddClientToQueue - same userhash") ) ) {
							cur_client->Safe_Delete();
						}
					}
				}

				if ( !client->IsIdentified() ) {
					// New client isn't identified, remove it
					theApp->clientlist->AddTrackClient( client );

					if ( !client->GetSocket() ) {
						if ( client->Disconnected( wxT("AddClientToQueue - same userhash") ) ) {
							client->Safe_Delete();
						}
					}

					return;
				}
			}
		}
	}

	// Count the number of clients with the same IP-address
	found = theApp->clientlist->GetClientsByIP( client->GetIP() );

	int ipCount = 0;
	for ( it = found.begin(); it != found.end(); it++ ) {
		if ( ( *it == client ) || IsOnUploadQueue( *it ) ) {
			ipCount++;
		}
	}

	// We do not accept more than 3 clients from the same IP
	if ( ipCount > 3 ) {
		return;
	} else if ( theApp->clientlist->GetClientsFromIP(client->GetIP()) >= 3 ) {
		return;
	}

	// statistic values
	CKnownFile* reqfile = (CKnownFile*) client->GetUploadFile();
	if (reqfile) {
		reqfile->statistic.AddRequest();
	}

	// TODO find better ways to cap the list
	if (m_waitinglist.size() >= (thePrefs::GetQueueSize())) {
		return;
	}

	if (client->IsDownloading()) {
		// he's already downloading and wants probably only another file
		CPacket* packet = new CPacket(OP_ACCEPTUPLOADREQ, 0, OP_EDONKEYPROT);
		theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
		AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_ACCEPTUPLOADREQ to ") + client->GetFullIP() );
		client->SendPacket(packet,true);
		return;
	}

// ADUNANZA BEGIN
// backport da aMule SVN
#if 0	
	if (m_waitinglist.empty() && AcceptNewClient()) {
		AddUpNextClient(client);
		m_nLastStartUpload = ::GetTickCount();
#else
	uint32 tick = GetTickCount();
	client->ClearWaitStartTime();
	// if possible start upload right away
	if (/*(CAdunanzAUtilities::AduNextClient() != ADUNANZA_NONE) &&*/ m_waitinglist.empty() && tick - m_nLastStartUpload >= 1000 && m_uploadinglist.size() < GetMaxSlots()) {
		AddUpNextClient(client);
		m_nLastStartUpload = tick;
#endif
// ADUNANZA END
	} else {
		m_waitinglist.push_back(client);
		theStats::AddWaitingClient();
// ADUNANZA BEGIN
// spostato in alto
#if 0
		client->ClearWaitStartTime();
#endif
// ADUNANZA END
		client->ClearAskedCount();
// ADUNANZA BEGIN
		// mod Adu
		// Emanem
		// se un client e' Adu incremento il conteggio
		if (client->IsAdunanzA()) {  // ADUFLAGS
			m_AduClientsNum++;
		}
		// fine mod Adu
// ADUNANZA END
		client->SetUploadState(US_ONUPLOADQUEUE);
		client->SendRankingInfo();
		Notify_QlistAddClient(client);
		Notify_ShowQueueCount(m_waitinglist.size());
	}
}


bool CUploadQueue::RemoveFromUploadQueue(CUpDownClient* client, bool updatewindow)
{
	// Keep track of this client
	theApp->clientlist->AddTrackClient(client);
	
	CClientPtrList::iterator it = std::find(m_uploadinglist.begin(),
			m_uploadinglist.end(), client);
	
	if (it != m_uploadinglist.end()) {
		if (updatewindow) {
			Notify_UploadCtrlRemoveClient(client);
		}
		m_uploadinglist.erase(it);
// ADUNANZA BEGIN
		// Mod Adu
		// lupz
		CClientPtrList::iterator adu_it = std::find(aduuploadinglist.begin(), aduuploadinglist.end(), client);

		if (adu_it != aduuploadinglist.end()) {
			aduuploadinglist.erase(adu_it);
		}
		// Fine mod Adu
// ADUNANZA END
		theStats::RemoveUploadingClient();
		if( client->GetTransferredUp() ) {
			theStats::AddSuccessfulUpload();
			theStats::AddUploadTime(client->GetUpStartTimeDelay() / 1000);
		} else {
			theStats::AddFailedUpload();
		}
		client->SetUploadState(US_NONE);
		client->ClearUploadBlockRequests();
		return true;
	}

	return false;
}


bool CUploadQueue::CheckForTimeOver(CUpDownClient* client)
{
// ADUNANZA BEGIN
	if (!client) return true;

// Patch da amule.org per favorire le release - begin
//
	// Don't kick anybody if there's no need to
	if (!m_allowKicking) {
		return false;
	}
	// First, check if it is a VIP slot (friend or Release-Prio).
	if (client->GetFriendSlot()) {
		return false;	// never drop the friend
	}
	// Release-Prio and nobody on queue for it?
	const CKnownFile* pUploadFile = client->GetUploadFile();
	if (pUploadFile && pUploadFile->GetUpPriority() == PR_POWERSHARE) {
		// Keep it unless half of the UL slots are occupied with friends or Release uploads.
		uint16 vips = 0;
		for (CClientPtrList::iterator it = m_uploadinglist.begin(); it != m_uploadinglist.end(); ++it) {
			CUpDownClient* cur_client = *it;
			if (cur_client && (cur_client->GetFriendSlot() || cur_client->GetUploadFile()->GetUpPriority() == PR_POWERSHARE)) {
				vips++;
			}
		}
		// allow if VIP uploads occupy at most half of the possible upload slots
		if (vips <= GetMaxSlots() / 2) {
			return false;
		}
		// Otherwise normal rules apply.
	}
	// Ordinary slots
	bool kickHim = false;
// Patch da amule.org per favorire le release - end

	// Patch da forum.amule.org: evito di calcolare lo score TUTTE le volte nel ciclo
	bool bGotClientScore(false);
	uint32 clientScore = 0;

	if (client->IsAdunanzA()) {
		if (thePrefs::TransferFullChunks()) { // sempre verificato
			if( client->GetUpStartTimeDelay() > SESSIONMAXTIME ) { // Tengo gli AdunanzA al max per 1 ora
				// return true;
				kickHim = true;
			}

			/*
			 * Kaiser -- 08/04/2004 - 15.43
			 * Se è un ext lo "stacca" dopo SESSIONMAXTRANS (9,3mb) altrimenti cerca
			 * di staccarlo dopo ADU_SESSIONMAXTRANS (18,6mb) ovvero il doppio.
			 * In questo modo possiamo favorire lo scambio tra aduner senza dover 
			 * modificare i tempi di reask e rischiando quindi meno ban!
			 */
			else if (client->GetQueueSessionPayloadUp() > ADU_SESSIONMAXTRANS) {
				// Inoltre se la prossima slot deve essere AdunanzA e non ho altri Adu in coda
				// non lo butto giu' per poi riprenderlo in coda di upload.
				// Quindi se la prossima slot e' non adunanza lo butto giu'.
				// Condizioni sufficienti per buttare giu' un client sono:
				// - Avere altri Adu in coda di attesa
				// - Che il prossimo client non sia Adu
				if ((theApp->uploadqueue->GetAdunanzAUserCount() > 0) || (ADUNANZA_FASTWEB != CAdunanzAUtilities::AduGetTypeBand())) {
					if (thePrefs::GetVerbose()) {
						AddDebugLogLineM(false, logClient,  wxString::Format(_("%s: Upload session ended due to max transfered amount. %s"), client->GetUserName().c_str(), CastItoXBytes((uint64) ADU_SESSIONMAXTRANS).c_str() ) );
					}
					// return true; // test
					kickHim = true; // test
				}
			}
		} else {
			if (!m_waitinglist.empty()) {
				if (!bGotClientScore) {
					clientScore = client->GetScore(true,true);
					bGotClientScore = true;
				}
				// Dovrebbe essere impossibile xche' gli AdunanzA hanno SEMPRE i fullChunks
				CClientPtrList::iterator it = m_waitinglist.begin();
				for (; it != m_waitinglist.end(); ++it ) {
					if (*it && (clientScore < (*it)->GetScore(true,false))) {
						// return true; // test
						kickHim = true; // test
						break;
					}
				}
			} // waiting list not empty
		}

		// return false; // test
		if (kickHim) {
			m_allowKicking = false;		// kick max one client per cycle
		}
	
		return kickHim;
	}
// ADUNANZA END
	if (thePrefs::TransferFullChunks()) {
// ADUNANZA BEGIN
// patch da forum.amule.org
#if 0 		
		if( client->GetUpStartTimeDelay() > 3600000 ) { // Try to keep the clients from downloading forever.
			return true;
		}
		// For some reason, some clients can continue to download after a chunk size.
		// Are they redownloading the same chunk over and over????
		if( client->GetSessionUp() > 10485760 ) {
			return true;
		}
#else
		// "Transfer full chunks": drop client after 10 MB upload, or after an hour.
		// (so average UL speed should at least be 2.84 kB/s)
		// We don't track what he is downloading, but if it's all from one chunk he gets it.
		if (client->GetUpStartTimeDelay() > 3600000 	// time: 1h
			|| client->GetSessionUp() > 10485760) {		// data: 10MB
			kickHim = true;
		}
#endif		
// ADUNANZA END
	} else {
		CClientPtrList::iterator it = m_waitinglist.begin();
// ADUNANZA BEGIN
// Patch da forum.amule.org: evito di calcolare lo score TUTTE le volte nel ciclo
		if ((it != m_waitinglist.end()) && !bGotClientScore) {
			clientScore = client->GetScore(true,true);
			bGotClientScore = true;
		}
// ADUNANZA END	
		for (; it != m_waitinglist.end(); ++it ) {
// ADUNANZA BEGIN
// Patch da forum.amule.org: 
#if 0
			if (client->GetScore(true,true) < (*it)->GetScore(true,false)) {
				return true;
#else
			if (clientScore < (*it)->GetScore(true,false)) {
				kickHim = true;
				break;
#endif
// ADUNANZA END	
			}
		}
	}
// ADUNANZA BEGIN
// Patch da forum.amule.org: 
#if 0
	return false;
#else
	if (kickHim) {
		m_allowKicking = false;		// kick max one client per cycle
	}
	
	return kickHim;
#endif
// ADUNANZA END	
}


uint16 CUploadQueue::GetWaitingPosition(const CUpDownClient *client) const
{
	if ( !IsOnUploadQueue(client) ) {
		return 0;
	}

	uint16 rank = 1;
	const uint32 myscore = client->GetScore(false);
	CClientPtrList::const_iterator it = m_waitinglist.begin();
	for (; it != m_waitinglist.end(); ++it) {
		if ((*it)->GetScore(false) > myscore) {
			rank++;
		}
	}
	
	return rank;
}


/*
 * This function removes a file indicated by filehash from suspended_uploads_list.
 */
void CUploadQueue::ResumeUpload( const CMD4Hash& filehash )
{
	//Find the position of the filehash in the list and remove it.
	suspendlist::iterator it = std::find( suspended_uploads_list.begin(), 
			                              suspended_uploads_list.end(),
			                              filehash );
	if ( it != suspended_uploads_list.end() )
		suspended_uploads_list.erase( it );
	
	AddLogLineM( false, CFormat( _("Resuming uploads of file: %s" ) )
				% filehash.Encode() );
}

/*
 * This function adds a file indicated by filehash to suspended_uploads_list
 */
void CUploadQueue::SuspendUpload( const CMD4Hash& filehash )
{
	AddLogLineM( false, CFormat( _("Suspending upload of file: %s" ) )
				% filehash.Encode() );

	//Append the filehash to the list.
	suspended_uploads_list.push_back(filehash);
	wxString base16hash = filehash.Encode();

	CClientPtrList::iterator it = m_uploadinglist.begin();
	while (it != m_uploadinglist.end()) {
		CUpDownClient *potential = *it++;
		//check if the client is uploading the file we need to suspend
		if(potential->GetUploadFileID() == filehash) {
			//remove the unlucky client from the upload queue and add to the waiting queue
			RemoveFromUploadQueue(potential, true);

			m_waitinglist.push_back(potential);
			theStats::AddWaitingClient();
			potential->SetUploadState(US_ONUPLOADQUEUE);
			potential->SendRankingInfo();
			Notify_QlistRefreshClient(potential);
			Notify_ShowQueueCount(m_waitinglist.size());
		}
	}
}

bool CUploadQueue::RemoveFromWaitingQueue(CUpDownClient* client, bool updatewindow)
{
	CClientPtrList::iterator it = std::find(m_waitinglist.begin(),
			m_waitinglist.end(), client);
	
	if (it != m_waitinglist.end()) {
		RemoveFromWaitingQueue(it);
		if (updatewindow) {
			Notify_ShowQueueCount(m_waitinglist.size());
		}
		return true;
	} else {
		return false;
	}
}


void CUploadQueue::RemoveFromWaitingQueue(CClientPtrList::iterator pos)
{
	CUpDownClient* todelete = *pos;
// ADUNANZA BEGIN
	// mod Adu
	// Emanem
	// se il client era adu decremento il contatore
	if (todelete && todelete->IsAdunanzA() && m_AduClientsNum)  {// ADUFLAGS
		m_AduClientsNum--;
	}
	// fine mod Adu
// ADUNANZA END
	m_waitinglist.erase(pos);
	theStats::RemoveWaitingClient();
	if( todelete->IsBanned() ) {
		todelete->UnBan();
	}
	Notify_QlistRemoveClient(todelete);
	todelete->SetUploadState(US_NONE);
}

// File_checked_for_headers
