//
// This file is part of the aMule AdunanzA Project (mod of official aMule)
//
// Copyright (c) 2003-2008 aMule AdunanzA Team ( http://www.adunanza.net )
//
// Any parts of this program derived from the xMule, lMule, eMule or aMule 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
//

#if defined(ENABLE_ADUNSLU2)
#warning AdunanzA NSLU2 patches enabled
#endif

#include "amule.h"
#include "updownclient.h"
#include "Friend.h"
#include "ClientList.h"
#include "OtherFunctions.h"
#include "PartFile.h"
#include "ListenSocket.h"
#include "Friend.h"
#include "Packet.h"
#include "SafeFile.h"
#include "Preferences.h"
#include "Statistics.h"
#include "ClientCredits.h"
#include "IPFilter.h"
#include "UploadQueue.h"
#include "DownloadQueue.h"
#include "SearchList.h"
#include "SharedFileList.h"

// Mr Hyde per Clarensio
#include <protocol/ed2k/ClientSoftware.h> // per SO_AMULE/SO_EMULE
#include "NetworkFunctions.h"

#include <common/Format.h>

#if defined(__USE_KAD__)
#include "Kademlia/Kademlia/Kademlia.h"
#include "Kademlia/Net/KademliaUDPListener.h"
#endif

#if !defined(_CONSOLE) && !defined(AMULE_DAEMON)
#include "TransferWnd.h"
#include "ServerWnd.h"
#include "ChatWnd.h"
#endif

#include <cmath>

#include "AdunanzA.h"
#include "RemoteSettings.h"
#include "common/Constants.h"

// Mr Hyde Patch BEGIN
#include "Logger.h"
// Mr Hyde Patch END

// Inclusi per il parsing sul file per Updater
#include <cstdio>
#include <cstdlib>

// #define thePrefs (*(theApp->glob_prefs)) Mr Hyde: thePrefs e' la definizione di una classe statica (vedi src/Preferences.h)
// Funzione molto semplice che ritorna un 
// valore booleano vero se l'ip passatogli in input 
// appartiene alla rete FastWeb italiana.
/*
MAN Milano........................   1.0.0.0/8
MAN Milano hinterland Nord........   2.0.0.0/8
MAN Genova........................   5.0.0.0/8
MAN Veneto........................  11.0.0.0/8
MAN Milano hinterland Sud.........  14.0.0.0/8
MAN Ancona........................  21.0.0.0/8
MAN Grosseto......................  22.0.0.0/8
MAN Roma..........................  23.0.0.0/8
MAN Toscana.......................  27.0.0.0/8
MAN Piemonte......................  28.0.0.0/8
MAN Triveneto.....................  29.0.0.0/8
MAN Bari..........................  31.0.0.0/8
MAN Sicilia.......................  36.0.0.0/8
MAN Bologna.......................  37.0.0.0/8
MAN Napoli........................  39.0.0.0/8
MAN Torino........................  41.0.0.0/8
MAN Reggio Emilia.................  42.0.0.0/8
MAN Cagliari......................  51.0.0.0/8

Routers & OAM.....................  10.0.0.0/8
Organization, Administration & Management (ad. es.: Server interni ntp, voip, billing etc. etc.)
*/

bool CAdunanzAUtilities::AduIsFastWebIP(const wxString& ipv4string)
{
	uint32 ipv4addr;
	bool bTmp = StringIPtoUint32(ipv4string, ipv4addr);

	return (bTmp && CAdunanzAUtilities::AduIsFastWebIP(ipv4addr));
}

