// 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"
#else
#include "DownloadQueue.h"

#	include "ClientList.h"
#include "DownloadListCtrl.h"               // Needed for CDownloadListCtrl - audited 5 Nov 2004
#include "packets.h"                        // Packet
#	include "PartFile.h"
#include "SearchDlg.h"                      // CSearchDlg::ExecuteLinkED2K
#	include "SearchList.h"
#include "server.h"                         // CServer
#include "ServerList.h"                     // Need for CServerList - audited 5 Nov 2004
#include "ServerSocket.h"                   // CServerConnect
#include "SharedFileList.h"
#include "sockets.h"                        // Needed for CServerConnect - audited 6 Nov 2004
#	include "SysTray.h"
#	include "TransferWnd.h"
#	include "updownclient.h"
#	include "xmuleDlg.h"
#endif

#include "ED2KLink.h"                       // CED2KFileLink
#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences

#include <wx/filename.h>
#include <wx/listimpl.cpp>
#include <wx/file.h>
#include <wx/textfile.h>
#include <wx/utils.h>

#define		MAX_FILES_PER_UDP_PACKET	31
#define		MAX_REQUESTS_PER_SERVER		35

int CDownloadQueue::GetMaxFilesPerUDPServerPacket() const
{
    //from emule30e
    int iMaxFilesPerPacket;
    if (cur_udpserver &&cur_udpserver->GetUDPFlags() &SRV_UDPFLG_EXT_GETSOURCES)
    {
        // get max. file ids per packet
        if (m_cRequestsSentToServer < MAX_REQUESTS_PER_SERVER)
        {
            if (MAX_FILES_PER_UDP_PACKET < MAX_REQUESTS_PER_SERVER - m_cRequestsSentToServer)
            iMaxFilesPerPacket = MAX_FILES_PER_UDP_PACKET;
            else
            iMaxFilesPerPacket = MAX_REQUESTS_PER_SERVER - m_cRequestsSentToServer;
        }
        else
        {
            iMaxFilesPerPacket = 0;
        }
    }
    else
    iMaxFilesPerPacket = 1;
    return iMaxFilesPerPacket;
}

CDownloadQueue::CDownloadQueue(CPreferences *in_prefs, CSharedFileList *in_sharedfilelist)
{
    app_prefs = in_prefs;
    sharedfilelist = in_sharedfilelist;
    filesrdy = 0;
    datarate = 0;
    cur_udpserver = 0;
    lastfile = 0;
    lastudpsearchtime = 0;
    lastudpstattime = 0;
    udcounter = 0;
    m_cRequestsSentToServer = 0;
    m_nDownDataRateMSOverhead = 0;
    m_nDownDatarateOverhead = 0;
    m_nDownDataOverheadSourceExchange = 0;
    m_nDownDataOverheadFileRequest = 0;
    m_nDownDataOverheadOther = 0;
    m_nDownDataOverheadServer = 0;
    m_nDownDataOverheadSourceExchangePackets = 0;
    m_nDownDataOverheadFileRequestPackets = 0;
    m_nDownDataOverheadOtherPackets = 0;
    m_nDownDataOverheadServerPackets = 0;
    m_nLastED2KLinkCheck = 0;
    m_lastRefreshedDLDisplay = 0;
}

void CDownloadQueue::UpdateDisplayedInfo(bool force)
{
    DWORD curTick =::GetTickCount();
    if (force || curTick - m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE + (uint32_t)(rand() / (RAND_MAX/1000)))
    {
        theApp.xmuledlg->transferwnd->downloadlistctrl->UpdateItem(this);
        m_lastRefreshedDLDisplay = curTick;
    }
}

void CDownloadQueue::AddPartFilesToShare()
{
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        if (cur_file->GetStatus(true) == PS_READY)
        {
            sharedfilelist->SafeAddKFile(cur_file, true);
            printf("Sharing %s\n", cur_file->GetFullName());
        }
    }
}

