//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 "downloadqueue.h"
#include "updownclient.h"
#include "partfile.h"
#include "ed2klink.h"

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


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_datarateMS=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;
	sumavgDDRO = 0;
    //averageDatarate = 0;
    m_random_update_wait = (uint32)(rand()/(RAND_MAX/1000));
}

void CDownloadQueue::UpdateDisplayedInfo(boolean force) {
    DWORD curTick = ::GetTickCount();

    if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE+m_random_update_wait) {
        theApp.emuledlg->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);
	}
}

void CDownloadQueue::CompDownDatarateOverhead(){
	// Patch By BadWolf - Accurate datarate Calculation
	TransferredData newitem = {m_nDownDataRateMSOverhead,::GetTickCount()};
	m_AvarageDDRO_list.AddTail(newitem);
	sumavgDDRO += m_nDownDataRateMSOverhead;

	while ((float)(m_AvarageDDRO_list.GetTail().timestamp - m_AvarageDDRO_list.GetHead().timestamp) > MAXAVERAGETIME) 
		sumavgDDRO -= m_AvarageDDRO_list.RemoveHead().datalen;

	m_nDownDataRateMSOverhead = 0;

	if(m_AvarageDDRO_list.GetCount() > 10)
		m_nDownDatarateOverhead = 1000 * sumavgDDRO /(m_AvarageDDRO_list.GetTail().timestamp - m_AvarageDDRO_list.GetHead().timestamp);
	else
		m_nDownDatarateOverhead = 0;

	// END Patch By BadWolf
}

void CDownloadQueue::Init(){
	// find all part files, read & hash them if needed and store into a list
	CFileFind ff;
	int count = 0;

	CString searchPath(app_prefs->GetTempDir());
	searchPath += "\\*.part.met";

	//check all part.met files
	bool end = !ff.FindFile(searchPath, 0);
	while (!end){
		end = !ff.FindNextFile();
		if (ff.IsDirectory())
			continue;
		CPartFile* toadd = new CPartFile();
		if (toadd->LoadPartFile(app_prefs->GetTempDir(),ff.GetFileName().GetBuffer())){
			count++;
			filelist.AddTail(toadd);			// to downloadqueue
			if (toadd->GetStatus(true) == PS_READY)
				sharedfilelist->SafeAddKFile(toadd); // part files are always shared files
			theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(toadd);// show in downloadwindow
		}
		else
			delete toadd;
	}
	ff.Close();

	//try recovering any part.met files
	searchPath += ".backup";
	end = !ff.FindFile(searchPath, 0);
	while (!end){
		end = !ff.FindNextFile();
		if (ff.IsDirectory())
			continue;
		CPartFile* toadd = new CPartFile();
		if (toadd->LoadPartFile(app_prefs->GetTempDir(),ff.GetFileName().GetBuffer())){
			toadd->SavePartFile(); // resave backup
			count++;
			filelist.AddTail(toadd);			// to downloadqueue
			if (toadd->GetStatus(true) == PS_READY)
				sharedfilelist->SafeAddKFile(toadd); // part files are always shared files
			theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(toadd);// show in downloadwindow

			theApp.emuledlg->AddLogLine(false, GetResString(IDS_RECOVERED_PARTMET), toadd->GetFileName());
		}
		else {
			delete toadd;
		}
	}
	ff.Close();

	if(count == 0) {
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_NOPARTSFOUND));
	} else {
		theApp.emuledlg->AddLogLine(false,GetResString(IDS_FOUNDPARTS),count);
		SortByPriority();
	}
}

