//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//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., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "StdAfx.h"
#include "uploadqueue.h"
#include "packets.h"
#include "emule.h"
#include "knownfile.h"
#include "listensocket.h"
#include "ini2.h"
#include "math.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


static uint32 counter, sec,statsave;
static uint32 igraph, istats;
VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg,UINT_PTR idEvent,DWORD dwTime);

//TODO rewrite the whole networkcode, use overlapped sockets

CUploadQueue::CUploadQueue(CPreferences* in_prefs){
	app_prefs = in_prefs;
	h_timer = SetTimer(0,141,100,TimerProc);
	if (!h_timer)
		theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_TIMERCREATEFAILED));
	estadatarate = 2000;
	datarate = 0;
	//dataratems = 0;
	datarateave = 0;
	bannedcount = 0;
	counter=0;
	successfullupcount = 0;
	failedupcount = 0;
	totaluploadtime = 0;
	m_nUpDataRateMSOverhead = 0;
	m_nUpDatarateOverhead = 0;
	m_nUpDataOverheadSourceExchange = 0;
	m_nUpDataOverheadFileRequest = 0;
	m_nUpDataOverheadOther = 0;
	m_nUpDataOverheadServer = 0;
	m_nUpDataOverheadSourceExchangePackets = 0;
	m_nUpDataOverheadFileRequestPackets = 0;
	m_nUpDataOverheadOtherPackets = 0;
	m_nUpDataOverheadServerPackets = 0;
	m_nLastStartUpload = 0;
	m_nSumForAvgDataRate = 0;
	statsave=0;

    m_MaxActiveClients = 0;
    m_MaxActiveClientsShortTime = 0;

    leftoverBandwidth = 0;

    m_guessedMaxBandwidth = 300;
    m_lastGaveDataTick = ::GetTickCount();
    m_lastCalculatedDataRateTick = 0;
    m_dwLastCheckedForHighPrioClient = 0;
    m_dwLastExtremeUploadAmountTick = 0;

    m_dwLastCalculatedAverageCombinedFilePrioAndCredit = 0;
    m_fAverageCombinedFilePrioAndCredit = 0;
}

void CUploadQueue::RemoveOrMoveDown(CUpDownClient* client) {
    CUpDownClient* newclient = FindBestClientInQueue();

    if(newclient != NULL &&
        (newclient->HasReleasePrio() == true &&
         (client->IsFriend() == false || client->GetFriendSlot() == false) ||
         newclient->IsFriend() == true && newclient->GetFriendSlot() == true)) {

        // Remove client from ul list to make room for higher/same prio client
        client->SetWaitStartTime();
	    theApp.uploadqueue->RemoveFromUploadQueue(client);
	    theApp.uploadqueue->AddClientToQueue(client,true);
    } else {
        // PENDING: Clear done blocks to prevent chunk check from failing again
        client->ClearUploadDoneBlocks();
        //client->SetWaitStartTime();
        //client->SetUpStartTime();

        // Move down
        // first find the client in the uploadinglist
        POSITION foundPos = NULL;
        POSITION pos = uploadinglist.GetHeadPosition();
	    while(pos != NULL && foundPos == NULL) {
		    if (uploadinglist.GetAt(pos) == client){
                foundPos = pos;
		    }
            
            uploadinglist.GetNext(pos);
	    }

        if(foundPos != NULL) {
            // Remove the found Client
		    uploadinglist.RemoveAt(foundPos);

            // then add it last in it's class
            InsertInUploadingList(client);
        }
    }
}

CUpDownClient* CUploadQueue::FindBestClientInQueue() {
	POSITION toadd = 0;
	uint32	bestscore = 0;
	CUpDownClient* newclient = NULL;

    POSITION pos1, pos2;
	for (pos1 = waitinglist.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		waitinglist.GetNext(pos1);
		CUpDownClient* cur_client =	waitinglist.GetAt(pos2);
		// clear dead clients
		ASSERT ( cur_client->GetLastUpRequest() );
		if ((::GetTickCount() - cur_client->GetLastUpRequest() > MAX_PURGEQUEUETIME) || !theApp.sharedfiles->GetFileByID(cur_client->reqfileid) ){
			RemoveFromWaitingQueue(pos2,true);	
			if (!cur_client->socket)
				cur_client->Disconnected();
		}
		// finished clearing
		else if ((newclient != NULL &&
                    ((cur_client->IsFriend() == true && cur_client->GetFriendSlot() == true) &&
                    (newclient->IsFriend() == false || newclient->GetFriendSlot() == false) ||
                    newclient->IsFriend() == cur_client->IsFriend() && 
                    newclient->GetFriendSlot() == cur_client->GetFriendSlot() &&
                    (newclient->HasReleasePrio() == false && cur_client->HasReleasePrio() == true ||
                    newclient->HasReleasePrio() ==  cur_client->HasReleasePrio() &&
                    cur_client->GetScore(true) > bestscore
                    )
                    ) ||
                    newclient == NULL && cur_client->GetScore(true) > bestscore
                    ) &&
                    (!cur_client->IsBanned()) && 
				    (!cur_client->HasLowID() || (cur_client->socket && cur_client->socket->IsConnected())) &&
                    IsDownloading(cur_client) == false){
            // cur_client is more worthy. Save it.
			bestscore = cur_client->GetScore(true);
			toadd = pos2;
            newclient = waitinglist.GetAt(toadd);
		}
	}

    if (!toadd) {
		return NULL;
    } else {
	    return waitinglist.GetAt(toadd);
    }
}