void CDownloadQueue::CompDownDatarateOverhead()
{
    m_AvarageDDRO_list.AddTail(m_nDownDataRateMSOverhead);
    if (m_AvarageDDRO_list.GetCount() > 150)
    {
        m_AvarageDDRO_list.RemoveAt(m_AvarageDDRO_list.GetHeadPosition());
    }
    m_nDownDatarateOverhead = 0;
    m_nDownDataRateMSOverhead = 0;
    for (POSITION pos = m_AvarageDDRO_list.GetHeadPosition() ; pos != 0 ; m_AvarageDDRO_list.GetNext(pos))
    m_nDownDatarateOverhead += m_AvarageDDRO_list.GetAt(pos);
    if (m_AvarageDDRO_list.GetCount() > 10)
    {
        m_nDownDatarateOverhead = 10 *m_nDownDatarateOverhead/m_AvarageDDRO_list.GetCount();
    }
    else
    {
        m_nDownDatarateOverhead = 0;
    }
    return;
}

void CDownloadQueue::Init()
{
        int count0 = 0;;
    do
    {
        wxString searchPath(theApp.dynprefs->Get<wxString>("temp-directory"));
        int count = 0;
        switch (count0)
        {
        case 0:
            searchPath += "/*.part.met";
            break;
        case 1:
            searchPath += "/*.pnew.met";
            break;
        }
        printf("Loading temp files from %s.\n", searchPath.GetData());
        wxString fileName =::wxFindFirstFile(searchPath, wxFILE);
        while (!fileName.IsEmpty())
        {
            wxFileName myFileName(fileName);
            printf("Loading %s... ", myFileName.GetFullName() .GetData());
            CPartFile *toadd = new CPartFile();
            if (toadd->LoadPartFile(const_cast<char *>(theApp.dynprefs->Get<wxString>("temp-directory").c_str()), (char *) myFileName.GetFullName() .GetData()))
            {
                count++;
                printf("Done.\n");
                filelist.AddTail(toadd);
                if (toadd->GetStatus(true) == PS_READY)
                {
                    sharedfilelist->SafeAddKFile(toadd);
                }
                theApp.xmuledlg->transferwnd->downloadlistctrl->AddFile(toadd);
            }
            else
            {
                delete toadd;
            }
            fileName =::wxFindNextFile();
        }
        if (count == 0)
        {
            theApp.xmuledlg->AddLogLine(false, GetResString(IDS_NOPARTSFOUND));
        }
        else
        {
            theApp.xmuledlg->AddLogLine(false, GetResString(IDS_FOUNDPARTS), count);
            SortByPriority();
        }
        count0++;
    }
    while (count0 < 2);
}

CDownloadQueue::~CDownloadQueue()
{
    //	if (!theApp.xmuledlg->m_app_state== APP_STATE_SHUTINGDOWN )
    //		SavePartFiles(true);	// changed by InterCeptor
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        //filelist.GetAt(pos)->m_hpartfile.Flush() ; // Flush before close app [Tarod]
        //filelist.GetAt(pos)->SavePartFile();
        delete filelist.GetAt(pos);
    }
}

// [InterCeptor]
    void CDownloadQueue::SavePartFiles(bool del /*= false*/

)
{
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        filelist.GetAt(pos)->m_hpartfile.Flush();
        filelist.GetAt(pos)->SavePartFile();
        if (del)
        delete filelist.GetAt(pos);
    }
}

void CDownloadQueue::AddSearchToDownload(CSearchFile *toadd)
{
    if (IsFileExisting(toadd->GetFileHash()))
    return;
    CPartFile *newfile = new CPartFile(toadd);
    if (!newfile) return;
    if (newfile->GetStatus() == PS_ERROR)
    {
        delete newfile;
        return;
    }
    AddDownload(newfile);
}

