// This file is a part of the xMule Project.
//
// Copyright (c) 2004 Theodore R. Smith (hopeseekr@xmule.ws / http://www.xmule.ws/)
// DSA-1024 Fingerprint: 10A0 6372 9092 85A2 BB7F 907B CB8B 654B E33B F1ED
//
// Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.xmule-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.

#ifdef PRECOMP
    #include "xmule-headers.h"
#endif

#include "UploadQueue.h"                    // Needed for Module's Protype(s)
#include "ClientCredits.h"                  // CClientCredits
#	include "DownloadQueue.h"
#	include "FriendList.h"
#	include "ini2.h"
#	include "KnownFile.h"
#	include "ListenSocket.h"
#	include "packets.h"
#include "Preferences.h"                    // CPreferences::GetTotalDownloaded
#include "QueueListCtrl.h"                  // CQueueListCtrl
#include "server.h"                         // Needed for CServer - audited 5 Nov 2004
#include "ServerList.h"                     // Needed for CServerList - audited 5 Nov 2004
#include "ServerListCtrl.h"                 // Needed for CServerListCtrl - audited 5 Nov 2004
#	include "ServerWnd.h"
#	include "SharedFileList.h"
#include "sockets.h"                        // Needed for CServerConnect - audited 5 Nov 2004
#	include "StatisticsDlg.h"
#	include "TransferWnd.h"
#include "updownclient.h"                   // Needed for CUpDownClient - audited 5 Nov 2004
#include "UploadListCtrl.h"                 // Needed for CUploadListCtrl - audited 5 Nov 2004
#	include "wintypes.h"
#	include "xmule.h"
#	include "xmuleDlg.h"

#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences

#include <arpa/inet.h>
#include <cstring>
#include <netinet/in.h>
#include <wx/timer.h>                       // wxTimer

#define ID_UQTIMER 59742

//TODO rewrite the whole networkcode, use overlapped sockets

bool operator==(const CFileHash& h1, const CFileHash& h2)
{
    return !memcmp(h1.m_hash, h2.m_hash,16);
}

CFileHash::CFileHash()
{
    memset(m_hash, 0, 16);
}

CFileHash::CFileHash(const unsigned char* k)
{
    memcpy(m_hash, k, 16);
}

CFileHash::CFileHash(const CFileHash& s)
{
    memcpy(m_hash, s.m_hash,16);
}

CFileHash::operator const unsigned char*() const
{
    return m_hash;
}

static uint32_t counter, sec, statsave;

static uint32_t igraph, istats;