void CUploadQueue::InsertInUploadingList(CUpDownClient* newclient) {
	if(newclient->IsFriend() && newclient->GetFriendSlot()) {
        // it's a friend. Add it last of friends.

        bool isFriend = true;
        POSITION insertPosition = uploadinglist.GetHeadPosition();
        while(insertPosition != NULL && isFriend == true) {
            CUpDownClient* uploadingClient = uploadinglist.GetAt(insertPosition);
            if(uploadingClient->IsFriend() == false || uploadingClient->GetFriendSlot() == false) {
                isFriend = false;
            } else {
                uploadinglist.GetNext(insertPosition);
            }
        }

        if(insertPosition != NULL) {
            // current pos is first pos that does not contain friend.
		    uploadinglist.InsertBefore(insertPosition, newclient);
        } else {
            // all were friends. Add it last
		    uploadinglist.AddTail(newclient);
        }
    } else if(newclient->HasReleasePrio() == true) {
        // it's release prio. Add it after all friends and
        // after all release downloaders

        bool isFriendOrRelease = true;
        POSITION insertPosition = uploadinglist.GetHeadPosition();
        while(insertPosition != NULL && isFriendOrRelease == true) {
            CUpDownClient* uploadingClient = uploadinglist.GetAt(insertPosition);
            if((uploadingClient->IsFriend() == false || uploadingClient->GetFriendSlot() == false) &&
                uploadingClient->HasReleasePrio() == false) {
                isFriendOrRelease = false;
            } else {
                uploadinglist.GetNext(insertPosition);
            }
        }

        if(insertPosition != NULL) {
            // current pos is first pos that does not contain friend or releaser.
		    uploadinglist.InsertBefore(insertPosition, newclient);
        } else {
            // all were friends or releasers. Add it last
		    uploadinglist.AddTail(newclient);
        }
	} else {
        // it's normal. Add it last.
		uploadinglist.AddTail(newclient);
	}
}

bool CUploadQueue::AddUpNextClient(CUpDownClient* directadd, bool highPrioCheck) {
	//POSITION toadd = 0;
	//uint32	bestscore = 0;
	CUpDownClient* newclient = NULL;
	// select next client or use given client
	if (!directadd){
        newclient = FindBestClientInQueue();

        if(highPrioCheck == true) {
		    POSITION lastpos = uploadinglist.GetTailPosition();

            CUpDownClient* lastClient = NULL;
            if(lastpos != NULL) {
                lastClient = uploadinglist.GetAt(lastpos);
            }

            if(lastClient != NULL) {
                if (newclient->HasReleasePrio() == true && lastClient->HasReleasePrio() == false &&
                    (lastClient->IsFriend() == false || lastClient->GetFriendSlot() == false) ||
                    newclient->IsFriend() == true && newclient->GetFriendSlot() == true &&
                    (lastClient->IsFriend() == false || lastClient->GetFriendSlot() == false)) {
                    // Remove last client from ul list to make room for higher prio client
		            theApp.uploadqueue->RemoveFromUploadQueue(lastClient, true, true);

		            // add to queue again. This is almost the same as in method
		            //    theApp.uploadqueue->AddClientToQueue(lastClient,true);

                    AddClientToQueue(lastClient,true, true);

                    //waitinglist.AddTail(lastClient);
		            //lastClient->SetUploadState(US_ONUPLOADQUEUE);
		            //lastClient->SendRankingInfo();
		            //theApp.emuledlg->transferwnd.queuelistctrl.AddClient(lastClient, false);
		            //theApp.emuledlg->transferwnd.ShowQueueCount(waitinglist.GetCount());
                } else {
                    return false;
                }
            }
        }

		//RemoveFromWaitingQueue(toadd, true);
        RemoveFromWaitingQueue(newclient, true);
		theApp.emuledlg->transferwnd.ShowQueueCount(waitinglist.GetCount());
	} else
		newclient = directadd;

    if(newclient == NULL) {
        return false;
    }

	if (IsDownloading(newclient)) {
		return false;
	}
	// tell the client that we are now ready to upload
	if (!newclient->socket || !newclient->socket->IsConnected()) {
		newclient->SetUploadState(US_CONNECTING);
		newclient->TryToConnect(true);
	} else {
		Packet* packet = new Packet(OP_ACCEPTUPLOADREQ,0);
		theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
		newclient->socket->SendPacket(packet,true);
		newclient->SetUploadState(US_UPLOADING);
	}

	newclient->SetUpStartTime();
	newclient->ResetSessionUp();

    InsertInUploadingList(newclient);

	// statistic
	CKnownFile* reqfile = theApp.sharedfiles->GetFileByID((uchar*)newclient->reqfileid);
	if (reqfile)
		reqfile->statistic.AddAccepted();
	
	theApp.emuledlg->transferwnd.uploadlistctrl.AddClient(newclient);

    return true;
}