void CUpDownClient::SetClientFWCity()
{
	switch (m_dwUserIP & 0x000000ff) {
		case MAN_ROMA:
			m_FWCity = wxT("RM");
			break;
		case MAN_MILANO:
			m_FWCity = wxT("MI");
			break;
		case MAN_MILANO_H_NORD:
			m_FWCity = wxT("MI H. Nord");
			break;
		case MAN_MILANO_H_SUD:
			m_FWCity = wxT("MI H. Sud");
			break;
		case MAN_GENOVA:
			m_FWCity = wxT("GE");
			break;
		case MAN_TRIVENETO:
			m_FWCity = wxT("Triveneto");
			break;
		case MAN_BARI:
			m_FWCity = wxT("BA");
			break;
		case MAN_BOLOGNA:
			m_FWCity = wxT("BO");
			break;
		case MAN_NAPOLI:
			m_FWCity = wxT("NA");
			break;
		case MAN_TORINO:
			m_FWCity = wxT("TO");
			break;
		case MAN_REGGIO_EMILIA:
			m_FWCity = wxT("RE");
			break;
		case MAN_TOSCANA:
			m_FWCity = wxT("Toscana");
			break;
		case MAN_PIEMONTE:
			m_FWCity = wxT("Piemonte");
			break;
		case MAN_VENETO:
			m_FWCity = wxT("Veneto");
			break;
		case MAN_GROSSETO:
			m_FWCity = wxT("GR");
			break;
		case MAN_SICILIA:
			m_FWCity = wxT("Sicilia");
			break;
		case MAN_ANCONA:
			m_FWCity = wxT("AN");
			break;
		case MAN_CAGLIARI:
			m_FWCity = wxT("CA");
			break;
		case FASTWEB_ROUTERS:
			m_FWCity = wxT("OAM");
			break;
		default:
			m_FWCity = wxT("");
			break;
	}
}



bool CAdunanzAUtilities::AduIsFiber (uint32 ip) {
  bool	is128 = (((ip >> 15) & 1) == (uint32)0);

	return (is128 && CAdunanzAUtilities::AduIsFastWebLANIP(ip));
}

 

bool     CAdunanzAUtilities::AduIsValidKaduAddress( uint32 host )
{
	host = wxUINT32_SWAP_ALWAYS( host );
	//AddDebugLogLineM(false, logClientKadUDP, CFormat(wxT("Checking %s for kADU validity")) % Uint32toStringIP(host));

	// TODO: Add Banlist support here

	return CAdunanzAUtilities::AduIsFastWebIP( host );
}


#if !defined(CLIENT_GUI)
float CAdunanzAUtilities::calcolaStima(float   input_avail,
                   uint32& firstPublish,
                   uint32  publishInterval,
                   uint32  pubkRTK,
                   bool    sameIP,
                   uint32  now) {

	float avail = input_avail;

	// int32 kRTK = theApp->rm->kadRepublishTimeK;
	int32 kRTK = theApp->get_kadRepublishTimeK();

	// Using this increment style, we can guess also if clients use different publish rates.
	float inc = (float) pubkRTK / kRTK;
	
	// Only old clients, with republishtimek == 24hrs don't publish their republishtimek.
	if (inc == (float) 0.0) {
  		inc = HR2S(24) / kRTK;	
	}

	if (publishInterval > kRTK) {
		avail        = (float) 0.0;
		firstPublish = now;
	}

	while ( ((now-firstPublish) > kRTK) && 
	        (avail > (float) 2.0) ) {
		avail--;
		firstPublish += ((uint32)(kRTK/avail));
	}

	// Some client seem to send duplicate publish to already contacted clients.
	// In 90% of the times we avoid this here.
	if (!sameIP) {
		avail += inc;
	}

	return avail;
}