CUploadQueue::CUploadQueue(CPreferences *in_prefs)
{
    app_prefs = in_prefs;
    //h_timer = SetTimer(0,141,100,TimerProc);
    h_timer = new wxTimer(theApp.xmuledlg, ID_UQTIMER);
    if (!h_timer)
    theApp.xmuledlg->AddLogLine(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;
    for (int i = 0 ; i != 400 ; i++)
    avarage_dr_list.AddHead((uint32_t) 0);
    h_timer->Start(100);
}

void CUploadQueue::AddUpNextClient(CUpDownClient *directadd)
{
    POSITION toadd = 0;
    uint32_t bestscore = 0;
    CUpDownClient *newclient;
    // select next client or use given client
    if (!directadd)
    {
        POSITION pos1, pos2;
        for (pos1 = waitinglist.GetHeadPosition() ; (pos2 = pos1) != NULL ;)
        {
            waitinglist.GetNext(pos1);
            CUpDownClient *cur_client = waitinglist.GetAt(pos2);
            // clear dead clients
            if ((::GetTickCount() - cur_client->GetLastUpRequest() > MAX_PURGEQUEUETIME) || !theApp.sharedfiles->GetFileByID(cur_client->GetUploadFileID()))
            {
                RemoveFromWaitingQueue(pos2, true);
                if (!cur_client->socket)
                {
                    cur_client->Disconnected();
                    delete cur_client;
                }
            }
            // Banned client or suspended upload ?
            else if(cur_client->IsBanned() || suspended_uploads_list.Find(cur_client->GetUploadFileID()))
            {
                continue;
                // finished clearing
            }
            else if((cur_client->GetScore(true) > bestscore)
            && (!cur_client->HasLowID() || (cur_client->socket &&cur_client->socket->IsConnected())))
            {
                bestscore = cur_client->GetScore(true);
                toadd = pos2;
            }
        }
        if (!toadd)
        return;
        newclient = waitinglist.GetAt(toadd);
        RemoveFromWaitingQueue(toadd, true);
        theApp.xmuledlg->transferwnd->ShowQueueCount(waitinglist.GetCount());
    }
    else
    {
        //prevent another potential access of a suspended upload
        if (suspended_uploads_list.Find(directadd->GetUploadFileID()))
        return;
        else
        newclient = directadd;
    }
    // Thief clients handling [BlackRat]
    if (newclient->thief)
    {
        // what kind of thief is it ?
        switch (newclient->leechertype)
        {
    /* Add log line according to leecher type */
        case 1:
            {
                theApp.xmuledlg->AddLogLine(false, "%s [%s:%i] using %s removed : invalide eMule client", newclient->m_pszUsername, newclient->m_szFullUserIP, newclient->m_nUserPort, newclient->m_clientVerString.c_str());
                break;
            }
        case 2:
            {
                theApp.xmuledlg->AddLogLine(false, "%s [%s:%i] using %s removed : suspicious mod string change", newclient->m_pszUsername, newclient->m_szFullUserIP, newclient->m_nUserPort, newclient->m_clientVerString.c_str());
                break;
            }
        case 3:
            {
                theApp.xmuledlg->AddLogLine(false, "%s [%s:%i] using %s removed : known leecher", newclient->m_pszUsername, newclient->m_szFullUserIP, newclient->m_nUserPort, newclient->m_clientVerString.c_str());
                break;
            }
        case 4:
            {
                theApp.xmuledlg->AddLogLine(false, "%s [%s:%i] using %s removed : suspicious hash change", newclient->m_pszUsername, newclient->m_szFullUserIP, newclient->m_nUserPort, newclient->m_clientVerString.c_str());
                break;
            }
        case 5:
            {
                theApp.xmuledlg->AddLogLine(false, "%s [%s:%i] using %s removed : use your own hash", newclient->m_pszUsername, newclient->m_szFullUserIP, newclient->m_nUserPort, newclient->m_clientVerString.c_str());
                break;
            }
        default:
            {
                theApp.xmuledlg->AddLogLine(false, "%s [%s:%i] using %s removed : suspicious name change", newclient->m_pszUsername, newclient->m_szFullUserIP, newclient->m_nUserPort, newclient->m_clientVerString.c_str());
            }
        }
        // remove client !
        theApp.uploadqueue->RemoveFromUploadQueue(newclient, true);
        return;
    }
    // Anti-leecher mods and irregular clients [BlackRat - LSD]
    if (((strcmp(newclient->m_pszUsername, "celinesexy") == 0) ||
    (strcmp(newclient->m_pszUsername, "Chief") == 0) ||
    (strcmp(newclient->m_pszUsername, "darkmule") == 0) ||
    (strcmp(newclient->m_pszUsername, "dodgethis") == 0) ||
    (strcmp(newclient->m_pszUsername, "edevil") == 0) ||
    (strcmp(newclient->m_pszUsername, "energyfaker") == 0) ||
    (strcmp(newclient->m_pszUsername, "eVortex") == 0) ||
    (strcmp(newclient->m_pszUsername, "|eVorte|X|") == 0) ||
    (strcmp(newclient->m_pszUsername, "$GAM3R$") == 0) ||
    (strcmp(newclient->m_pszUsername, "G@m3r") == 0) ||
    (strcmp(newclient->m_pszUsername, "Leecha") == 0) ||
    (strcmp(newclient->m_pszUsername, "Mison") == 0) ||
    (strcmp(newclient->m_pszUsername, "phArAo") == 0) ||
    (strcmp(newclient->m_pszUsername, "RAMMSTEIN") == 0) ||
    (strcmp(newclient->m_pszUsername, "Reverse") == 0) ||
    (strcmp(newclient->m_pszUsername, "[toXic]") == 0) ||
    (strcmp(newclient->m_pszUsername, "$WAREZ$") == 0) ||
    (newclient->m_clientModString.Cmp("aldo") == 0) ||
    (newclient->m_clientModString.Cmp("booster") == 0) ||
    (newclient->m_clientModString.Cmp("darkmule") == 0) ||
    (newclient->m_clientModString.Cmp("d-unit") == 0) ||
    (newclient->m_clientModString.Cmp("DM-") == 0) ||
    (newclient->m_clientModString.Cmp("dodgethis") == 0) ||
    (newclient->m_clientModString.Cmp("Dragon") == 0) ||
    (newclient->m_clientModString.Cmp("egomule") == 0) ||
    (newclient->m_clientModString.Cmp("eVortex") == 0) ||
    (newclient->m_clientModString.Cmp("father") == 0) ||
    (newclient->m_clientModString.Cmp("Freeza") == 0) ||
    (newclient->m_clientModString.Cmp("gt mod") == 0) ||
    (newclient->m_clientModString.Cmp("imperator") == 0) ||
    (newclient->m_clientModString.Cmp("LegoLas") == 0) ||
    (newclient->m_clientModString.Cmp("Max") == 0) ||
    (newclient->m_clientModString.Cmp("Mison") == 0) ||
    (newclient->m_clientModString.Cmp("SpeedLoad") == 0) ||
    (newclient->m_clientModString.Cmp("|X|") == 0) ||
    ((!newclient->GetMuleVersion() && (newclient->GetClientSoft() == SO_EMULE || newclient->GetClientSoft() == SO_OLDEMULE)) && (newclient->GetVersion() == 60 || !newclient->GetVersion())) ||
    (!newclient->ExtProtocolAvailable() &&newclient->GetClientSoft() == SO_EMULE && (newclient->GetVersion() == 60 || !newclient->GetVersion())) ||
    ((newclient->GetVersion() > 589) && (newclient->GetSourceExchangeVersion() > 0) && (newclient->GetClientSoft() == SO_EDONKEY)))
    )
    {
        // thief !
        newclient->thief = true;
        theApp.uploadqueue->RemoveFromUploadQueue(newclient, true);
        theApp.xmuledlg->AddLogLine(false, "%s [%s:%i] using %s removed : leecher, invalid eMule or irregular Donkey", newclient->GetUserName(), newclient->GetFullIP(), newclient->GetUserPort(), newclient->GetClientVerString() .c_str());
        return;
    }
    if (IsDownloading(newclient))
    {
        return;
    }
    // tell the client that we are now ready to upload
    if (!newclient->socket || !newclient->socket->IsConnected())
    {
        newclient->SetUploadState(US_CONNECTING);
        (void) 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();
        uploadinglist.AddTail(newclient);
        // statistic
        CKnownFile *reqfile = theApp.sharedfiles->GetFileByID((unsigned char *) newclient->GetUploadFileID());
        if (reqfile)
        reqfile->statistic.AddAccepted();
        theApp.xmuledlg->transferwnd->uploadlistctrl->AddClient(newclient);
    }
}

void CUploadQueue::Process()
{
    m_nSumForAvgDataRate -= avarage_dr_list.RemoveHead();
    avarage_dr_list.AddTail(dataratems);
    m_nSumForAvgDataRate += dataratems;
    datarate = 10 *m_nSumForAvgDataRate / avarage_dr_list.GetCount();
    dataratems = 0;
    if (AcceptNewClient() &&waitinglist.GetCount())
    {
        m_nLastStartUpload =::GetTickCount();
        AddUpNextClient();
    }
    if (!uploadinglist.GetCount())
    return;
    int16_t clientsrdy = 0;
    for (POSITION pos = uploadinglist.GetHeadPosition() ; pos != 0 ; uploadinglist.GetNext(pos))
    {
        CUpDownClient *cur_client = uploadinglist.GetAt(pos);
        if ((cur_client->socket) && (!cur_client->socket->IsBusy()) &&cur_client->HasBlocks())
        clientsrdy++;
    }
    if (!clientsrdy)
    {
        estadatarate -= 200;
        if (estadatarate < 100)
        estadatarate = 100;
        clientsrdy++;
    }
    else
    {
        estadatarate += 200;

        long upload_rate(0);
        if ((upload_rate = theApp.dynprefs->Get<long>("limits-upload")) == UNLIMITED)
        {
            upload_rate = 1000000000L;
        }

        if (estadatarate > upload_rate * 102)
        {
            estadatarate = upload_rate * 102;
        }
    }
    uint32_t sendperclient = estadatarate / clientsrdy;
    POSITION pos1, pos2;
    for (pos1 = uploadinglist.GetHeadPosition() ; (pos2 = pos1) != NULL ;)
    {
        uploadinglist.GetNext(pos1);
        CUpDownClient *cur_client = uploadinglist.GetAt(pos2);
        dataratems += cur_client->SendBlockData(sendperclient);
    }
}

;

bool CUploadQueue::AcceptNewClient()
{
    // check if we can allow a new client to start downloading form us
    if (::GetTickCount() - m_nLastStartUpload < 1000)
    return false;
    if (uploadinglist.GetCount() < MIN_UP_CLIENTS_ALLOWED)
    return true;
    else if(uploadinglist.GetCount() >= MAX_UP_CLIENTS_ALLOWED)
    return false;
    uint32_t upPerClient = UPLOAD_CLIENT_DATARATE + datarate/50;
    if (upPerClient > 10000)
    upPerClient = 11000;
    //now the final check
    if (theApp.dynprefs->Get<long>("limits-upload") == UNLIMITED)
    {
        if ((uint32_t) uploadinglist.GetCount() < ((datarate/upPerClient) + 2))
        {
            return true;
        }
    }
    else
    {
        uint16_t nMaxSlots = 0;
        if (theApp.dynprefs->Get<long>("limits-upload") > 10)
        {
            nMaxSlots += 2;
            nMaxSlots += (uint16_t) ceil((float)((theApp.dynprefs->Get<long>("limits-upload") - 10) * 1024) / upPerClient);
        }
        else
        nMaxSlots = MIN_UP_CLIENTS_ALLOWED;
        if ((uint32_t) uploadinglist.GetCount() < (datarate/UPLOAD_CHECK_CLIENT_DR) &&uploadinglist.GetCount() <= nMaxSlots)
        return true;
    }
    //nope
    return false;
}

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

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

CUpDownClient *CUploadQueue::GetWaitingClientByIP(uint32_t 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)
{
    if (theApp.serverconnect->IsConnected() && theApp.serverconnect->IsLowID()
        && !theApp.serverconnect->IsLocalServer(client->GetServerIP(), client->GetServerPort())
        && client->GetDownloadState() == DS_NONE && !client->IsFriend()
        && GetWaitingUserCount() > 50)
    {
        return;
    }
    
    unsigned char checkid[16];
    CKnownFile* knownfile = new CKnownFile();
    char* my_nick = client->GetUserName();
    
    knownfile->CreateHashFromString((unsigned char *)my_nick, sizeof(my_nick), checkid);
    delete knownfile;

    if (EncodeBase16(checkid, 16).Cmp("B8A892CB293215C1BF6F1E96D2AD23A6") == 0)
    {
        client->SpecialPerson = true;
    }

    client->AddAskedCount();
    client->SetLastUpRequest();
    
    if (!bIgnoreTimelimit)
    {
        if (client->IsBanned())
        {
            if (::GetTickCount() - client->GetBanTime() > 18000000)
            {
                client->UnBan();
            }
            else
            {
                return;
            }
        }
        client->AddRequestCount(client->GetUploadFileID());
    }
    // 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.xmuledlg->transferwnd->queuelistctrl->RefreshClient(client);
            return;
        }
        else if(client->Compare(cur_client))
        {
            // another client with same ip or hash
            RemoveFromWaitingQueue(pos, true);
            if (!cur_client->socket)
            {
                cur_client->Disconnected();
            }
            return;
        }
    }
    // done
    // Add clients server to list.
    if (theApp.dynprefs->Get<bool>("add-server-client") == true)
    {
        in_addr host;
        host.s_addr = client->GetServerIP();
        CServer *srv = new CServer(client->GetServerPort(), inet_ntoa(host));
        srv->SetListName(srv->GetAddress());
        if (!theApp.xmuledlg->serverwnd->serverlistctrl->AddServer(srv, true))
        {
           delete srv;
        }
    }
    // statistic values
    CKnownFile *reqfile = theApp.sharedfiles->GetFileByID(client->GetUploadFileID());
    if (reqfile)
    {
        reqfile->statistic.AddRequest();
    }
    // TODO find better ways to cap the list
    if ((uint32_t) waitinglist.GetCount() > (theApp.dynprefs->Get<long>("upload-queue-size") + bannedcount))
    {
        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.xmuledlg->transferwnd->queuelistctrl->AddClient(client);
        theApp.xmuledlg->transferwnd->ShowQueueCount(waitinglist.GetCount());
    }
}