void CUploadQueue::Process(){
    DWORD curTick = ::GetTickCount();
    int timeSinceLastGaveDataMs;

    // after 49 days tick wraps around. I think this still handles it. (Yeah right! As if we wouldn't have crashed by then :))
    timeSinceLastGaveDataMs = curTick - m_lastGaveDataTick;

#define MAXNORMALTIMEBETWEENCALLSMS 500
    if(timeSinceLastGaveDataMs > MAXNORMALTIMEBETWEENCALLSMS) {
        m_dwLastExtremeUploadAmountTick = curTick;
    }

    // Keeps track of how much bandWidth we use this call
    sint32 usedBandwidthThisCall = 0;

    // PENDING: Each 3 seconds
    if(curTick - m_dwLastCheckedForHighPrioClient >= 3*1000) {
        bool added = AddUpNextClient(NULL, true);
        if(added == false) {
            // set timer so we can wait a while
            m_dwLastCheckedForHighPrioClient = curTick;
        } else {
            // there might be another highprio client
            // don't set timer, so that we check next call as well
        }
    }

    // How much data should be given each round?
	if(theApp.glob_prefs->GetMaxUpload() == UNLIMITED) {
        // Special case for unlimited upload

        if (AcceptNewClient(uploadinglist.GetCount()) && waitinglist.GetCount()) {
		    AddUpNextClient();
        }

        bool clientWasReady = false;

	    POSITION pos = uploadinglist.GetHeadPosition();
	    while (pos != NULL){
            // Get the client. Note! Also updates activeClientsListPos as a side effect.
		    CUpDownClient* cur_client = uploadinglist.GetNext(pos);
            bool tempWantsMoreBandwidth;
		    usedBandwidthThisCall += cur_client->SendBlockData(m_guessedMaxBandwidth-usedBandwidthThisCall, tempWantsMoreBandwidth);

            if(tempWantsMoreBandwidth == true) {
                clientWasReady = true;
            }
	    }

	    if (clientWasReady == false){
            if(m_guessedMaxBandwidth > 300) {
		        m_guessedMaxBandwidth -= 200;
            } else {
                m_guessedMaxBandwidth = 100;
            }
	    } else {
		    m_guessedMaxBandwidth += 200;
	    }

	    activeClients_list.AddTail(uploadinglist.GetCount());
        m_MaxActiveClients = uploadinglist.GetCount();
    } else {
        // Normal case for limited upload.

        uint32 tempMaxActiveClients = 0;
        uint32 tempMaxActiveClientsShortTime = 0;
        POSITION activeClientsTickPos = activeClients_tick_list.GetHeadPosition();
        POSITION activeClientsListPos = activeClients_list.GetHeadPosition();
        while(activeClientsListPos != NULL) {
            DWORD activeClientsTickSnapshot = activeClients_tick_list.GetAt(activeClientsTickPos);
            uint32 activeClientsSnapshot = activeClients_list.GetAt(activeClientsListPos);

            if(activeClientsSnapshot > tempMaxActiveClients) {
                tempMaxActiveClients = activeClientsSnapshot;
            }

            if(activeClientsSnapshot > tempMaxActiveClientsShortTime && curTick - activeClientsTickSnapshot < 10 * 1000) {
                tempMaxActiveClientsShortTime = activeClientsSnapshot;
            }

            activeClients_tick_list.GetNext(activeClientsTickPos);
            activeClients_list.GetNext(activeClientsListPos);
        }
        m_MaxActiveClients = tempMaxActiveClients;
        m_MaxActiveClientsShortTime = tempMaxActiveClientsShortTime;
        //m_MaxActiveClientsShortTime = (float)m_MaxActiveClientsShortTime*0.7 + (float)tempMaxActiveClientsShortTime*0.3;

        sint64 realMaxULRatems = (theApp.glob_prefs->GetMaxUpload()*1024*timeSinceLastGaveDataMs)/1000;

#define MINNUMBEROFTRICKLEUPLOADS 2

        // How many slots should be open? Trickle slots included (at least 2 trickles, 30% of total, and the slots are expected to use UPLOAD_CLIENT_REALISTIC_AVERAGE_DATARATE in average, whichever number is largest)
        sint32 wantedNumberOfTotalUploads = max((uint32)(theApp.glob_prefs->GetMaxUpload()*1024/UPLOAD_CLIENT_REALISTIC_AVERAGE_DATARATE), m_MaxActiveClientsShortTime + MINNUMBEROFTRICKLEUPLOADS);
        wantedNumberOfTotalUploads = max(wantedNumberOfTotalUploads, m_MaxActiveClientsShortTime*1.3);

		POSITION lastpos = uploadinglist.GetTailPosition();

        CUpDownClient* lastClient = NULL;
        if(lastpos != NULL) {
            lastClient = uploadinglist.GetAt(lastpos);
        }

#define MINWAITBEFOREOPENANOTHERSLOTMS 1000

        // Add or remove connections as needed. Mostly, this is controlled by the number of
        // fully active uploads.
        if(AcceptNewClient(uploadinglist.GetCount()) && waitinglist.GetCount() > 0 &&
           wantedNumberOfTotalUploads > uploadinglist.GetCount() &&
           (lastClient == NULL || lastClient->GetUpStartTimeDelay() > MINWAITBEFOREOPENANOTHERSLOTMS) &&
           curTick - m_dwLastExtremeUploadAmountTick > 5000) {
            // There's not enough open uploads. Open another one.
            AddUpNextClient();
        } else if(leftoverBandwidth <= 0 &&
                  uploadinglist.GetCount() > MIN_UP_CLIENTS_ALLOWED &&
                  (
                   uploadinglist.GetCount() > wantedNumberOfTotalUploads &&
                   uploadinglist.GetCount() > m_MaxActiveClients*1.3 &&
                   uploadinglist.GetCount() > (int)(m_MaxActiveClients + MINNUMBEROFTRICKLEUPLOADS) ||
                   AcceptNewClient(uploadinglist.GetCount()-1) == false)) {
            // There's to many open uploads (propably due to the user changing
            // the upload limit to a lower value). Remove the last opened upload and put
            // it back on the waitinglist. When it is put back, it get
            // to keep its waiting time. This means it is likely to soon be
            // choosen for upload again.
            if(lastClient->GetUpStartTimeDelay() > 3*1000) {
		        // Remove from upload list.
		        RemoveFromUploadQueue(lastClient, true, true);

		        // add to queue again. This is almost the same as in method
		        //    theApp.uploadqueue->AddClientToQueue(lastClient,true);
                AddClientToQueue(lastClient,true, true);

          //      waitinglist.AddTail(lastClient);
		        //lastClient->SetUploadState(US_ONUPLOADQUEUE);
		        //lastClient->SendRankingInfo();
		        //theApp.emuledlg->transferwnd.queuelistctrl.AddClient(lastClient, false);
		        //theApp.emuledlg->transferwnd.ShowQueueCount(waitinglist.GetCount());
            }
        }

        // There's some saved bandwidth that we want to try to spend this round.
        // curMaxULRatems keeps track of how much data we want to spend.
        sint32 curMaxULRatems = realMaxULRatems + leftoverBandwidth;

        sint32 allowedBandwidthThisCall = min(curMaxULRatems, realMaxULRatems+MAXFRAGSIZE);

        // Which active slot are we giving data each loop
        uint32 slotcounter = 0;

        bool wantsMoreBandWidth = false;

        POSITION ulpos = uploadinglist.GetHeadPosition();
        // The loop that gives the fully activated connections data.
        while (ulpos != NULL && /*slotcounter < activeClients &&*/ (allowedBandwidthThisCall > usedBandwidthThisCall) && wantsMoreBandWidth == false) {
            // Only allow this connection to take what has not already been taken
            // by the other connections above it in the list.
            uint32 allowedThisIteration = allowedBandwidthThisCall-usedBandwidthThisCall;

            // Get the client. Note! Also updates ulpos as a side effect.
		    CUpDownClient* cur_client = uploadinglist.GetNext(ulpos);

            // Give data to the upload, if it wants it. Remember how much data we have given this call.
            // The connection wont take any data if it hasn't emptied its buffer since we last gave it data.
            // variable wantsMoreBandWidth is updated by this method (it is a ref bool&)
		    usedBandwidthThisCall += cur_client->SendBlockData(allowedThisIteration, wantsMoreBandWidth);

            slotcounter++;
	    }

#define MAXTIMESLOTISALLOWEDTOGOWITHOUTDATAMS 2540
        // Trickle the unneeded uploads (just give them enough to not time out)
        // Theese downloads are kept connected, in a ready-to-go state, just in case
        // one of the fully activated uploads completes/timeouts/ends.
        // As soon as there's a little bandwidth leftover, the first one of these
        // uploads will go to fully activated state
        POSITION trickle_client_pos = uploadinglist.FindIndex(slotcounter);
        while (trickle_client_pos != NULL){
            // Get the client. Note! Also updates trickle_client_pos as a side effect.
	        CUpDownClient* cur_client = uploadinglist.GetNext(trickle_client_pos);

            if(cur_client->GetLastGotULData() + MAXTIMESLOTISALLOWEDTOGOWITHOUTDATAMS < ::GetTickCount()) {
                // It's more than 3 seconds since this connection got any data.
                // Feed it a mercy package to prevent it from timing out.
                bool tempWantsMoreData;
                usedBandwidthThisCall += cur_client->SendBlockData(MAXFRAGSIZE, tempWantsMoreData);
            }
        }

        // Since we don't save bandwidth for the trickles above, we may
        // have used to much bandwidth.
        // We may also have used to little bandwidth this round, in that
        // case it is saved to the next round.
        leftoverBandwidth = curMaxULRatems-usedBandwidthThisCall;
        
        // We don't want to long peaks. Don't save to much bandwidth (about a second should be OK)
        // Always make sure we can save enough to get a packet through
        sint32 limitSave = MAXFRAGSIZE; // max(realMaxULRatems*10, MAXFRAGSIZE+realMaxULRatems*4);
        if(leftoverBandwidth > limitSave) {
            leftoverBandwidth = limitSave;
        }

        // Save number of active clients for statistics
   	    //activeClients_list.RemoveHead();
	    activeClients_list.AddTail(slotcounter);
    }

    // Save used bandwidth for speed calculations
	//avarage_dr_list.AddTail(usedBandwidthThisCall);
	avarage_dr_list.AddTail(theApp.stat_sessionSentBytes);

    avarage_friend_dr_list.AddTail(theApp.stat_sessionSentBytesToFriend);

    // Save time beetween each speed snapshot
    avarage_tick_list.AddTail(curTick);

    // don't save more than 40 secs of data
    while(curTick-avarage_tick_list.GetHead() > 40*1000) {
   	    avarage_dr_list.RemoveHead();
        avarage_friend_dr_list.RemoveHead();
        avarage_tick_list.RemoveHead();
    }

    // handle data about active clients
    // actual number of clients has been saved above, both in unlimited case (0)
    // limited case (number of active clients)
    activeClients_tick_list.AddTail(curTick);
    while(curTick-activeClients_tick_list.GetHead() > 3*60*1000) {
        activeClients_tick_list.RemoveHead();
	    activeClients_list.RemoveHead();
    }

    // Save time to be able to calculate how long between each call
    m_lastGaveDataTick = curTick;
};