void CDownloadQueue::AddSearchToDownload(wxString link)
{
    // Bodo: Why here not, but in
    // Bodo: void CDownloadQueue::AddSearchToDownload(CSearchFile* toadd)?
    //	if (IsFileExisting("link->GetFileHash()"))
    //		return;
    CPartFile *newfile = new CPartFile(link);
    if (!newfile) return;
    if (newfile->GetStatus() == PS_ERROR)
    {
        delete newfile;
        return;
    }
    AddDownload(newfile);
}

void CDownloadQueue::StartNextFile()
{
    if (theApp.dynprefs->Get<bool>("start-next-file") == false)
    {
        return;
    }

    CPartFile* pfile = NULL;

    for (POSITION pos = filelist.GetHeadPosition(); pos != 0; filelist.GetNext(pos))
    {
        CPartFile* cur_file = filelist.GetAt(pos);

        if (cur_file->GetStatus() == PS_PAUSED)
        {
            if (!pfile)
            {
                pfile = cur_file;

                if (pfile->GetDownPriority() == PR_HIGH)
                {
                    break;
                }
            }
            else
            {
                if (cur_file->GetDownPriority() > pfile->GetDownPriority())
                {
                    pfile = cur_file;

                    if (pfile->GetDownPriority() == PR_HIGH)
                    {
                        break;
                    }
                }
            }
        }
    }

    if (pfile)
    {
        pfile->ResumeFile();
    }
}

void CDownloadQueue::AddFileLinkToDownload(CED2KFileLink *pLink)
{
    CPartFile *newfile = new CPartFile(pLink);
    if (!newfile) return;
    if (newfile->GetStatus() == PS_ERROR)
    {
        delete newfile;
        newfile = NULL;
        // Bodo: no return?
    }
    else
    {
        AddDownload(newfile);
    }
    if (pLink->HasValidSources())
    {
        if (newfile)
        {
            // Bodo: "no return?" seems to be ok...
            newfile->AddClientSources(pLink->SourcesList, 1);
        }
        else
        {
            CPartFile *partfile = GetFileByID((unsigned char *) pLink->GetHashKey());
            if (partfile) partfile->AddClientSources(pLink->SourcesList, 1);
        }
    }
}

void CDownloadQueue::AddDownload(CPartFile *newfile)
{
    // Barry - Add in paused mode if required
    if (theApp.dynprefs->Get<bool>("add-paused") == true)
    {
        newfile->PauseFile();
    }
    filelist.AddTail(newfile);
    SortByPriority();
    theApp.xmuledlg->transferwnd->downloadlistctrl->AddFile(newfile);
    theApp.xmuledlg->AddLogLine(true, GetResString(IDS_NEWDOWNLOAD), newfile->GetFileName());
    wxString msgTemp;
    msgTemp = GetResString(IDS_NEWDOWNLOAD) + wxT("\n") + newfile->GetFileName();
    theApp.xmuledlg->ShowNotifier(msgTemp, TBN_DLOAD);
}

bool CDownloadQueue::IsFileExisting(unsigned char *fileid)
{
    if (CKnownFile *file = sharedfilelist->GetFileByID((unsigned char *) fileid))
    {
        if (file->IsPartFile())
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADING), file->GetFileName());
        else
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADED), file->GetFileName());
        return true;
    }
    else if(file = this->GetFileByID((unsigned char *) fileid))
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_ALREADY_DOWNLOADING), file->GetFileName());
        return true;
    }
    return false;
}