CDownloadQueue::~CDownloadQueue(){
	for (POSITION pos =filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos))
		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,uint8 cat){
	if (IsFileExisting(toadd->GetFileHash()))
		return;
	CPartFile* newfile = new CPartFile(toadd);
	if (newfile->GetStatus() == PS_ERROR){
		delete newfile;
		return;
	}
	newfile->SetCategory(cat);
	AddDownload(newfile);

	// If the search result is from OP_GLOBSEARCHRES there may also be a source
	if (toadd->GetClientID() && toadd->GetClientPort()){
		CSafeMemFile sources(1+4+2);
		try{
		    uint8 uSources = 1;
		    sources.Write(&uSources, 1);
		    uint32 uIP = toadd->GetClientID();
		    sources.Write(&uIP, 4);
		    uint16 uPort = toadd->GetClientPort();
		    sources.Write(&uPort, 2);
		    sources.SeekToBegin();
		    newfile->AddSources(&sources, toadd->GetClientServerIP(), toadd->GetClientServerPort());
		}
		catch(CFileException* error){
			ASSERT(0);
			error->Delete();
		}
	}

	// Add more sources which were found via global UDP search
	const CSimpleArray<CSearchFile::SClient>& aClients = toadd->GetClients();
	for (int i = 0; i < aClients.GetSize(); i++){
		CSafeMemFile sources(1+4+2);
		try{
		    uint8 uSources = 1;
		    sources.Write(&uSources, 1);
		    sources.Write(&aClients[i].nIP, 4);
		    sources.Write(&aClients[i].nPort, 2);
		    sources.SeekToBegin();
		    newfile->AddSources(&sources,aClients[i].nServerIP,aClients[i].nServerPort);
	    }
		catch(CFileException* error){
			ASSERT(0);
			error->Delete();
			break;
		}
	}
}

void CDownloadQueue::AddSearchToDownload(CString link,uint8 cat){
	CPartFile* newfile = new CPartFile(link);
	if (newfile->GetStatus() == PS_ERROR){
		delete newfile;
		return;
	}
	newfile->SetCategory(cat);
	AddDownload(newfile);
}