bool CUploadQueue::AcceptNewClient(uint32 numberOfUploads){
	// check if we can allow a new client to start downloading form us
	if (numberOfUploads < MIN_UP_CLIENTS_ALLOWED)
		return true;
	else if (numberOfUploads >= MAX_UP_CLIENTS_ALLOWED)
		return false;

	//now the final check
	if (theApp.glob_prefs->GetMaxUpload() == UNLIMITED){
		if (numberOfUploads < (GetDatarate()/UPLOAD_CLIENT_DATARATE))
			return true;
	} else {
		if (numberOfUploads < (uint32)(theApp.glob_prefs->GetMaxUpload()*1024/UPLOAD_CLIENT_DATARATE)+1)
			return true;
	}
	//nope
	return false;
}

CUploadQueue::~CUploadQueue(){
	KillTimer(0,141);
}

POSITION CUploadQueue::GetWaitingClient(CUpDownClient* client){
	return waitinglist.Find(client); 
}

/*POSITION CUploadQueue::GetWaitingClientByID(CUpDownClient* client){
	for (POSITION pos = waitinglist.GetHeadPosition();pos != 0;waitinglist.GetNext(pos)){
		if (client->Compare(waitinglist.GetAt(pos)))
			return pos;
	}
	return 0;
}*/