void CDownloadQueue::Process()
{
    uint32_t downspeed = 0;
    if ((theApp.dynprefs->Get<long>("limits-download") != UNLIMITED) && (datarate > 1500))
    {
        //(uint16_t)((float)((float)(app_prefs->GetMaxDownload()*1024)/(datarate+1)) * 100);:
        downspeed = (theApp.dynprefs->Get<long>("limits-download") * 1024 * 100) / (datarate + 1);
        if (downspeed < 50)
        downspeed = 50;
        else if(downspeed > 200)
        downspeed = 200;
        //if (app_prefs->GetMaxDownload()*1024 < datarate)
        //	downspeed = 0xFFF;
    }
    datarate = 0;
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        if ((cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY) && (!cur_file->IsAutoDownPriority() &&cur_file->GetDownPriority() == PR_HIGH))
        {
            datarate += cur_file->Process(downspeed);
        }
    }
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        if ((cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY) && (!cur_file->IsAutoDownPriority() &&cur_file->GetDownPriority() == PR_NORMAL))
        {
            datarate += cur_file->Process(downspeed);
        }
    }
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        if ((cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY) && (cur_file->GetDownPriority() == PR_LOW || cur_file->IsAutoDownPriority()))
        {
            datarate += cur_file->Process(downspeed);
        }
    }
    udcounter++;
    if (udcounter == 5)
    {
        if ((!lastudpstattime) || (::GetTickCount() - lastudpstattime) > UDPSERVERSTATTIME)
        {
            lastudpstattime =::GetTickCount();
            theApp.serverlist->ServerStats();
        }
    }
    if (udcounter == 10)
    {
        udcounter = 0;
        if ((!lastudpsearchtime) || (::GetTickCount() - lastudpsearchtime) > UDPSERVERREASKTIME)
        SendNextUDPPacket();
    }
    // Check for new links once per second.
    if ((::GetTickCount() - m_nLastED2KLinkCheck) >= 1000)
    {
        wxString filename = wxString(getenv("HOME"), *wxConvCurrent) + wxT("/.xMule/ED2KLinks");
        if (wxFile::Exists(filename))
        {
            AddLinksFromFile();
        }
        m_nLastED2KLinkCheck =::GetTickCount();
    }
}

CPartFile *CDownloadQueue::GetFileByID(unsigned char *filehash)
{
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        if (!memcmp(filehash, filelist.GetAt(pos)->GetFileHash(), 16))
        return filelist.GetAt(pos);
    }
    return 0;
}

CPartFile *CDownloadQueue::GetFileByIndex(int index)
{
    //      if (index>=filelist.GetCount()) return 0;
    int count = 0;
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        if (count == index) return filelist.GetAt(pos);
        count++;
    }
    return 0;
}

bool CDownloadQueue::IsPartFile(void *totest)
{
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    if (totest == filelist.GetAt(pos))
    return true;
    return false;
}

void CDownloadQueue::CheckAndAddSource(CPartFile *sender, CUpDownClient *source)
{
    // if we block loopbacks at this point it should prevent us from connecting to ourself
    // Bodo: If one has running two clients running on the same system,
    // Bodo: does this prevent us from conneting to the other client?
    if (!source->HasLowID() && (source->GetUserID() &0xFF) == 0x7F)
    {
        delete source;
        return;
    }
 /*
 if (IsBlacklisted(source)) {
 delete source;
 return;
 }
    */
    // uses this only for temp. clients
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        for (POSITION pos2 = cur_file->srclist.GetHeadPosition() ; pos2 != 0 ; cur_file->srclist.GetNext(pos2))
        {
            if (cur_file->srclist.GetAt(pos2)->Compare(source))
            {
                if (cur_file == sender)
                {
                    // this file has already this source
                    delete source;
                    return;
                }
                // set request for this source
                if (cur_file->srclist.GetAt(pos2)->AddRequestForAnotherFile(sender))
                {
                    // add it to uploadlistctrl
                    theApp.xmuledlg->transferwnd->downloadlistctrl->AddSource(sender, cur_file->srclist.GetAt(pos2), true);
                    delete source;
                    return;
                }
                else
                {
                    delete source;
                    return;
                }
            }
        }
    }
    //our new source is real new but maybe it is already uploading to us?
    //if yes the known client will be attached to the var "source"
    //and the old sourceclient will be deleted
    if (theApp.clientlist->AttachToAlreadyKnown( &source, 0))
    source->SetDownloadFile(sender);
    else
    theApp.clientlist->AddClient(source, true);
    sender->srclist.AddTail(source);
    theApp.xmuledlg->transferwnd->downloadlistctrl->AddSource(sender, source, false);
    //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    UpdateDisplayedInfo();
}