float CAdunanzAUtilities::normalizzaStima( float avail, uint32 from, uint32 to ) {
	// Parte necessaria per il debug
	// calcolo il coefficiente di normalizzazione
	// int kRTK = theApp->rm->kadRepublishTimeK;
	int kRTK = theApp->get_kadRepublishTimeK();

 	float norm_factor = (float) 1.0;

	// if (avail > theApp->rm->kadFreshGuess_NoNorm) {
	if (avail > theApp->get_kadFreshGuess_NoNorm()) {
// Mr Hyde Patch BEGIN	
		if (kRTK == 0) {
			AddDebugLogLineM(false, logKadSearch, wxString::Format(wxT("DENOMINATORE kRTK ZERO")));
			return 0.0; 
		}
// Mr Hyde Patch END	
		norm_factor = (to - from)/kRTK;
// Mr Hyde Patch BEGIN	
		if (avail == 0.0) {
			AddDebugLogLineM(false, logKadSearch, wxString::Format(wxT("DENOMINATORE avail ZERO")));
			return 0.0; 
		}
		if ( norm_factor > 1-1/avail ) {
			norm_factor = (float) 1.0;
		}
// Mr Hyde Patch END	

		// norm_factor = ( norm_factor < theApp->rm->kadFreshGuess_Tol ? theApp->rm->kadFreshGuess_Tol : norm_factor );
		norm_factor = ( norm_factor < theApp->get_kadFreshGuess_Tol() ? theApp->get_kadFreshGuess_Tol() : norm_factor );

		// We have a good number of publishes, but not high.
		// Low normalization is still suggested.
		// if ( avail < theApp->rm->kadFreshGuess_LowNorm && norm_factor > avail / theApp->rm->kadFreshGuess_LowNorm ) {
		if ( (avail < theApp->get_kadFreshGuess_LowNorm()) && (norm_factor > (avail / theApp->get_kadFreshGuess_LowNorm())) ) {
			// norm_factor /= powf( (avail/theApp->rm->kadFreshGuess_LowNorm), 0.5f );
			norm_factor /= powf( (avail/theApp->get_kadFreshGuess_LowNorm()), 0.5f );
		}
	}

	if ( avail > 2.0f ) {
		// avail /= powf( norm_factor, theApp->rm->kadFreshGuess_Weight );
		avail /= powf( norm_factor, theApp->get_kadFreshGuess_Weight() );
	}

  return avail;
}
#endif