CUpDownClient* CUploadQueue::GetWaitingClientByIP(uint32 dwIP){
	for (POSITION pos = waitinglist.GetHeadPosition();pos != 0;waitinglist.GetNext(pos)){
		if (dwIP == waitinglist.GetAt(pos)->GetIP())
			return waitinglist.GetAt(pos);
	}
	return 0;
}

POSITION CUploadQueue::GetDownloadingClient(CUpDownClient* client){
	for (POSITION pos = uploadinglist.GetHeadPosition();pos != 0;uploadinglist.GetNext(pos)){
		if (client == uploadinglist.GetAt(pos))
			return pos;
	}
	return 0;
}

void CUploadQueue::UpdateBanCount(){
	int count=0;
	for (POSITION pos = waitinglist.GetHeadPosition();pos != 0;waitinglist.GetNext(pos)){
		CUpDownClient* cur_client= waitinglist.GetAt(pos);
		if(cur_client->IsBanned())
			count++;
	}
	SetBanCount(count);
}
void CUploadQueue::AddClientToQueue(CUpDownClient* client, bool bIgnoreTimelimit, bool addInFirstPlace){
    if(addInFirstPlace == false) {
	    if (theApp.serverconnect->IsConnected() && theApp.serverconnect->IsLowID()
		    && !theApp.serverconnect->IsLocalServer(client->GetServerIP(),client->GetServerPort())
		    && client->GetDownloadState() == DS_NONE && !client->IsFriend()
		    && GetWaitingUserCount() > 50)
		    return;
	    client->AddAskedCount();
	    client->SetLastUpRequest();
	    if (!bIgnoreTimelimit){
		    if (client->IsBanned()){
			    if (::GetTickCount() - client->GetBanTime() > 18000000){
				    client->UnBan();
			    }
			    else
				    return;
		    }
		    client->AddRequestCount(client->reqfileid);
	    }
    }

	// check for double
	for (POSITION pos = waitinglist.GetHeadPosition();pos != 0;waitinglist.GetNext(pos)){
		CUpDownClient* cur_client= waitinglist.GetAt(pos);
		if (cur_client == client){	//already on queue
			client->SendRankingInfo();
			theApp.emuledlg->transferwnd.queuelistctrl.RefreshClient(client);
			return;			
		}
		else if ( client->Compare(cur_client) ) {
			// another client with same ip or hash
			theApp.emuledlg->AddDebugLogLine(false,CString(GetResString(IDS_SAMEUSERHASH)),client->GetUserName(),cur_client->GetUserName(),cur_client->GetUserName() );
			RemoveFromWaitingQueue(pos,true);	
			if (!cur_client->socket)
				cur_client->Disconnected();	
			return;
		}
	}
	// done

	// Add clients server to list.
	if (theApp.glob_prefs->AddServersFromClient()){
		in_addr host;
		host.S_un.S_addr = client->GetServerIP();
		CServer* srv = new CServer(client->GetServerPort(), inet_ntoa(host));
		srv->SetListName(srv->GetAddress());

		if (!theApp.emuledlg->serverwnd.serverlistctrl.AddServer(srv, true))
			delete srv;
		/*else
			theApp.emuledlg->AddLogLine(false,"Added new server: %s:%d", srv->GetFullIP(), srv->GetPort());*/
	}

    if(addInFirstPlace == false) {
	    // statistic values
	    CKnownFile* reqfile = theApp.sharedfiles->GetFileByID((uchar*)client->reqfileid);
	    if (reqfile)
		    reqfile->statistic.AddRequest();
	    // better ways to cap the list
        uint32 softQueueLimit;
        uint32 hardQueueLimit;

        if(theApp.glob_prefs->GetQueueSize()+bannedcount < 500) {
            softQueueLimit = theApp.glob_prefs->GetQueueSize()+bannedcount;
            hardQueueLimit = theApp.glob_prefs->GetQueueSize()+bannedcount + 200;
        } else {
            softQueueLimit = (theApp.glob_prefs->GetQueueSize()+bannedcount)/4*3;
            hardQueueLimit = theApp.glob_prefs->GetQueueSize()+bannedcount;
        }

        if ((uint32)waitinglist.GetCount() > softQueueLimit &&
            (client->IsFriend() == false || client->GetFriendSlot() == false) &&
            client->GetCombinedFilePrioAndCredit() < GetAverageCombinedFilePrioAndCredit()) {
		    return;
        }
    }

	if (client->IsDownloading()){
		// he's already downloading and wants probably only another file
		Packet* packet = new Packet(OP_ACCEPTUPLOADREQ,0);
		theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
		client->socket->SendPacket(packet,true);
		return;
	}
    //if (waitinglist.IsEmpty() && AcceptNewClient()){
    //	AddUpNextClient(client);
    //	m_nLastStartUpload = ::GetTickCount();
    //}
    //else{

    waitinglist.AddTail(client);
    client->SetUploadState(US_ONUPLOADQUEUE);
    client->SendRankingInfo();
    theApp.emuledlg->transferwnd.queuelistctrl.AddClient(client);

    theApp.emuledlg->transferwnd.ShowQueueCount(waitinglist.GetCount());
    //}
}