void CDownloadQueue::StartNextFile(int cat){

	CPartFile*  pfile = NULL;
	CPartFile* cur_file ;
	POSITION pos;
	
	bool samecat=theApp.glob_prefs->GetResumeSameCat() || cat==-1;
	bool found=false;

	if (samecat) {
		for (pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos))
		{
			cur_file = filelist.GetAt(pos);
			if (cur_file->GetCategory()==cat || 
				(cat==0 && theApp.glob_prefs->GetAllcatType()==0 && cur_file->GetCategory()>0)
				) {found=true;break;}
		}
	}

	for (pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
		cur_file = filelist.GetAt(pos);
		if (cur_file->GetStatus() == PS_PAUSED &&
			(!found || (found && 
			(cur_file->GetCategory()==cat || (cat==0 && theApp.glob_prefs->GetAllcatType()==0 && cur_file->GetCategory()>0) )
			))
			)
		{
			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,uint8 cat)
{
	CPartFile* newfile = new CPartFile(pLink);
	if (newfile->GetStatus() == PS_ERROR){
		delete newfile;
		newfile=NULL;
	}
	else {
		newfile->SetCategory(cat);
		AddDownload(newfile);
	}

	if(pLink->HasValidSources()) {
		if (newfile) newfile->AddClientSources(pLink->SourcesList,1);
		else{
				CPartFile* partfile = GetFileByID((uchar*)pLink->GetHashKey());
				if (partfile) partfile->AddClientSources(pLink->SourcesList,1);
		}

	}
}

void CDownloadQueue::AddDownload(CPartFile* newfile) {
	// Barry - Add in paused mode if required
	if (theApp.glob_prefs->AddNewFilesPaused())
		newfile->PauseFile();

	filelist.AddTail(newfile);
	SortByPriority();
	theApp.emuledlg->transferwnd.downloadlistctrl.AddFile(newfile);
	theApp.emuledlg->AddLogLine(true,GetResString(IDS_NEWDOWNLOAD),newfile->GetFileName());
	CString msgTemp;
	msgTemp.Format(GetResString(IDS_NEWDOWNLOAD)+"\n",newfile->GetFileName());
	theApp.emuledlg->ShowNotifier(msgTemp, TBN_DLOAD);
}

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


void CDownloadQueue::Process(){
	uint32 downspeed = 0;

    // Enforce a session ul:dl ratio of 1:3 no matter what upload speed.
    // The old ratio code is only used if the upload-queue is empty.
    // If the queue is empty, the client may not have gotten enough
    // requests, to be able to upload full configured speed. So we fallback
    // to old ratio-variant.
    // This new ratio check really needs to be here with the new more
    // powerful friends slot, to prevent the user from giving all his
    // bandwidth to just friends, and at the same time leaching from
    // all other clients.
    uint32 maxDownload = app_prefs->GetMaxDownload();

    if(theApp.uploadqueue->GetUploadQueueLength() <= 0) {
        // copied from prefs load
        if( app_prefs->GetMaxUpload() != 0 ){
            if(app_prefs->GetMaxUpload() < 4 && (app_prefs->GetMaxUpload()*3 < app_prefs->GetMaxDownload())) {
				maxDownload = app_prefs->GetMaxUpload()*3;
            }
    
            if(app_prefs->GetMaxUpload() < 10 && (app_prefs->GetMaxUpload()*4 < app_prefs->GetMaxDownload())) {
				maxDownload = app_prefs->GetMaxUpload()*4;
            }
		}
    }

	if (maxDownload != UNLIMITED && datarate > 1500){
		downspeed = (maxDownload*1024*100)/(datarate+1); //(uint16)((float)((float)(app_prefs->GetMaxDownload()*1024)/(datarate+1)) * 100);
		if (downspeed < 50)
			downspeed = 50;
		else if (downspeed > 200)
			downspeed = 200;
		//if (app_prefs->GetMaxDownload()*1024 < datarate)
		//	downspeed = 0xFFF;
	}

    uint32 friendDownspeed = downspeed;

    if(theApp.uploadqueue->GetUploadQueueLength() > 0) {
        // has this client downloaded more than it has uploaded this session? (friends excluded)
        // then limit its download speed from all clients but friends
        // limit will be removed as soon as upload has catched up to download
        if(theApp.stat_sessionReceivedBytes/3 > theApp.stat_sessionSentBytes &&
           datarate > 1500) {
            
            // calc allowed dl speed for rest of network (those clients that don't have
            // friend slots. Since you don't upload as much to them, you won't be able to download
            // as much from them. This will only be lower than friends speed if you are currently
            // uploading to a friend slot, otherwise they are the same.

            uint32 secondsNeededToEvenOut = (theApp.stat_sessionReceivedBytes/3-theApp.stat_sessionSentBytes)/(theApp.uploadqueue->GetToNetworkDatarate()+1);
            uint32 tempDownspeed = max(min(3*100/max(secondsNeededToEvenOut, 1), 200), 30);

            if(downspeed == 0 || tempDownspeed < downspeed) {
                downspeed = tempDownspeed;
                //theApp.emuledlg->AddLogLine(true, "Limiting downspeed");
            }
        }

        // has this client downloaded more than it has uploaded this session? (friends included)
        // then limit its download speed from all friends
        // limit will be removed as soon as upload has catched up to download
        if(theApp.stat_sessionReceivedBytes/3 > (theApp.stat_sessionSentBytes + theApp.stat_sessionSentBytesToFriend) &&
           datarate > 1500) {

            float secondsNeededToEvenOut = (theApp.stat_sessionReceivedBytes/3-(theApp.stat_sessionSentBytes + theApp.stat_sessionSentBytesToFriend))/(theApp.uploadqueue->GetDatarate()+1);
            uint32 tempDownspeed = max(min(3*100/max(secondsNeededToEvenOut, 1), 200), 30);

            if(friendDownspeed == 0 || tempDownspeed < friendDownspeed) {
                friendDownspeed = tempDownspeed;
            }
        }
    }

	uint32 datarateX=0;
	udcounter++;
    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_HIGH){
			datarateX += cur_file->Process(downspeed, udcounter, friendDownspeed);
		}
	}
	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_NORMAL){
			datarateX += cur_file->Process(downspeed, udcounter, friendDownspeed);
		}
	}
	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->GetDownPriority() == PR_AUTO )){
			datarateX += cur_file->Process(downspeed, udcounter, friendDownspeed);
		}
	}
    datarate = datarateX;

	if (udcounter == 5){
		if((!lastudpstattime) || (::GetTickCount() - lastudpstattime) > UDPSERVERSTATTIME){
			lastudpstattime = ::GetTickCount();
			theApp.serverlist->ServerStats();
		}
	}

	if (udcounter == 10){
		udcounter = 0;
		if ((!lastudpsearchtime) || (::GetTickCount() - lastudpsearchtime) > UDPSERVERREASKTIME)
			SendNextUDPPacket();
	}
}


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