void CAdunanzAUtilities::CalcolaRatio(bool bUpdatepage){

	//bool bADUGetRipBanda = thePrefs::ADU_GetRipBanda();
	bool bIsCustomRipBWEnabled = thePrefs::ADU_GetCustomRipBanda();

	uint32 valueRipBanda = thePrefs::ADU_GetValueRipBanda();

	// se non ho il custom allora uso il valore recuperato dai RemoteSettings
	if (!bIsCustomRipBWEnabled && theApp && theApp->rm){ 
		thePrefs::ADU_SetValueRipBanda(theApp->rm->get_AduValRipBanda_Std());
		valueRipBanda = thePrefs::ADU_GetValueRipBanda(); // update localValueRipBanda
	}
// Pegasus, fix 20091105 CalcolaRatio 
// Meglio controllare che GetMaxUpload sia superiore a MIN_BW_TROTTLER per evitare problemi
// di overflow
	uint32 valueMaxUpload            = thePrefs::GetMaxUpload();
	uint32 valueMaxDownload          = thePrefs::GetMaxDownload();
	uint32 valueMaxGraphDownloadRate = (uint32) theStats::GetDownloadRate(); // per aMule si usa theStats::GetDownloadRate()

	if ((valueRipBanda > valueMaxUpload) && (valueMaxUpload > ADUNANZA_MIN_BW_TROLLER)) {
		thePrefs::ADU_SetValueRipBanda((valueMaxUpload - ADUNANZA_MIN_BW_TROLLER)) ;
		// update valueRipBanda
		valueRipBanda = thePrefs::ADU_GetValueRipBanda();
	}

// Pegasus, fix 20091105 CalcolaRatio 
// Mr Hyde (lo scrivente) ha ceffato di brutto nello scrivere la sequenza di if.
// Pegasus ha rimediato!
//
// OCCHIO: ricordo che moltiplicare per 1024 è ome eseguire uno shift di 10 bit (da cui i "<< 10" seguenti)
	uint32 kbps_extMaxDown; 
	if (valueRipBanda < 4) {
		// kbp_extMaxDowns = 1024*3*valueRipBanda;
		kbps_extMaxDown = ((valueRipBanda << 10) * 3);
		thePrefs::ADU_SetExtMaxDown(kbps_extMaxDown);
	} else if (valueRipBanda < 10) {
		// kbp_extMaxDowns = 1024*4*valueRipBanda;
		kbps_extMaxDown = ((valueRipBanda << 10) * 4);
		thePrefs::ADU_SetExtMaxDown(kbps_extMaxDown);
	} else if (valueRipBanda >= 10) {
		// kbps = 1024*UNLIMITED;
		kbps_extMaxDown = (UNLIMITED << 10);
		thePrefs::ADU_SetExtMaxDown(kbps_extMaxDown);
	}
	// notare che per (4 <= valueRipBanda < 10) _NON_ cambio extMaxDown

	uint32 getupspeed = 0;
	if (valueMaxUpload != UNLIMITED) {
		//ho un limite di banda	
		getupspeed = valueMaxUpload;
	} else {
		// getupspeed = thePrefs.GetMaxGraphUploadRate(true);
		getupspeed = (uint32) theStats::GetUploadRate(); // per aMule si usa theStats::GetUploadRate
	}

	
        // entrambi i seg. sono unsigned, quindi la loro differenza e' comunque un unsigned. Riscrivo la condizione in modo sicuro	
	// if( (getupspeed - valueRipBanda)< 20)
	//
	// getupspeed < 20+valueRipBanda
	//
	// oppure
	//
	// (getupspeed >= 20) && (valueRipBanda > (getupspeed - 20))
	uint32 maxByteDown = 0;
	if (getupspeed < (valueRipBanda+20)) {
		if (valueMaxDownload != UNLIMITED) {
			// thePrefs::ADU_SetKaduMaxDown(((thePrefs::GetMaxDownload())*1024)/2);
			// thePrefs::ADU_SetKaduMaxDown(valueMaxDownload << 9); // (maxDownload*512)
			maxByteDown = (valueMaxDownload << 9); // (maxDownload*512)

		}
		else {
			// thePrefs.ADU_SetKaduMaxDown(((thePrefs::GetMaxGraphDownloadRate())*1024)/2);
			// thePrefs::ADU_SetKaduMaxDown(valueMaxGraphDownloadRate << 9); // (valueMaxGraphDownloadRate*512)
			maxByteDown = (valueMaxGraphDownloadRate << 9); // (valueMaxGraphDownloadRate*512)
		}
		
		// if (thePrefs::ADU_GetKaduMaxDown() > ADU_MAX_RATIO_KADU_DOWN) {
		// 	thePrefs::ADU_SetKaduMaxDown(ADU_MAX_RATIO_KADU_DOWN);
		// }

		maxByteDown = std::min<uint32>(maxByteDown, ADU_MAX_RATIO_KADU_DOWN);

	} else {
		// thePrefs::ADU_SetKaduMaxDown(UNLIMITED << 10); // UNLIMITED * 1024);
		maxByteDown = (UNLIMITED << 10); // UNLIMITED * 1024);
	}

	// Mr Hyde correzione:
	// il limite per il download dei KADU NON puo' mai essere superiore al limite di banda in Download imposto dall'utente
	uint32 userDownloadLimit_bytes = 1024*thePrefs::GetMaxDownload();

	if (thePrefs::GetMaxDownload() != UNLIMITED) {
		if (maxByteDown == UNLIMITED) { // UNLIMITED in realtà e' 0
			maxByteDown = userDownloadLimit_bytes ;
		} else {
			maxByteDown = std::min<uint32>(maxByteDown, userDownloadLimit_bytes);
		}
	}
	// non metto l'else xche' in caso di download senza limite dell'utente mi va bene qualunque valore di maxByteDown
	thePrefs::ADU_SetKaduMaxDown(maxByteDown);

	// se ho impostazioni custom e ho bUpdatepage ricarico preferenze di AdunanzA
	if (bIsCustomRipBWEnabled /*(!bADUGetRipBanda)*/ && bUpdatepage) {
	 	theApp->glob_prefs->LoadAdunanzAPreferences();
	}
/*
	std::cout << "(DBG) END rip " << valueRipBanda
		  << " maxup " << valueMaxUpload
		  << " maxdown " << valueMaxDownload
	          << " ADU KADUmaxdown " << thePrefs::ADU_GetKaduMaxDown()
	          << " ADU EXTmaxdown "   << thePrefs::ADU_GetExtMaxDown()
		  << std::endl;
*/	
}