float CUploadQueue::GetAverageCombinedFilePrioAndCredit() {
    DWORD curTick = ::GetTickCount();

    if (curTick - m_dwLastCalculatedAverageCombinedFilePrioAndCredit > 5*1000) {
        m_dwLastCalculatedAverageCombinedFilePrioAndCredit = curTick;

        POSITION pos1, pos2;

        // TODO: is there a risk of overflow? I don't think so...
        double sum = 0;

	    for (pos1 = waitinglist.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		    waitinglist.GetNext(pos1);
		    CUpDownClient* cur_client =	waitinglist.GetAt(pos2);

            sum += cur_client->GetCombinedFilePrioAndCredit();
        }

        m_fAverageCombinedFilePrioAndCredit = sum/waitinglist.GetSize();
    }

    return m_fAverageCombinedFilePrioAndCredit;
}

void CUploadQueue::RemoveLowestFromWaitinglist() {
	POSITION toremove = 0;
	uint32	bestscore = 0;
	CUpDownClient* removeclient = NULL;

    POSITION pos1, pos2;
	for (pos1 = waitinglist.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		waitinglist.GetNext(pos1);
		CUpDownClient* cur_client =	waitinglist.GetAt(pos2);
        if ((removeclient != NULL &&
             ((cur_client->IsFriend() == false || cur_client->GetFriendSlot() == false) &&
              (removeclient->IsFriend() == true && removeclient->GetFriendSlot() == true) ||
              removeclient->IsFriend() == cur_client->IsFriend() && 
              removeclient->GetFriendSlot() == cur_client->GetFriendSlot() &&
              (removeclient->HasReleasePrio() == true && cur_client->HasReleasePrio() == false ||
               removeclient->GetCombinedFilePrioAndCredit() > cur_client->GetCombinedFilePrioAndCredit() ||
               removeclient->GetCombinedFilePrioAndCredit() ==  cur_client->GetCombinedFilePrioAndCredit() &&
               bestscore > cur_client->GetScore(true)
              )
             ) ||
             removeclient == NULL
            ) &&
            IsDownloading(cur_client) == false) {
                // cur_client is less worthy. Remember it.
                bestscore = cur_client->GetScore(true);
                toremove = pos2;
                removeclient = waitinglist.GetAt(toremove);
            }
	}

    if (toremove) {
	    RemoveFromWaitingQueue(toremove, true);
    }
}