void CDownloadQueue::CheckAndAddKnownSource(CPartFile *sender, CUpDownClient *source)
{
    if (!source->HasLowID() && (source->GetUserID() &0xFF) == 0x7F)
    return;
    // use this for client which are already know (downloading for example)
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);

        if (cur_file->srclist.Find(source))
        {
            if (cur_file == sender)
            return;
            if (source->AddRequestForAnotherFile(sender))
            theApp.xmuledlg->transferwnd->downloadlistctrl->AddSource(sender, source, true);
            return;
        }
    }
    source->SetDownloadFile(sender);
    sender->srclist.AddTail(source);
    theApp.xmuledlg->transferwnd->downloadlistctrl->AddSource(sender, source, false);
    UpdateDisplayedInfo();
}

bool CDownloadQueue::RemoveSource(CUpDownClient *toremove, bool updatewindow)
{
    bool removed = false;
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);

        for (POSITION pos2 = cur_file->srclist.GetHeadPosition() ; pos2 != 0 ; cur_file->srclist.GetNext(pos2))
        {
            if (toremove == cur_file->srclist.GetAt(pos2))
            {
                cur_file->srclist.RemoveAt(pos2);
                cur_file->NewSrcPartsInfo();
                removed = true;
    /* Razor 1a - Modif by MikaelB */
                if (!toremove->m_OtherRequests_list.IsEmpty())
                {
                    POSITION pos3, pos4;
                    for (pos3 = toremove->m_OtherRequests_list.GetHeadPosition() ; (pos4 = pos3) != NULL ;)
                    {
                        toremove->m_OtherRequests_list.GetNext(pos3);
                        POSITION pos5 = toremove->m_OtherRequests_list.GetAt(pos4)->A4AFSourcesList.Find(toremove);
                        if (pos5)
                        {
                            toremove->m_OtherRequests_list.GetAt(pos4)->A4AFSourcesList.RemoveAt(pos5);
                            theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(toremove, toremove->m_OtherRequests_list.GetAt(pos4));
                            toremove->m_OtherRequests_list.RemoveAt(pos4);
                        }
                    }
                }
                cur_file->RemoveDownloadingSource(toremove);
                if (updatewindow)
                {
                    toremove->SetDownloadState(DS_NONE);
                    theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(toremove, cur_file);
                }
                toremove->SetDownloadFile(NULL);
    /* End modif */
                break;
            }
        }
        cur_file->UpdateAvailablePartsCount();
    }
    if (updatewindow)
    {
        toremove->SetDownloadState(DS_NONE);
        theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(toremove, 0);
    }
    toremove->SetDownloadFile(NULL);
    return removed;
}

void CDownloadQueue::RemoveFile(CPartFile *toremove)
{
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        if (toremove == filelist.GetAt(pos))
        {
            filelist.RemoveAt(pos);
            return;
        }
    }
}

void CDownloadQueue::DeleteAll()
{
    POSITION pos;
    for (pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        cur_file->srclist.RemoveAll();
        // Barry - Should also remove all requested blocks
        // Don't worry about deleting the blocks, that gets handled
        // when CUpDownClient is deleted in CClientList::DeleteAll()
        cur_file->RemoveAllRequestedBlocks();
    }
}