// Decido come selezionare un client in base alla
// banda realmente data in upload
//
// 26/05/2009 Mr Hyde BEGIN
// Riportate modifiche di Tigerjact da eMule AdunanzA
uint32 CAdunanzAUtilities::AduGetTypeBand()
{
	if (!theApp || !theApp->m_statistics) return ADUNANZA_EXTERN;

	uint64 sessionSentBytes    = theApp->m_statistics->GetSessionSentBytes();
	// uint64 sessionRecvBytes    = theApp->m_statistics->GetSessionReceivedBytes();

	uint64 aduSessionSentBytes = theApp->m_statistics->GetAduSessionSentBytes();
	// uint64 aduSessionRecvBytes = theApp->m_statistics->GetAduSessionReceivedBytes();

	uint64 sentToExternal      = sessionSentBytes - aduSessionSentBytes;
	// uint64 recvFromExternal    = sessionRecvBytes - aduSessionRecvBytes;


	// Calcolo il rate dei KB trasferiti agli esterni a partire da quando ho lanciato AdunanzA (KB/secondo)
	// Per far questo non faccio altro che dividere il numero dei byte trasmessi agli esterni per 1024: in questo modo ottengo i KB spediti a esterni,
	// poi il risultato lo divido ancora per l'Uptime (da quanti secondi sta girando AdunanzA)
	//
	// uint64 uploadTime_sec = CStatistics::GetTotalUploadTimeSeconds();
	uint64 uploadTime_sec = theApp->m_statistics->GetUptimeSeconds();
	if (uploadTime_sec == 0) return ADUNANZA_EXTERN; // giusto per evitare div by 0)

	// sentToExternal_kb_per_sec = sentToExternal / (1024 * sessionTime_sec);
	uint64 sentToExternal_kb_per_sec = sentToExternal / (1024 * uploadTime_sec);

	if (sentToExternal_kb_per_sec <  thePrefs::ADU_GetValueRipBanda()) {
		// sono al di sotto della soglia prevista
		// Commento di Tigerjact:
		// 	se la coda degli ext e' vuota passo comunque un adu
		// 	in questo modo se ho un client adu in up, non ho altri (esterni o adu) in coda
		// 	non stacco il client adu per poi riprenderlo
		if (CAdunanzAUtilities::ExtQ() <= 0) {
			return ADUNANZA_FASTWEB;
		}
		return ADUNANZA_EXTERN;
	} else {
		return theApp->pUploadSlotsMng->next();
		// return ADUNANZA_FASTWEB;
	}

	/*
	if ( sentToExternal < recvFromExternal ) {
       		return ADUNANZA_EXTERN;
	} else {
		return ADUNANZA_FASTWEB;
	}
	*/

	}


#ifndef CLIENT_GUI
int CAdunanzAUtilities::AduQ() {
	return ((theApp && theApp->uploadqueue) ? theApp->uploadqueue->GetAdunanzAUserCount() : 0);
}

int CAdunanzAUtilities::ExtQ() {
	return ((theApp && theApp->uploadqueue) ? (theApp->uploadqueue->GetWaitingList().size() - theApp->uploadqueue->GetAdunanzAUserCount()) : 0);
}


bool CAdunanzAUtilities::AduDebt() {
	uint64 sentToExternal   = theApp->m_statistics->GetSessionSentBytes() - theApp->m_statistics->GetAduSessionSentBytes();
	uint64 recvFromExternal = theApp->m_statistics->GetSessionReceivedBytes() - theApp->m_statistics->GetAduSessionReceivedBytes();

	if ( sentToExternal < recvFromExternal )
		return true;
	else 
		return false;
}

// Mr Hyde: per ricavare l'upload rate corrente
// (codice copiato da UploadBandwidthThrottler.cpp)

uint32 CAdunanzAUtilities::getAllowedUploadDataRate()
{
	uint32 allowedDataRate = 0;

	if (thePrefs::GetMaxUpload() == UNLIMITED) {
		// Try to increase the upload rate
		if (theApp->uploadqueue) {
			allowedDataRate = ((uint32)theStats::GetUploadRate()) + (5 * 1024);
		} else {
			// App not created yet or already destroyed.
			allowedDataRate = (uint32)(-1);
		}
	} else {
		allowedDataRate = thePrefs::GetMaxUpload() * 1024;
	}

	return allowedDataRate;
}