bool CUploadQueue::RemoveFromUploadQueue(CUpDownClient* client, bool updatewindow, bool earlyabort){
	for (POSITION pos = uploadinglist.GetHeadPosition();pos != 0;uploadinglist.GetNext(pos)){
		if (client == uploadinglist.GetAt(pos)){
			if (updatewindow)
				theApp.emuledlg->transferwnd.uploadlistctrl.RemoveClient(uploadinglist.GetAt(pos));
			uploadinglist.RemoveAt(pos);
			if( client->GetTransferedUp() || earlyabort == true ){
				successfullupcount++;
				totaluploadtime += client->GetUpStartTimeDelay()/1000;
			} else if(client->HasBlocks() || client->GetUploadState() != US_UPLOADING) {
				failedupcount++;
			}

            client->SetUploadState(US_NONE);
			client->ClearUploadBlockRequests(!earlyabort);
			return true;
		}
	}
	return false;
}

uint32 CUploadQueue::GetAverageUpTime(){
	if( successfullupcount ){
		return totaluploadtime/successfullupcount;
	}
	return 0;
}

bool CUploadQueue::RemoveFromWaitingQueue(CUpDownClient* client, bool updatewindow){
	POSITION pos = waitinglist.Find(client);
	if (pos){
		RemoveFromWaitingQueue(pos,updatewindow);
		if (updatewindow)
			theApp.emuledlg->transferwnd.ShowQueueCount(waitinglist.GetCount());
		return true;
	}
	else
		return false;
}

void CUploadQueue::RemoveFromWaitingQueue(POSITION pos, bool updatewindow){	
	CUpDownClient* todelete = waitinglist.GetAt(pos);
	waitinglist.RemoveAt(pos);
	if( todelete->IsBanned() )
		todelete->UnBan();
	if (updatewindow)
		theApp.emuledlg->transferwnd.queuelistctrl.RemoveClient(todelete);
	todelete->SetUploadState(US_NONE);
}

void CUploadQueue::DeleteAll(){
	waitinglist.RemoveAll();
	uploadinglist.RemoveAll();
}

uint16 CUploadQueue::GetWaitingPosition(CUpDownClient* client){
	if (!IsOnUploadQueue(client))
		return 0;
	uint16 rank = 1;
	uint32 myscore = client->GetScore(false);
	for (POSITION pos = waitinglist.GetHeadPosition();pos != 0;waitinglist.GetNext(pos)){
		if (waitinglist.GetAt(pos)->GetScore(false) > myscore)
			rank++;
	}
	return rank;
}

void CUploadQueue::CompUpDatarateOverhead(){
	this->m_AvarageUDRO_list.AddTail(m_nUpDataRateMSOverhead);
	if (m_AvarageUDRO_list.GetCount() > 150)
		m_AvarageUDRO_list.RemoveAt(m_AvarageUDRO_list.GetHeadPosition());
	m_nUpDatarateOverhead = 0;
	m_nUpDataRateMSOverhead = 0;
	for (POSITION pos = m_AvarageUDRO_list.GetHeadPosition();pos != 0;m_AvarageUDRO_list.GetNext(pos))
		m_nUpDatarateOverhead += m_AvarageUDRO_list.GetAt(pos);

	if(m_AvarageUDRO_list.GetCount() > 10)
		m_nUpDatarateOverhead = 10*m_nUpDatarateOverhead/m_AvarageUDRO_list.GetCount();
	else
		m_nUpDatarateOverhead = 0;
	return;
}

VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg,UINT_PTR idEvent,DWORD dwTime){

	// Barry - Don't do anything if the app is shutting down - can cause unhandled exceptions
	if (!theApp.emuledlg->IsRunning())
		return;

	theApp.uploadqueue->Process();
	theApp.downloadqueue->Process();
	theApp.uploadqueue->CompUpDatarateOverhead();
	theApp.downloadqueue->CompDownDatarateOverhead();
	counter++;

	// one second
	if (counter >= 10){
		counter=0;
		
		theApp.clientcredits->Process();
		theApp.serverlist->Process();
		theApp.friendlist->Process();
		if( theApp.serverconnect->IsConnecting() && !theApp.serverconnect->IsSingleConnect() )
			theApp.serverconnect->TryAnotherConnectionrequest();

		theApp.emuledlg->statisticswnd.UpdateConnectionsStatus();

		if (theApp.serverconnect->IsConnecting()) theApp.serverconnect->CheckForTimeout();

		// display graphs
		if (theApp.glob_prefs->GetTrafficOMeterInterval()>0) {
			igraph++;

			if (igraph >= (uint32)(theApp.glob_prefs->GetTrafficOMeterInterval()) ) {
				igraph=0;
				theApp.emuledlg->statisticswnd.SetCurrentRate((float)theApp.uploadqueue->GetDatarate()/1024,(float)theApp.downloadqueue->GetDatarate()/1024, (float)(theApp.uploadqueue->GetDatarate()-theApp.uploadqueue->GetToNetworkDatarate())/1024);
			}
		}
		if (theApp.emuledlg->activewnd == &theApp.emuledlg->statisticswnd && theApp.emuledlg->IsWindowVisible() )  {
			// display stats
			if (theApp.glob_prefs->GetStatsInterval()>0) {
				istats++;

				if (istats >= (uint32)(theApp.glob_prefs->GetStatsInterval()) ) {
					istats=0;
					theApp.emuledlg->ShowStatistics();
				}
			}
		}
		//save rates every second
		theApp.emuledlg->statisticswnd.RecordRate();

		
		sec++;
		// 5 seconds
		if (sec>=5) {

			#ifdef _DEBUG
				if (!AfxCheckMemory()) AfxDebugBreak();
			#endif

			sec = 0;
			theApp.listensocket->Process();
			theApp.OnlineSig(); // Added By Bouc7 
			theApp.emuledlg->ShowTransferRate();
		}

		statsave++;
		if (statsave>=60) {
			statsave=0;
			CString buffer;
			char* fullpath = new char[strlen(theApp.glob_prefs->GetAppDir())+16];
			sprintf(fullpath,"%spreferences.ini",theApp.glob_prefs->GetAppDir());
			CIni ini( fullpath, "eMule" );
			delete[] fullpath;
			fullpath=NULL;

			buffer.Format("%I64Lu",theApp.stat_sessionReceivedBytes+theApp.glob_prefs->GetTotalDownloaded());
			ini.WriteString("TotalDownloadedBytes",buffer ,"Statistics");

			buffer.Format("%I64Lu",theApp.stat_sessionSentBytes+theApp.glob_prefs->GetTotalUploaded());
			ini.WriteString("TotalUploadedBytes",buffer ,"Statistics");
		}
	}

}

CUpDownClient* CUploadQueue::GetNextClient(CUpDownClient* lastclient){
	if (waitinglist.IsEmpty())
		return 0;
	if (!lastclient)
		return waitinglist.GetHead();
	POSITION pos = waitinglist.Find(lastclient);
	if (!pos){
		TRACE("Error: CServerList::GetNextClient");
		return waitinglist.GetHead();
	}
	waitinglist.GetNext(pos);
	if (!pos)
		return NULL;
	else
		return waitinglist.GetAt(pos);
}

void CUploadQueue::FindSourcesForFileById(CTypedPtrList<CPtrList, CUpDownClient*>* srclist, uchar* filehash) {
	POSITION pos;
	
	pos = uploadinglist.GetHeadPosition();
	while(pos) {
		CUpDownClient *potential = uploadinglist.GetNext(pos);
		if(memcmp(potential->reqfileid, filehash, 16) == 0)
			srclist->AddTail(potential);
	}

	pos = waitinglist.GetHeadPosition();
	while(pos) {
		CUpDownClient *potential = waitinglist.GetNext(pos);
		if(memcmp(potential->reqfileid, filehash, 16) == 0)
			srclist->AddTail(potential);
	}
}

void CUploadQueue::UpdateDatarates() {
    // Calculate average datarate
    if(::GetTickCount()-m_lastCalculatedDataRateTick > 1000) {
        if(avarage_dr_list.GetSize() >= 2) {
	        datarate = (uint32)(((float)(avarage_dr_list.GetTail()-avarage_dr_list.GetHead()))*1000 / (avarage_tick_list.GetTail()-avarage_tick_list.GetHead()));
            friendDatarate = (uint32)(((float)(avarage_friend_dr_list.GetTail()-avarage_friend_dr_list.GetHead()))*1000 / (avarage_tick_list.GetTail()-avarage_tick_list.GetHead()));
        } else {
            datarate = 0;
            friendDatarate = 0;
        }
    }
}

uint32 CUploadQueue::GetDatarate() {
    UpdateDatarates();
    return datarate + friendDatarate;
}

uint32 CUploadQueue::GetToNetworkDatarate() {
    UpdateDatarates();
    return datarate;
}