bool CDownloadQueue::SendGlobGetSourcesUDPPacket(CSafeMemFile &data)
{
    //from emule30e
    bool bSentPacket = false;
    if (cur_udpserver && (theApp.serverconnect->GetCurrentServer() == NULL ||
    cur_udpserver != theApp.serverlist->GetServerByAddress(theApp.serverconnect->GetCurrentServer()->GetAddress(),
    theApp.serverconnect->GetCurrentServer()->GetPort())))
    {
        int iFileIDs = data.GetLength() / 16;
        Packet *packet = new Packet( &data);
        packet->opcode = OP_GLOBGETSOURCES;
        theApp.serverconnect->SendUDPPacket(packet, cur_udpserver, true);
        m_cRequestsSentToServer += iFileIDs;
        bSentPacket = true;
    }
    return bSentPacket;
}

bool CDownloadQueue::SendNextUDPPacket()
{
    //from emule30e & amule
    if (filelist.IsEmpty() || !theApp.serverconnect->IsConnected())
    {
        return false;
    }
    if (!cur_udpserver)
    {
        if (! (cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver)))
        {
            StopUDPRequests();
        }
        m_cRequestsSentToServer = 0;
    }
    int iMaxFilesPerPacket = GetMaxFilesPerUDPServerPacket();
    bool bSentPacket = false;
    CSafeMemFile dataGlobGetSources(16);
    int iFiles = 0;
    while (iFiles < iMaxFilesPerPacket && !bSentPacket)
    {
        // get next file to search sources for
        CPartFile *nextfile = NULL;
        while (!bSentPacket && ! (nextfile && (nextfile->GetStatus() == PS_READY || nextfile->GetStatus() == PS_EMPTY)))
        {
            if (lastfile == NULL)
            {
                // we just started the global source searching
                // or have switched the server
                // get first file to search sources for
                nextfile = filelist.GetHead();
                lastfile = nextfile;
            }
            else
            {
                POSITION pos = filelist.Find(lastfile);
                if (pos == 0)
                {
                    // the last file is no longer in the DL-list
                    // (may have been finished or canceld)
                    // get first file to search sources for
                    nextfile = filelist.GetHead();
                    lastfile = nextfile;
                }
                else
                {
                    filelist.GetNext(pos);
                    if (pos == 0)
                    {
                        if (dataGlobGetSources.GetLength() > 0)
                        {
                            if (SendGlobGetSourcesUDPPacket(dataGlobGetSources))
                            {
                                bSentPacket = true;
                            }
                            dataGlobGetSources.SetLength(0);
                        }
                        cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver);
                        m_cRequestsSentToServer = 0;
                        if (cur_udpserver == NULL)
                        {
                            // finished asking all servers for all files
                            lastudpsearchtime =::GetTickCount();
                            lastfile = NULL;
                            m_iSearchedServers = 0;
                            return false;
                        }
                        m_iSearchedServers++;
                        if (bSentPacket)
                        {
                            lastfile = NULL;
                            break;
                        }
                        iMaxFilesPerPacket = GetMaxFilesPerUDPServerPacket();
                        nextfile = filelist.GetHead();
                        lastfile = nextfile;
                    }
                    else
                    {
                        nextfile = filelist.GetAt(pos);
                        lastfile = nextfile;
                    }
                }
            }
        }
        // Number of sources to get via UDP...60% of MaxSources.
        if (!bSentPacket && nextfile && nextfile->GetSourceCount() < (theApp.dynprefs->Get<long>("max-sources") * 0.60))
        {
            dataGlobGetSources.Write(nextfile->GetFileHash(), 16);
            iFiles++;
        }
    }
    if (!bSentPacket &&dataGlobGetSources.GetLength() > 0)
    {
        SendGlobGetSourcesUDPPacket(dataGlobGetSources);
    }
    if (m_cRequestsSentToServer >= MAX_REQUESTS_PER_SERVER)
    {
 /*
 if (filelist.GetCount() >= MAX_REQUESTS_PER_SERVER)
 {
 for (int i = 0 ; i != MAX_REQUESTS_PER_SERVER ; i++)
 {
 filelist.AddHead(filelist.RemoveTail());
 }
 }
    */
        cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver);
        m_cRequestsSentToServer = 0;
        if (cur_udpserver == NULL)
        {
            lastudpsearchtime =::GetTickCount();
            lastfile = NULL;
            // finished (processed all file & all servers):
            return false;
        }
        m_iSearchedServers++;
        lastfile = NULL;
    }
    return true;
}