CPartFile*	CDownloadQueue::GetFileByID(uchar* filehash){
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;filelist.GetNext(pos)){
		if (!md4cmp(filehash,filelist.GetAt(pos)->GetFileHash()))
			return filelist.GetAt(pos);
	}
	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
	if( (!source->HasLowID() && (source->GetUserID() & 0xFF) == 0x7F) || sender->IsStopped()) {
		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 (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
		for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; cur_file->srclists[sl].GetNext(pos2)){
			if (cur_file->srclists[sl].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->srclists[sl].GetAt(pos2)->AddRequestForAnotherFile(sender)){
					// add it to uploadlistctrl
					theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,cur_file->srclists[sl].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->reqfile = sender;
	else
		theApp.clientlist->AddClient(source,true);
	
	if (source->GetFileRate()>0 || source->GetFileComment().GetLength()>0) sender->UpdateFileRatingCommentAvail();
	sender->srclists[source->sourcesslot].AddTail(source);
	theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,source,false);
	UpdateDisplayedInfo();
}

void CDownloadQueue::CheckAndAddKnownSource(CPartFile* sender,CUpDownClient* source){
	if((!source->HasLowID() && (source->GetUserID() & 0xFF) == 0x7F) || sender->IsStopped() )
		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);
		for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
		if (cur_file->srclists[sl].Find(source)){
			if (cur_file == sender)
				return;
			if (source->AddRequestForAnotherFile(sender))
				theApp.emuledlg->transferwnd.downloadlistctrl.AddSource(sender,source,true);
			return;
		}
	}
	source->reqfile = sender;
	if (source->GetFileRate()>0||source->GetFileComment().GetLength()>0) sender->UpdateFileRatingCommentAvail();
	sender->srclists[source->sourcesslot].AddTail(source);
	theApp.emuledlg->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 (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
		for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; cur_file->srclists[sl].GetNext(pos2)){
			if (toremove == cur_file->srclists[sl].GetAt(pos2)){
				cur_file->srclists[sl].RemoveAt(pos2);
				cur_file->NewSrcPartsInfo();
				removed = true;
				cur_file->RemoveDownloadingSource(toremove);
				break;
			}
		}
		cur_file->UpdateAvailablePartsCount();
	}

	if (toremove->GetFileComment().GetLength()>0 || toremove->GetFileRate()>0 )
		toremove->reqfile->UpdateFileRatingCommentAvail();

	if (updatewindow){
		toremove->SetDownloadState(DS_NONE);
		theApp.emuledlg->transferwnd.downloadlistctrl.RemoveSource(toremove,0);
	}
	toremove->reqfile = 0;
	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);
		for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty())
			cur_file->srclists[sl].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::SendNextUDPPacket(){
	if (filelist.IsEmpty() || !theApp.serverconnect->IsConnected())
		return false;
	if (!cur_udpserver)
		if (!(cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver))){
			TRACE("ERROR:SendNextUDPPacket() no server found\n");
			StopUDPRequests();
		};

	// get nextfile
	CPartFile* nextfile = 0;
	while (!(nextfile && (nextfile->GetStatus() == PS_READY ||nextfile->GetStatus() == PS_EMPTY))){
		if (lastfile == 0){
			nextfile = filelist.GetHead();
			lastfile = nextfile;
		}
		else{
			POSITION pos = filelist.Find(lastfile);
			if (!pos){
				TRACE("Error: CDownloadQueue::SendNextUDPPacket()\n");
				nextfile = filelist.GetHead();
				lastfile = nextfile;
			}
			else{
				filelist.GetNext(pos);
				if (pos == 0){
					cur_udpserver = theApp.serverlist->GetNextServer(cur_udpserver);
					if (cur_udpserver == 0){
						//TRACE("finished");
						lastudpsearchtime = ::GetTickCount();
						lastfile = 0;
						return false; // finished (processed all file & all servers)
					}
					nextfile = filelist.GetHead();
					lastfile = nextfile;
				}
				else{
					nextfile = filelist.GetAt(pos);
					lastfile = nextfile;
				}
			}
		}
	}
	if( (theApp.glob_prefs->GetMaxSourcePerFileUDP()) < nextfile->GetSourceCount()) //<<--
		return true; 
	Packet packet(OP_GLOBGETSOURCES,16);
	md4cpy(packet.pBuffer,nextfile->GetFileHash());
	if (cur_udpserver != theApp.serverlist->GetServerByAddress(theApp.serverconnect->GetCurrentServer()->GetAddress(),theApp.serverconnect->GetCurrentServer()->GetPort())){
		theApp.uploadqueue->AddUpDataOverheadServer(packet.size);
		theApp.serverconnect->SendUDPPacket(&packet,cur_udpserver,false);
	}
	return true;
}

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