int CAdunanzAUtilities::OLDSetSlots() {
	float maxUp           = (float) thePrefs::GetMaxUpload();
	float kBpsUpPerClient = (float) thePrefs::GetSlotAllocation();
	float kBpsUp          = ((float) theStats::GetUploadRate()) / 1024.0f;
	int   slots;


	if (thePrefs::GetMaxUpload() == UNLIMITED)
		slots = (int)( kBpsUp / kBpsUpPerClient + 2 );
	else
		slots = (int)floor( maxUp / kBpsUpPerClient + 0.5f );

	return std::max(MIN_UP_CLIENTS_ALLOWED,slots);
}


int CAdunanzAUtilities::SetSlots() {
#if 0
	float maxUp           = (float) (thePrefs::GetMaxUpload()); 
	float kBpsUpPerClient = (float) thePrefs::GetSlotAllocation();
	float kBpsUp          = (float) (theStats::GetUploadRate() / 1024.0); // lo shift di 10 bit equivale a dividere per 1024 (piu' rapido)  

	int   slots;
	slots = (int)floor( maxUp / kBpsUpPerClient + 0.5f );

	return std::max(MIN_UP_CLIENTS_ALLOWED,slots);
#endif
	uint16   nMaxSlots;
	float kBpsUpPerClient = (float) thePrefs::GetSlotAllocation();

		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;
		}

		return ((int) nMaxSlots);

}

int CAdunanzAUtilities::TotSlots() {
#if 0	
	if (CAdunanzAUtilities::AduDebt())
		return (int)(CAdunanzAUtilities::SetSlots() * 1.5f);
	else
		return CAdunanzAUtilities::SetSlots();
#endif

/*	
	// return CPreferences::ADU_GetMaxUpSlots();
	
	// calcolo l'attuale upload rate massimo
	uint32 maxUpDataRate = CAdunanzAUtilities::getAllowedUploadDataRate();
	if (maxUpDataRate == (uint32)-1) {
		return 0;
	}

	maxUpDataRate >>= 10;

	// calcolo il numero degli slot attualmente a disposizione
	uint32 slotAllocation = thePrefs::GetSlotAllocation();

	return ((int)((slotAllocation != 0) ? (maxUpDataRate / slotAllocation) : 0)); 
*/
	if (theApp && (theApp->uploadqueue)) {
		return ((int) theApp->uploadqueue->GetMaxSlots());
	} else {
		return 0;
	}
}

#if 0
int CAdunanzAUtilities::ExtSlots() {
	if (CAdunanzAUtilities::AduDebt())
		return (int)(CAdunanzAUtilities::TotSlots() * 0.5f);
	else
		return CAdunanzAUtilities::TotSlots();
}
#endif