void CDownloadQueue::StopUDPRequests()
{
    cur_udpserver = 0;
    lastudpsearchtime =::GetTickCount();
    lastfile = 0;
}

void CDownloadQueue::SortByPriority()
{
    POSITION pos1, pos2;
    uint16_t i = 0;
    for (pos1 = filelist.GetHeadPosition() ; (pos2 = pos1) != NULL ;)
    {
        filelist.GetNext(pos1);
        CPartFile *cur_file = filelist.GetAt(pos2);
        if (cur_file->GetDownPriority() == PR_HIGH)
        {
            filelist.AddHead(cur_file);
            filelist.RemoveAt(pos2);
        }
        else if(cur_file->GetDownPriority() == PR_LOW)
        {
            filelist.AddTail(cur_file);
            filelist.RemoveAt(pos2);
        }
        i++;
        if (i == filelist.GetCount())
        break;
    }
}

void CDownloadQueue::GetDownloadStats(int results[])
{
    results[0] = 0;
    results[1] = 0;
    for (POSITION pos = theApp.downloadqueue->filelist.GetHeadPosition() ; pos != 0 ; theApp.downloadqueue->filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        results[0] += cur_file->GetSourceCount();
        results[1] += cur_file->GetTransferingSrcCount();
    }
}

CUpDownClient *CDownloadQueue::GetDownloadClientByIP(uint32_t dwIP)
{
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);

        for (POSITION pos2 = cur_file->srclist.GetHeadPosition() ; pos2 != 0 ; cur_file->srclist.GetNext(pos2))
        {
            if (dwIP == cur_file->srclist.GetAt(pos2)->GetIP())
            {
                return cur_file->srclist.GetAt(pos2);
            }
        }
    }
    return NULL;
}

void CDownloadQueue::AddLinksFromFile()
{
    wxString filename;
    wxString link;
    wxTextFile linksfile(wxString::Format("%s/.xMule/ED2KLinks", getenv("HOME")));
    if (linksfile.Open())
    {
        link = linksfile.GetFirstLine();
        // GetLineCount returns the actual number of lines in file, but line numbering
        // starts from zero. Thus we must loop until i = GetLineCount()-1.
        for (unsigned int i = 0 ; i < linksfile.GetLineCount() ; i++)
        {
            // Need the links to end with /, otherwise CreateLinkFromUrl crashes us.
            if (link.Right(1) != "/")
            {
                link += "/";
            }

            CSearchDlg::ExecuteLinkED2K(link);
            // We must double-check here where are we, because GetNextLine moves reading head
            // one line below, and we must make sure that line exists. Thus check if we are
            // at least one line away from end before trying to read next line.
            if (i < linksfile.GetLineCount() - 1)
            {
                link = linksfile.GetNextLine();
            }
        }
    }
    else
    {
        printf("Failed to open ED2KLinks file.\n");
    }
    // Save and Delete the file.
    linksfile.Write();
    wxRemoveFile(wxString::Format("%s/.xMule/ED2KLinks", getenv("HOME")));
}

 /* Razor 1a - Modif by MikaelB
    RemoveSourceFromPartFile function */