void CDownloadQueue::SortByPriority(){
   POSITION pos1, pos2;
   uint16 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;
   }
}

// -khaos--+++> Rewritten GetDownloadStats
void CDownloadQueue::GetDownloadStats(int results[], uint64 moreresults[]) {
	
	for (int i=0;i<16;i++)
		results[i] = 0;

	moreresults[0]=0; // Lost Due To Corruption
	moreresults[1]=0; // Gained Due To Compression

	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();
		// -khaos-/
		results[2]+=cur_file->GetOnQueueSrcCount();
		results[3]+=cur_file->GetQueueFullSrcCount();
		results[4]+=cur_file->GetNoNeededPartsSrcCount();
		results[5]+=cur_file->GetConnectedSrcCount();
		results[6]+=cur_file->GetRecHashsetSrcCount();
		results[7]+=cur_file->GetConnectingSrcCount();
		results[8]+=cur_file->GetConnectingViaServerSrcCount();
		results[9]+=cur_file->GetTooManyConnsSrcCount();
		results[10]+=cur_file->GetLow2LowSrcCount();
		results[11]+=cur_file->GetUnknownSrcCount();
		results[12]+=cur_file->GetErrorSrcCount();
		results[13]+=cur_file->GetBannedSrcCount();
		results[14]+=cur_file->TotalPacketsSavedDueToICH();
		results[15]+=cur_file->GetSrcA4AFCount();
		moreresults[0]+=cur_file->GetLostDueToCorruption();
		moreresults[1]+=cur_file->GetGainDueToCompression();
		// /-khaos-
	}
} // GetDownloadStats
// <-----khaos-

CUpDownClient* CDownloadQueue::GetDownloadClientByIP(uint32 dwIP){
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty()){
			for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0; ){
				CUpDownClient* cur_client = cur_file->srclists[sl].GetNext(pos2);
				if (dwIP == cur_client->GetIP()){
					return cur_client;
				}
			}
		}
	}
	return NULL;
}

CUpDownClient* CDownloadQueue::GetDownloadClientByIP_UDP(uint32 dwIP, uint16 nUDPPort){
	for (POSITION pos = filelist.GetHeadPosition();pos != 0;){
		CPartFile* cur_file = filelist.GetNext(pos);
		for (int sl=0;sl<SOURCESSLOTS;sl++) if (!cur_file->srclists[sl].IsEmpty()){
			for (POSITION pos2 = cur_file->srclists[sl].GetHeadPosition();pos2 != 0;){
				CUpDownClient* cur_client = cur_file->srclists[sl].GetNext(pos2);
				if (dwIP == cur_client->GetIP() && nUDPPort == cur_client->GetUDPPort()){
					return cur_client;
				}
			}
		}
	}
	return NULL;
}

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 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==-1 || (cat==-2 && cur_file->GetCategory()==0) ||
			(cat==0 && CheckShowItemInGivenCat(cur_file,cat))
			 || ( cat>0 && cat==cur_file->GetCategory() ) )
			switch (newstatus) {
				case MP_CANCEL:cur_file->DeleteFile();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();}
	}
}

void CDownloadQueue::MoveCat(uint8 from, uint8 to){
	if (from < to) --to;
	uint8 mycat;

	POSITION pos= filelist.GetHeadPosition();
	while (pos != 0) {
		CPartFile* cur_file = filelist.GetAt(pos);
		if (!cur_file) continue;
		mycat=cur_file->GetCategory();
		if ( mycat>=min(from,to) && mycat<=max(from,to)) {
			//if ((from<to && (mycat<from || mycat>to)) || (from>to && (mycat>from || mycat<to)) )  continue; //not affected

			if (cur_file->GetCategory()==from) cur_file->SetCategory(to); 
			else {
				if (from<to) cur_file->SetCategory(cur_file->GetCategory()-1);
					else cur_file->SetCategory(cur_file->GetCategory()+1);
			}
		}
		filelist.GetNext(pos);
	}
}