uint32 CAdunanzAUtilities::AduNextClient(int fix) {
	
	if (!theApp || !(theApp->uploadqueue)) return ADUNANZA_NONE;

	// Mr Hyde: il controllo si basa sul numero di slot, NON sulla banda realmente occupata
	//          dall'upload.
	//          Introduco ANCHE un controllo sulla banda attualmente occupata dall'upload
	uint32 currentUpload_KB_sec    = (uint32) (theStats::GetUploadRate() / 1024.0); // non posso usare lo shift perche' usa double
	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
		if (currentUpload_KB_sec >= maxAllowedUpload_KB_sec) {
			return ADUNANZA_NONE;
		}
	}

	int totslots = CAdunanzAUtilities::TotSlots();
	int totcount = ((int) theApp->uploadqueue->GetUploadCount()) - fix;


	if (totcount < totslots) {
		if ((CAdunanzAUtilities::AduQ() <= 0) &&  (CAdunanzAUtilities::ExtQ() <= 0)) {
			// return ADUNANZA_ANY;
			return ADUNANZA_EXTERN; // Mr Hyde: sono buono, inizio con gli esterni
		}

		if (CAdunanzAUtilities::AduQ() <= 0) {
			return ADUNANZA_EXTERN; // Tigerjact
		}

		// Tigerjact
		// Verifico: se la coda degli esterni e' vuota e quella degli adu piena allora prendo un client adu
		if (CAdunanzAUtilities::ExtQ() <= 0) {
			return ADUNANZA_FASTWEB;
		}

		uint64 sessionSentBytes    = theApp->m_statistics->GetSessionSentBytes();
		uint64 aduSessionSentBytes = theApp->m_statistics->GetAduSessionSentBytes();
		uint64 sentToExternal      = sessionSentBytes - aduSessionSentBytes;

		// uint64 sessionTime_sec = theApp->m_statistics->GetUptimeSeconds();

		// Calcolo il rate dei KB trasferiti agli esterni a partire da quando ho lanciato AdunanzA (KB/secondo)
		// Per far questo non faccio altro che dividere il numero dei byte trasmessi agli esterni per 1024: in questo modo ottengo i KB spediti a esterni,
		// poi il risultato lo divido ancora per l'Uptime (da quanti secondi sta girando AdunanzA)
		//
		// Problemone: in realta' potrei non aver ancora fatto upload (quindi richiare un div by 0)
		// uploadTime_sec = CStatistics::GetTotalUploadTimeSeconds();
		// uint64 uploadTime_sec = theApp->m_statistics->GetUptimeSeconds();
		uint64 uploadTime_sec = theApp->m_statistics->GetUptimeSeconds();
		if (uploadTime_sec == 0) {
			return ADUNANZA_NONE;
		}


		// sentToExternal_kb_per_sec = sentToExternal / (1024 * sessionTime_sec);
		uint64 sentToExternal_kb_per_sec = sentToExternal / (1024 * uploadTime_sec);

		if (sentToExternal_kb_per_sec < thePrefs::ADU_GetValueRipBanda()) {
			return ADUNANZA_EXTERN;
		} else {
			// return ADUNANZA_FASTWEB;
			return theApp->pUploadSlotsMng->next();
		}

	} else {
		return ADUNANZA_NONE;
	}





		
#if 0
		uint64 recvFromExternal    = sessionRecvBytes - aduSessionRecvBytes;

		int aducount = theApp->uploadqueue->GetAduUploadCount();
		int extcount = totcount - aducount;

		


		if (CAdunanzAUtilities::AduDebt()) {
			int extslots = CAdunanzAUtilities::ExtSlots() - fix;
			int aduslots = totslots - extslots;
/*
			if ( (extslots > extcount) && (aduslots > aducount) )
				return ADUNANZA_ANY;
			else if (extslots > extcount)
				return ADUNANZA_EXTERN;
			else if (aduslots > aducount)
				return ADUNANZA_FASTWEB;
			else
				return ADUNANZA_NONE; // non dovrebbe poter succedere
*/
			if (extslots > extcount) {
				return ((aduslots > aducount) ? ADUNANZA_ANY : ADUNANZA_EXTERN);
			} else {
				return ((aduslots > aducount) ? ADUNANZA_FASTWEB : ADUNANZA_NONE);
			}

 

		} else {
			return ADUNANZA_FASTWEB;
		}
	} else
		return ADUNANZA_NONE;
#endif
}

uint32 CAdunanzAUtilities::AduGetMaxUploadSlots()
{
	return CAdunanzAUtilities::TotSlots();
}

bool CAdunanzAUtilities::AduMaxTrans() { // Probabilmente non più usata
	if (!theApp || !(theApp->uploadqueue)) return false;

	uint32 now = (uint32)time(NULL);

	if ( CAdunanzAUtilities::AduQ() && 
	     (CAdunanzAUtilities::AduNextClient(1) == ADUNANZA_FASTWEB) &&
	     (now > (theApp->uploadqueue->lastmax + 45)) ) {
		theApp->uploadqueue->lastmax = now;
		return true;
	} else {
		return false;
	}
}
#endif