void CDownloadQueue::RemoveSourceFromPartFile(CPartFile *file, CUpDownClient *client, POSITION position)
{
    // Remove source at position in sources list of file
    file->srclist.RemoveAt(position);
    // Update infos of file
    file->NewSrcPartsInfo();
    file->UpdateAvailablePartsCount();
    // Set client download state to none
    client->SetDownloadState(DS_NONE);
    // Set client valid source to false
    client->SetValidSource(false);
    // Remove downloading source of client
    file->RemoveDownloadingSource(client);
    // Remove source of download list window
    theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(client, file);
    // Set requested file of client to 0
    client->SetDownloadFile(NULL);
    // If other requests list of client isn't empty...
    if (! client->m_OtherRequests_list.IsEmpty())
    {
        // Some positions
        POSITION position, temp_position;
        // For every position in other requests list of client...
        for (position = client->m_OtherRequests_list.GetHeadPosition() ; (temp_position = position) != NULL ;)
        {
            // Get next position in position
            client->m_OtherRequests_list.GetNext(position);
            // Get position of client in the A4AF sources list of file at current position in client's other requests list
            POSITION position2 = client->m_OtherRequests_list.GetAt(temp_position)->A4AFSourcesList.Find(client);
            // If such a position have been found
            if (position2)
            {
                // Remove this position of the A4AF sources list of file at current position in client's other requests list
                client->m_OtherRequests_list.GetAt(temp_position)->A4AFSourcesList.RemoveAt(position2);
                // Remove current position from the download list window
                theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(client, client->m_OtherRequests_list.GetAt(temp_position));
                // Remove current position from client's other requests list
                client->m_OtherRequests_list.RemoveAt(temp_position);
            }
        }
    }
    // If client's upload status id none
    if (client->GetUploadState() == US_NONE)
    {
        client->Disconnected();
    }
}

    /* End modif */

 /* Razor 1a - Modif by MikaelB
    DisableAnyA4AFAuto function */

void CDownloadQueue::DisableAnyA4AFAuto(void)
{
    // For all position in filelist...
    for (POSITION position = filelist.GetHeadPosition() ; position != NULL ; filelist.GetNext(position))
    {
        // Get file at position in filelist
        CPartFile *file = (CPartFile *) filelist.GetAt(position);
        // If file has been found...
        if (file != NULL)
        // Disable A4AF Auto
        file->SetA4AFAuto(false);
    }
}

    /* End modif */

void CDownloadQueue::ResetCatParts(int cat)
{
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        if (cur_file->GetCategory() == cat)
        {
            cur_file->SetCategory(0);
        }
        else if(cur_file->GetCategory() > cat)
        {
            cur_file->SetCategory(cur_file->GetCategory() - 1);
        }
    }
}

void CDownloadQueue::SetCatPrio(int cat, uint8_t newprio)
{
    for (POSITION pos = filelist.GetHeadPosition() ; pos != 0 ; filelist.GetNext(pos))
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        if (cat == 0 || cur_file->GetCategory() == cat)
        {
            if (newprio == PR_AUTO)
            {
                cur_file->SetAutoDownPriority(true);
                cur_file->SetDownPriority(PR_HIGH);
            }
            else
            {
                cur_file->SetAutoDownPriority(false);
                cur_file->SetDownPriority(newprio);
            }
        }
    }
}

void CDownloadQueue::SetCatStatus(int cat, int newstatus)
{
    bool reset = false;
    POSITION pos = filelist.GetHeadPosition();
    while (pos != 0)
    {
        CPartFile *cur_file = filelist.GetAt(pos);
        if (!cur_file)
        {
            continue;
        }
        if (cat == cur_file->GetCategory() || cat == 0)
        {
            switch (newstatus)
            {
            case MP_CANCEL:
                cur_file->DeleteFile();
                delete cur_file;
                reset = true;
                break;
            case MP_PAUSE:
                cur_file->PauseFile();
                break;
            case MP_STOP:
                cur_file->StopFile();
                break;
            case MP_RESUME:
                if (cur_file->GetStatus() == PS_PAUSED)
                {
                    cur_file->ResumeFile();
                }
                break;
            }
        }
        filelist.GetNext(pos);
        if (reset)
        {
            reset = false;
            pos = filelist.GetHeadPosition();
        }
    }
}