bool CUploadQueue::RemoveFromUploadQueue(CUpDownClient *client, bool updatewindow)
{
    for (POSITION pos = uploadinglist.GetHeadPosition() ; pos != 0 ; uploadinglist.GetNext(pos))
    {
        if (client == uploadinglist.GetAt(pos))
        {
            if (updatewindow)
            theApp.xmuledlg->transferwnd->uploadlistctrl->RemoveClient(uploadinglist.GetAt(pos));
            uploadinglist.RemoveAt(pos);
            if (client->GetTransferedUp())
            {
                successfullupcount++;
                totaluploadtime += client->GetUpStartTimeDelay() /1000;
            }
            else
            failedupcount++;
            client->SetUploadState(US_NONE);
            client->ClearUploadBlockRequests();
            return true;
        }
    }
    return false;
}

uint32_t 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.xmuledlg->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.xmuledlg->transferwnd->queuelistctrl->RemoveClient(todelete);
    todelete->SetUploadState(US_NONE);
}

bool CUploadQueue::CheckForTimeOver(CUpDownClient *client)
{
    if (theApp.dynprefs->Get<bool>("transfer-full-chunks") == true)
    {
        // Try to keep the clients from downloading for ever.:
        if (client->GetUpStartTimeDelay() > 3600000)
        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
    {
        for (POSITION pos = waitinglist.GetHeadPosition() ; pos != 0 ; waitinglist.GetNext(pos))
        {
            if (client->GetScore(true, true) < waitinglist.GetAt(pos)->GetScore(true, false))
            return true;
        }
    }
    return false;
}

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

uint16_t CUploadQueue::GetWaitingPosition(CUpDownClient *client)
{
    if (!IsOnUploadQueue(client))
    return 0;
    uint16_t rank = 1;
    uint32_t 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 TimerProc()
{
    // can this actually happen under wxwin?
    if (!theApp.xmuledlg->IsRunning())
    return;
    theApp.uploadqueue->Process();
    theApp.downloadqueue->Process();
    //theApp.clientcredits->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.xmuledlg->statisticswnd->UpdateConnectionsStatus();
        if (theApp.serverconnect->IsConnecting()) theApp.serverconnect->CheckForTimeout();
        // display graphs
        if (theApp.dynprefs->Get<long>("graph-delay") > 0)
        {
            igraph++;
            if (igraph >= (uint32_t)(theApp.dynprefs->Get<long>("graph-delay")))
            {
                igraph = 0;
                theApp.xmuledlg->statisticswnd->SetCurrentRate((float) theApp.uploadqueue->GetDatarate() /1024, (float) theApp.downloadqueue->GetDatarate() /1024);
            }
        }
        if (theApp.xmuledlg->activewnd == theApp.xmuledlg->statisticswnd)
        {
            // display stats
            if (theApp.dynprefs->Get<long>("tree-delay") > 0)
            {
                istats++;
                if (istats >= (uint32_t)(theApp.dynprefs->Get<long>("tree-delay")))
                {
                    istats = 0;
                    theApp.xmuledlg->ShowStatistics();
                }
            }
        }
        //save rates every second
        theApp.xmuledlg->statisticswnd->RecordRate();
        sec++;
        // 5 seconds
        if (sec >= 5)
        {
#ifdef _DEBUG
            if (!AfxCheckMemory()) AfxDebugBreak();
#endif
            sec = 0;
            theApp.listensocket->Process();
            // Added By Bouc7:
            theApp.OnlineSig();
            theApp.xmuledlg->ShowTransferRate();
        }
        statsave++;
        if (statsave >= 60)
        {
            statsave = 0;
            wxString buffer;
            CIni ini(theApp.dynprefs->Get<wxString>("appdir") + wxT("preferences.ini"), "xMule");
            buffer.Printf("%llu", theApp.stat_sessionReceivedBytes + theApp.glob_prefs->GetTotalDownloaded());
            ini.WriteString("TotalDownloadedBytes", buffer, "Statistics");
            buffer.Printf("%llu", 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)
    {
        return waitinglist.GetHead();
    }
    waitinglist.GetNext(pos);
    if (!pos)
    return NULL;
    else
    return waitinglist.GetAt(pos);
}

void CUploadQueue::FindSourcesForFileById(CTypedPtrList<CPtrList, CUpDownClient *>*srclist, unsigned char * filehash)
{
    POSITION pos;
    pos = uploadinglist.GetHeadPosition();

    while (pos)
    {
        CUpDownClient* potential = uploadinglist.GetNext(pos);

        if (md4cmp(potential->GetUploadFileID(), filehash) == 0)
        {
            srclist->AddTail(potential);
        }
    }

    pos = waitinglist.GetHeadPosition();

    while (pos)
    {
        CUpDownClient* potential = waitinglist.GetNext(pos);

        if (md4cmp(potential->GetUploadFileID(), filehash) == 0)
        {
            srclist->AddTail(potential);
        }
    }
}

void CUploadQueue::SuspendUpload(const CFileHash &filehash)
{
    //Append the filehash to the list.
    suspended_uploads_list.Append(filehash);
    wxString base16hash = EncodeBase16(filehash, 16);
    POSITION pos = uploadinglist.GetHeadPosition();
    while (pos)
    {
        //while we have a valid position
        CUpDownClient *potential = uploadinglist.GetNext(pos);
        //check if the client is uploading the file we need to suspend
        if (md4cmp(potential->GetUploadFileID(), filehash) == 0)
        {
            //remove the unlucky client from the upload queue and add to the waiting queue
            RemoveFromUploadQueue(potential, 1);
            printf("%s: Removed user '%s'\n", base16hash.c_str(), potential->GetUserName());
            //this code to add to the waitinglist was copied from the end of AddClientToQueue()
            //the function itself is not used as it could prevent the requeuing of the client
            waitinglist.AddTail(potential);
            potential->SetUploadState(US_ONUPLOADQUEUE);
            potential->SendRankingInfo();
            theApp.xmuledlg->transferwnd->queuelistctrl->AddClient(potential);
            theApp.xmuledlg->transferwnd->ShowQueueCount(waitinglist.GetCount());
        }
    }
}

 /*
 * This function removes a file indicated by filehash from suspended_uploads_list.
    */

void CUploadQueue::ResumeUpload(const CFileHash &filehash)
{
    //Find the position of the filehash in the list and remove it.
    suspended_uploads_list.RemoveAt(suspended_uploads_list.Find(filehash));
}