// Mr Hyde
uint32 CAduUploadSlotsManager::getAllowedUploadDataRate()
{
	uint32 allowedDataRate = 0;

	if (thePrefs::GetMaxUpload() == UNLIMITED) {
		// Try to increase the upload rate
		if (theApp->uploadqueue) {
			allowedDataRate = (uint32)theStats::GetUploadRate() + 5 * 1024;
		} else {
			// App not created yet or already destroyed.
			allowedDataRate = (uint32)(-1);
		}
	} else {
		allowedDataRate = thePrefs::GetMaxUpload() * 1024;
	}

	return allowedDataRate;
}

uint32 CAduUploadSlotsManager::next() const
{
	if (!m_adunanzaSlots && !m_externSlots) return ADUNANZA_NONE;

	// controllo se ho debiti pendenti
	if (m_adunanzaDebt) {
		// adunanza e' in debito nei confronti degli esterni
		// quindi se ho disponibili slot per esterni scelgo subito uno slot esterno
		if (m_externSlots) return ADUNANZA_EXTERN;
	}

	if (m_externDebt) {
		// gli esterni sono in debito con gli AdunanzA
		// quindi se ho disponibili slot per AdunanzA scelgo subito uno slot AdunanzA
		if (m_adunanzaSlots) return ADUNANZA_FASTWEB;
	}


	if (m_adunanzaSlots && m_externSlots) return ADUNANZA_ANY;


	if (m_adunanzaSlots) return ADUNANZA_FASTWEB;
	if (m_externSlots)   return ADUNANZA_EXTERN;

	return ADUNANZA_NONE; // giusto per evitare warning
}


void CAduUploadSlotsManager::update(CClientPtrList& uploadingList)
{
	// OK, al momento quanti sono gli slot attivi in totale?
	// Tanti quanti i client in upload
	uint32 currentUsedSlots = uploadingList.size();

	// e di questi quanti sono gli slot attivi AdunanzA?
	uint32 currentAdunanzASlots = 0;
	for (CClientPtrList::iterator it = uploadingList.begin();
			it != uploadingList.end();
			++it) {
		if ((*it)->IsAdunanzA()) currentAdunanzASlots++;
	}

	uint32 currentExternSlots = currentUsedSlots - currentAdunanzASlots;

	// if (m_extern || m_fastweb) return; // aggiorno solo se ho esaurito gli slot!

/*
	// calcolo l'attuale upload rate massimo
	uint32 maxUpDataRate = (getAllowedUploadDataRate() >> 10);

	// calcolo il numero degli slot attualmente a disposizione
	// uint32 maxUpSlots = maxUpDataRate / thePrefs::GetSlotAllocation();
*/
	uint32 maxUpSlots = CAdunanzAUtilities::TotSlots();

	// sia ad AdunanzA che a Esterni al max spetta la meta' degli slot
	// a disposizione.
	// Uso uno shift di 1 bit (equivale a dividere per 2)
	uint32 maxCategorySlotsLimit = (maxUpSlots >> 1);


	// ora calcolo, per ciascuna categoria, quanti sono gli slot ancora liberi.
	// Per farlo sottraggo gli slot occupati agli slot possibili (maxCategorySlotLimit)
	//
	if (maxCategorySlotsLimit > currentExternSlots) {
		m_externSlots = maxCategorySlotsLimit - currentExternSlots;
		m_externDebt = 0;
	} else {
		m_externSlots = 0;
		m_externDebt = currentExternSlots - maxCategorySlotsLimit;
	}


	if (maxCategorySlotsLimit > currentAdunanzASlots) {
		m_adunanzaSlots = maxCategorySlotsLimit - currentAdunanzASlots;
		m_adunanzaDebt = 0;
	} else {
		m_adunanzaSlots = 0;
		m_adunanzaDebt = currentAdunanzASlots - maxCategorySlotsLimit;
	}

	// se adunanza e' in debito nei confronti degli extern scarico il debito
	// dagli extern (perche' adunanza STA USANDO slot che sarebbero degli extern)
	if (m_externSlots > m_adunanzaDebt) {
		m_externSlots-=m_adunanzaDebt;
	}

	if (m_adunanzaSlots > m_externDebt) {
		m_adunanzaSlots-=m_externDebt;
	}

}


