//this file is part of eMule
//Copyright (C)2002-2008 Merkur ( strEmail.Format("%s@%s", "devteam", "emule-project.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 "emule.h"
#include <zlib/zlib.h>
#include "UpDownClient.h"
#include "UrlClient.h"
#include "Packets.h"
#include "UploadQueue.h"
#include "Statistics.h"
#include "ClientList.h"
#include "ClientUDPSocket.h"
#include "SharedFileList.h"
#include "KnownFileList.h"
#include "PartFile.h"
#include "ClientCredits.h"
#include "ListenSocket.h"
#include "PeerCacheSocket.h"
#include "Sockets.h"
#include "OtherFunctions.h"
#include "SafeFile.h"
#include "DownloadQueue.h"
#include "emuledlg.h"
#include "TransferWnd.h"
#include "Log.h"
#include "Collection.h"
#include "UploadDiskreaderThread.h"
//>>> CA
#include "ClientAnalyzer.h"
//<<< CA
#include <Mmsystem.h>

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


//	members of CUpDownClient
//	which are mainly used for uploading functions 

CBarShader CUpDownClient::s_UpStatusBar(16);

void CUpDownClient::DrawUpStatusBar(CDC* dc, RECT* rect, bool onlygreyrect, bool  bFlat) const
{
    COLORREF crNeither;
	COLORREF crNextSending;
	COLORREF crBoth;
	COLORREF crSent;
	COLORREF crBuffer;
	COLORREF crSending;

    if(GetSlotNumber() <= theApp.uploadqueue->GetActiveUploadsCount() ||
       (GetUploadState() != US_UPLOADING && GetUploadState() != US_CONNECTING) ) {
        crNeither = RGB(224, 224, 224);
	    crNextSending = RGB(255,208,0);
	    crBoth = bFlat ? RGB(0, 0, 0) : RGB(104, 104, 104);
	    crSent = RGB(0, 150, 0);
 		crBuffer = RGB(255, 100, 100);
		crSending = RGB(0, 224, 0);
    } else {
        // grayed out
        crNeither = RGB(248, 248, 248);
	    crNextSending = RGB(255,244,191);
	    crBoth = bFlat ? RGB(191, 191, 191) : RGB(191, 191, 191);
	    crSent = RGB(191, 229, 191);
 		crBuffer = RGB(255, 216, 216);
		crSending = RGB(191, 255, 191);
    }

	// wistily: UpStatusFix
	CKnownFile* currequpfile = theApp.sharedfiles->GetFileByID(requpfileid);
	EMFileSize filesize;
	if (currequpfile)
		filesize = currequpfile->GetFileSize();
	else
		filesize = (uint64)(PARTSIZE * (uint64)m_nUpPartCount);
	// wistily: UpStatusFix

    if(filesize > (uint64)0) {
	    s_UpStatusBar.SetFileSize(filesize); 
	    s_UpStatusBar.SetHeight(rect->bottom - rect->top); 
	    s_UpStatusBar.SetWidth(rect->right - rect->left); 
	    s_UpStatusBar.Fill(crNeither); 
	    if (!onlygreyrect && m_abyUpPartStatus) { 
		    for (UINT i = 0; i < m_nUpPartCount; i++)
			    if (m_abyUpPartStatus[i])
				    s_UpStatusBar.FillRange(PARTSIZE*(uint64)(i), PARTSIZE*(uint64)(i+1), crBoth);
	    }

        if(m_UploadDiskreaderThread != NULL) {
            m_UploadDiskreaderThread->DrawUpStatusBar(s_UpStatusBar, GetSessionPayloadUp(), crNextSending, crSent, crSending, crBuffer);
        }

   	    s_UpStatusBar.Draw(dc, rect->left, rect->top, bFlat);
    }
} 

/**
 * Display current uploading chunk
 */
void CUpDownClient::DrawUpStatusBarChunk(CDC* dc, RECT* rect, bool /*onlygreyrect*/, bool  bFlat) const
{
	COLORREF crNeither;
	COLORREF crNextSending;
	COLORREF crSent;
	COLORREF crBuffer;
	COLORREF crSending;
    if(GetSlotNumber() <= theApp.uploadqueue->GetActiveUploadsCount() ||
       (GetUploadState() != US_UPLOADING && GetUploadState() != US_CONNECTING) ) {
        crNeither = RGB(224, 224, 224);
	    crNextSending = RGB(255,208,0);
	    crSent = RGB(0, 150, 0);
		crBuffer = RGB(255, 100, 100);
//		crDot = RGB(255, 255, 255);
		crSending = RGB(0, 224, 0);
    } else {
        // grayed out
        crNeither = RGB(248, 248, 248);
	    crNextSending = RGB(255,244,191);
	    crSent = RGB(191, 229, 191);
		crBuffer = RGB(255, 216, 216);
//		crDot = RGB(255, 255, 255);
		crSending = RGB(191, 255, 191);
    }

	CKnownFile* currequpfile = theApp.sharedfiles->GetFileByID(requpfileid);
	EMFileSize filesize;
	if (currequpfile)
		filesize=currequpfile->GetFileSize();
	else
		filesize = (uint64)(PARTSIZE * (uint64)m_nUpPartCount);

	if(filesize <= (uint64)0)
		return;

    if(m_UploadDiskreaderThread != NULL) {
        m_UploadDiskreaderThread->DrawUpStatusBarChunk(s_UpStatusBar, dc, rect, filesize, bFlat, GetSessionPayloadUp(), crNextSending, crSent, crSending, crBuffer, crNeither);
    }
}

void CUpDownClient::SetUploadState(EUploadState eNewState)
{
	if (eNewState != m_nUploadState)
	{
		if (m_nUploadState == US_UPLOADING)
		{
            //AddDebugLogLine(DLP_DEFAULT, true,_T("SetUploadState to state other than US_UPLOADING. Client: %s"), DbgGetClientInfo());

            // Reset upload data rate computation
			m_nUpDatarate = 0;
			m_nSumForAvgUpDataRate = 0;
			m_AvarageUDR_list.RemoveAll();

            if(m_UploadDiskreaderThread) {
                //AddDebugLogLine(DLP_DEFAULT, true,_T("Deleting m_UploadDiskreaderThread. Client: %s"), DbgGetClientInfo());
                delete m_UploadDiskreaderThread;
                m_UploadDiskreaderThread = NULL;
            }
		}
        if (eNewState == US_UPLOADING) {
			m_fSentOutOfPartReqs = 0;

            m_UploadDiskreaderThread = new UploadDiskreaderThread(this, m_byDataCompVer);
        }

		// don't add any final cleanups for US_NONE here
		m_nUploadState = (_EUploadState)eNewState;
		theApp.emuledlg->transferwnd->clientlistctrl.RefreshClient(this);
	}
}

/**
 * Gets the queue score multiplier for this client, taking into consideration client's credits
 * and the requested file's priority.
 */
float CUpDownClient::GetCombinedFilePrioAndCredit() {
	if (credits == 0){
		ASSERT ( IsKindOf(RUNTIME_CLASS(CUrlClient)) );
		return 0.0F;
	}

//>>> CA
	if (pAntiLeechData)
		return (10.0f*credits->GetScoreRatio(GetIP()) * float(GetFilePrioAsNumber())) * pAntiLeechData->GetScore();
	else
//<<< CA
    return 10.0f * credits->GetScoreRatio(GetIP()) * (float)GetFilePrioAsNumber();
}

/**
 * Gets the file multiplier for the file this client has requested.
 */
int CUpDownClient::GetFilePrioAsNumber() const {
	CKnownFile* currequpfile = theApp.sharedfiles->GetFileByID(requpfileid);
	if(!currequpfile)
		return 0;
	
	// TODO coded by tecxx & herbert, one yet unsolved problem here:
	// sometimes a client asks for 2 files and there is no way to decide, which file the 
	// client finally gets. so it could happen that he is queued first because of a 
	// high prio file, but then asks for something completely different.
	int filepriority = 10; // standard
	switch(currequpfile->GetUpPriority()){
		case PR_VERYHIGH:
			filepriority = 18;
			break;
		case PR_HIGH: 
			filepriority = 9; 
			break; 
		case PR_LOW: 
			filepriority = 6; 
			break; 
		case PR_VERYLOW:
			filepriority = 2;
			break;
		case PR_NORMAL: 
			default: 
			filepriority = 7; 
		break; 
	} 

    return filepriority;
}

/**
 * Gets the current waiting score for this client, taking into consideration waiting
 * time, priority of requested file, and the client's credits.
 */
uint32 CUpDownClient::GetScore(bool sysvalue, bool isdownloading, bool onlybasevalue) const
{
	if (!m_pszUsername)
		return 0;

	if (credits == 0){
		ASSERT ( IsKindOf(RUNTIME_CLASS(CUrlClient)) );
		return 0;
	}
	CKnownFile* currequpfile = theApp.sharedfiles->GetFileByID(requpfileid);
	if(!currequpfile)
		return 0;
	
	// bad clients (see note in function)
	if (credits->GetCurrentIdentState(GetIP()) == IS_IDBADGUY)
		return 0;
	// friend slot
	if (IsFriend() && GetFriendSlot() && !HasLowID())
		return 0x0FFFFFFF;

	if (IsBanned() || m_bGPLEvildoer)
		return 0;

	if (sysvalue && HasLowID() && !(socket && socket->IsConnected())){
		return 0;
	}

    int filepriority = GetFilePrioAsNumber();

	// calculate score, based on waitingtime and other factors
	float fBaseValue;
	if (onlybasevalue)
		fBaseValue = 100;
	else if (!isdownloading)
		fBaseValue = (float)(::GetTickCount()-GetWaitStartTime())/1000;
	else{
		// we dont want one client to download forever
		// the first 15 min downloadtime counts as 15 min waitingtime and you get a 15 min bonus while you are in the first 15 min :)
		// (to avoid 20 sec downloads) after this the score won't raise anymore 
		fBaseValue = (float)(m_dwUploadTime-GetWaitStartTime());
		//ASSERT ( m_dwUploadTime-GetWaitStartTime() >= 0 ); //oct 28, 02: changed this from "> 0" to ">= 0" -> // 02-Okt-2006 []: ">=0" is always true!
		fBaseValue += (float)(::GetTickCount() - m_dwUploadTime > 900000)? 900000:1800000;
		fBaseValue /= 1000;
	}
	if(thePrefs.UseCreditSystem())
	{
		float modif = credits->GetScoreRatio(GetIP());
		fBaseValue *= modif;
//>>> CA
		if (pAntiLeechData)
			fBaseValue *= pAntiLeechData->GetScore();
		else
			ASSERT(0); //should never happen...
//<<< CA
	}
	if (!onlybasevalue)
		fBaseValue *= (float(filepriority)/10.0f);

	if( (IsEmuleClient() || this->GetClientSoft() < 10) && m_byEmuleVersion <= 0x19 )
		fBaseValue *= 0.5f;
	return (uint32)fBaseValue;
}

/**
 * Checks if the file this client has requested has powershare priority.
 *
 * @return true if the requested file has powershare priority
 */
bool CUpDownClient::GetPowerShared() const {
	if(GetUploadFileID() != NULL &&
       theApp.sharedfiles->GetFileByID(GetUploadFileID()) != NULL) {
		return theApp.sharedfiles->GetFileByID(GetUploadFileID())->GetPowerShared();
	} else {
		return false;
	}
}


//class CSyncHelper
//{
//public:
//	CSyncHelper()
//	{
//		m_pObject = NULL;
//	}
//	~CSyncHelper()
//	{
//		if (m_pObject)
//			m_pObject->Unlock();
//	}
//	CSyncObject* m_pObject;
//};

//#define PACKETSPLITSIZE (GetDatarate() > 100*1024 ? m_minSendBufferSize : 10240)
////10240
//
//void CUpDownClient::CreateNextBlockPackage(){
//    theApp.QueueDebugLogLine(false, _T("%i CUpDownClient::CreateNextBlockPackage() started. Buffer limits: %s-%s Buffered amount: %s Client: %s"), timeGetTime(), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//
//    //if(m_bQueueWasEmptyAfterLastRead && GetQueueSessionPayloadUp() > 0 && GetPayloadInBuffer() == 0) {
//    //    AddDebugLogLine(DLP_VERYLOW, false, _T("Buffer empty AND req queue empty! Buffer limits: %s-%s Buffered amount: %s Client: %s"), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//    //}
//
//    // See if we can do an early return. 
//
//    if(m_addedPayloadQueueSession > GetQueueSessionPayloadUp() && m_addedPayloadQueueSession-GetQueueSessionPayloadUp() >= m_minSendBufferSize) {
//        // Buffer is large enough already.
//        theApp.QueueDebugLogLine(false, _T("%i Buffered data determined to be enough before while loop. Buffer limits: %s-%s Buffered amount: %s Client: %s"), timeGetTime(), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//        return;
//    }
//
//    if(m_BlockRequests_queue.IsEmpty()) {
//        // There are no new blocks requested
//        theApp.QueueDebugLogLine(false, _T("%i Req queue empty before while loop. Buffer limits: %s-%s Buffered amount: %s Client: %s"), timeGetTime(), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//        return;
//    }
//
//    if(GetQueueSessionPayloadUp() > 0 && !m_bQueueWasEmptyAfterLastRead) {
//        if(m_minSendBufferSize < 10*1024*1024 && (m_addedPayloadQueueSession <= GetQueueSessionPayloadUp() || m_addedPayloadQueueSession-GetQueueSessionPayloadUp() == 0)) {
//            if(GetDatarate() < 200*1024) {
//                m_minSendBufferSize = min(m_minSendBufferSize+20*1024, 10*1024*1024);
//            } else {
//                // LAN
//                if(m_minSendBufferSize < (UINT)GetDatarate()/2) {
//                    m_minSendBufferSize = GetDatarate()/2;
//                } else {
//                    m_minSendBufferSize = min(m_minSendBufferSize+256*1024, 10*1024*1024);
//                }
//            }
//
//            if(m_maxSendBufferSize < m_minSendBufferSize) {
//                m_maxSendBufferSize = m_minSendBufferSize;
//            }
//            //AddDebugLogLine(DLP_LOW, false, _T("m_minSendBufferSize grew. New buffer limits: %s-%s Buffered amount: %s Client: %s"), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//
//            m_dwLastDiskCountTick = ::GetTickCount();
//        } else if(::GetTickCount()-m_dwLastDiskCountTick > 10*1000) {
//            m_dwLastDiskCountTick = ::GetTickCount();
//        } else if(m_maxSendBufferSize < 10*1024*1024) {
//            if(GetDatarate() < 200*1024) {
//                m_maxSendBufferSize = min(m_maxSendBufferSize+20*1024, 10*1024*1024);
//            } else {
//                // LAN
//                m_maxSendBufferSize = min(m_maxSendBufferSize+256*1024, 10*1024*1024);
//            }
//            //AddDebugLogLine(DLP_LOW, false, _T("m_maxSendBufferSize grew. New buffer limits: %s-%s Buffered amount: %s Client: %s"), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//
//            m_dwLastDiskCountTick = ::GetTickCount();
//        }
//    }
//
//    CFile file;
//	byte* filedata = 0;
//	CString fullname;
//	bool bFromPF = true; // Statistic to breakdown uploaded data by complete file vs. partfile.
//	CSyncHelper lockFile;
//	try{
//        uint64 totalBytesRead = 0;
//
//        // Buffer new data if current buffer has too little in it
//        while (!m_BlockRequests_queue.IsEmpty() &&
//               (m_addedPayloadQueueSession <= GetQueueSessionPayloadUp() || m_addedPayloadQueueSession-GetQueueSessionPayloadUp() < m_maxSendBufferSize)) {
//
//			Requested_Block_Struct* currentblock = m_BlockRequests_queue.GetHead();
//			CKnownFile* srcfile = theApp.sharedfiles->GetFileByID(currentblock->FileID);
//			if (!srcfile)
//				throw GetResString(IDS_ERR_REQ_FNF);
//
//			if (srcfile->IsPartFile() && ((CPartFile*)srcfile)->GetStatus() != PS_COMPLETE){
//				// Do not access a part file, if it is currently moved into the incoming directory.
//				// Because the moving of part file into the incoming directory may take a noticable 
//				// amount of time, we can not wait for 'm_FileCompleteMutex' and block the main thread.
//				if (!((CPartFile*)srcfile)->m_FileCompleteMutex.Lock(0)){ // just do a quick test of the mutex's state and return if it's locked.
//					return;
//				}
//				lockFile.m_pObject = &((CPartFile*)srcfile)->m_FileCompleteMutex;
//				// If it's a part file which we are uploading the file remains locked until we've read the
//				// current block. This way the file completion thread can not (try to) "move" the file into
//				// the incoming directory.
//
//				fullname = RemoveFileExtension(((CPartFile*)srcfile)->GetFullName());
//			}
//			else{
//				fullname.Format(_T("%s\\%s"),srcfile->GetPath(),srcfile->GetFileName());
//			}
//		
//            // check if we can/should compress the packages
//            bool compFlag = (m_currentBlockDone == 0 && !IsUploadingToPeerCache() && m_byDataCompVer == 1 && theApp.uploadqueue && theApp.uploadqueue->GetDatarate() < 200*1024);
//            if(compFlag) {
//		        // check extension to decide whether to compress or not
//		        CString ext = srcfile->GetFileName();
//		        ext.MakeLower();
//		        int pos = ext.ReverseFind(_T('.'));
//
//                if (pos>-1)
//			        ext = ext.Mid(pos);
//
//                compFlag = (ext!=_T(".zip") && ext!=_T(".cbz") && ext!=_T(".rar") && ext!=_T(".cbr") && ext!=_T(".ace") && ext!=_T(".ogm"));
//
//                if (compFlag && ext==_T(".avi") && thePrefs.GetDontCompressAvi())
//			        compFlag=false;
//            }
//
//            // check how large the requested block is, and how much of it we should buffer from disk
//            uint64 i64uTogo;
//            uint64 blockSize;
//			if ((currentblock->StartOffset) > currentblock->EndOffset){
//                throw _T("currentblock->StartOffset) > currentblock->EndOffset! Does this ever occur?");
//
//				blockSize = currentblock->EndOffset + (srcfile->GetFileSize() - currentblock->StartOffset);
//                i64uTogo = blockSize;
//
//                if( i64uTogo > EMBLOCKSIZE*3 ) {
//			    	throw GetResString(IDS_ERR_LARGEREQBLOCK);
//                }
//			}
//			else{
//				blockSize = currentblock->EndOffset - currentblock->StartOffset;
//
//                if(blockSize > EMBLOCKSIZE*3) {
//                    compFlag = false;
//                }
//
//				if (srcfile->IsPartFile() && !((CPartFile*)srcfile)->IsComplete(currentblock->StartOffset,currentblock->EndOffset-1, true))
//					throw GetResString(IDS_ERR_INCOMPLETEBLOCK);
//
//                if(!compFlag && !IsUploadingToPeerCache()) {
//                    i64uTogo = blockSize-m_currentBlockDone;
//                    uint64 neededToReachBufferLimit = m_maxSendBufferSize;
//                    if(m_addedPayloadQueueSession >= GetQueueSessionPayloadUp()) {
//                        neededToReachBufferLimit = m_maxSendBufferSize - (m_addedPayloadQueueSession-GetQueueSessionPayloadUp());
//                        if(neededToReachBufferLimit % PACKETSPLITSIZE != 0) {
//                            neededToReachBufferLimit = (neededToReachBufferLimit/PACKETSPLITSIZE)*PACKETSPLITSIZE+PACKETSPLITSIZE;
//                        }
//                    }
//
//                    if(i64uTogo > neededToReachBufferLimit) {
//                        //AddDebugLogLine(DLP_VERYLOW, false, _T("i64uTogo (%s) bigger than neededToReachBufferLimit (%s). Setting i64uTogo to this limit. Buffer limits: %s-%s Buffered amount: %s Client: %s"),
//                        //    CastItoXBytes(i64uTogo), CastItoXBytes(neededToReachBufferLimit), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize),
//                        //    (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//                        i64uTogo = neededToReachBufferLimit;
//                    }
//                } else {
//                    i64uTogo = blockSize;
//
//                    if( i64uTogo > EMBLOCKSIZE*3 ) {
//			    	    throw GetResString(IDS_ERR_LARGEREQBLOCK);
//                    }
//                }
//            }
//
//			uint32 togo = (uint32)i64uTogo;
//
//            // read the data from disk into memory buffer
//			if (!srcfile->IsPartFile()){
//				bFromPF = false; // This is not a part file...
//				if (!file.Open(fullname,CFile::modeRead|CFile::osSequentialScan|CFile::shareDenyNone))
//					throw GetResString(IDS_ERR_OPEN);
//				file.Seek((currentblock->StartOffset+m_currentBlockDone),0);
//				
//				filedata = new byte[togo+500];
//				if (uint32 done = file.Read(filedata,togo) != togo){
//					file.SeekToBegin();
//					file.Read(filedata + done,togo-done);
//				}
//				file.Close();
//			}
//			else{
//				CPartFile* partfile = (CPartFile*)srcfile;
//
//				partfile->m_hpartfile.Seek((currentblock->StartOffset+m_currentBlockDone),0);
//				
//				filedata = new byte[togo+500];
//				if (uint32 done = partfile->m_hpartfile.Read(filedata,togo) != togo){
//					partfile->m_hpartfile.SeekToBegin();
//					partfile->m_hpartfile.Read(filedata + done,togo-done);
//				}
//			}
//			if (lockFile.m_pObject){
//				lockFile.m_pObject->Unlock(); // Unlock the (part) file as soon as we are done with accessing it.
//				lockFile.m_pObject = NULL;
//			}
//
//			SetUploadFileID(srcfile);
//
//            // create the packages from the memory buffer
//            if (compFlag && m_currentBlockDone == 0 && togo == blockSize)
//				CreatePackedPackets(filedata,togo,currentblock,bFromPF);
//			else
//				CreateStandartPackets(filedata,togo,currentblock,bFromPF);
//			
//			// file statistic
//			srcfile->statistic.AddTransferred(togo);
//
//            m_addedPayloadQueueSession += togo;
//            m_currentBlockDone += togo;
//            ASSERT(m_currentBlockDone <= blockSize);
//
//            totalBytesRead += togo;
//
//			delete[] filedata;
//			filedata = 0;
//
//            theApp.QueueDebugLogLine(false, _T("%i Read %s from disk in loop. %s/%s of requested block has been buffered/sent. Buffer limits: %s-%s Buffered amount: %s Client: %s"), timeGetTime(), CastItoXBytes(togo), CastItoXBytes(m_currentBlockDone), CastItoXBytes(blockSize), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//
//            m_bQueueWasEmptyAfterLastRead = false; // may be set to true in the check below
//
//            if(m_currentBlockDone == blockSize) {
//			    m_DoneBlocks_list.AddHead(m_BlockRequests_queue.RemoveHead());
//
//                //AddDebugLogLine(DLP_LOW, false, _T("Completed a req block of size %s. %i requested blocks now in queue. Buffer limits: %s-%s Buffered amount: %s Client: %s"), CastItoXBytes(blockSize), m_BlockRequests_queue.GetCount(), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//
//                m_currentBlockDone = 0;
//
//                if(m_BlockRequests_queue.IsEmpty()) {
//                    theApp.QueueDebugLogLine(false, _T("%i Req queue empty. Buffer limits: %s-%s Buffered amount: %s Client: %s"), timeGetTime(), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//                    m_bQueueWasEmptyAfterLastRead = true;
//                }
//            }
//
//		}
//
//        theApp.QueueDebugLogLine(false, _T("%i CUpDownClient::CreateNextBlockPackage() completed. Read %s from disk. Buffer limits: %s-%s Buffered amount: %s Client: %s"), timeGetTime(), CastItoXBytes(totalBytesRead), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), (m_addedPayloadQueueSession < GetQueueSessionPayloadUp() ? _T("< 0") : CastItoXBytes(m_addedPayloadQueueSession-GetQueueSessionPayloadUp())), DbgGetClientInfo());
//	}
//	catch(CString error)
//	{
//		if (thePrefs.GetVerbose())
//			DebugLogWarning(GetResString(IDS_ERR_CLIENTERRORED), GetUserName(), error);
//		theApp.uploadqueue->RemoveFromUploadQueue(this, _T("Client error: ") + error);
//		delete[] filedata;
//		return;
//	}
//	catch(CFileException* e)
//	{
//		TCHAR szError[MAX_CFEXP_ERRORMSG];
//		e->GetErrorMessage(szError, ARRSIZE(szError));
//		if (thePrefs.GetVerbose())
//			DebugLogWarning(_T("Failed to create upload package for %s - %s"), GetUserName(), szError);
//		theApp.uploadqueue->RemoveFromUploadQueue(this, ((CString)_T("Failed to create upload package.")) + szError);
//		delete[] filedata;
//		e->Delete();
//		return;
//	}
//}

bool CUpDownClient::ProcessExtendedInfo(CSafeMemFile* data, CKnownFile* tempreqfile)
{
	delete[] m_abyUpPartStatus;
	m_abyUpPartStatus = NULL;
	m_nUpPartCount = 0;
	m_nUpCompleteSourcesCount= 0;
	if (GetExtendedRequestsVersion() == 0)
		return true;

	uint16 nED2KUpPartCount = data->ReadUInt16();
	if (!nED2KUpPartCount)
	{
		m_nUpPartCount = tempreqfile->GetPartCount();
		m_abyUpPartStatus = new uint8[m_nUpPartCount];
		memset(m_abyUpPartStatus, 0, m_nUpPartCount);
	}
	else
	{
		if (tempreqfile->GetED2KPartCount() != nED2KUpPartCount)
		{
			//We already checked if we are talking about the same file.. So if we get here, something really strange happened!
			m_nUpPartCount = 0;
			return false;
		}
		m_nUpPartCount = tempreqfile->GetPartCount();
		m_abyUpPartStatus = new uint8[m_nUpPartCount];
		uint16 done = 0;
//>>> CA
		uint16 complcount = 0;
//<<< CA
		while (done != m_nUpPartCount)
		{
			uint8 toread = data->ReadUInt8();
			for (UINT i = 0; i != 8; i++)
			{
				m_abyUpPartStatus[done] = ((toread >> i) & 1) ? 1 : 0;
//				We may want to use this for another feature..
//				if (m_abyUpPartStatus[done] && !tempreqfile->IsComplete((uint64)done*PARTSIZE,((uint64)(done+1)*PARTSIZE)-1))
//					bPartsNeeded = true;
//>>> CA
				if (m_abyUpPartStatus[done])
					complcount++;
//<<< CA
				done++;
				if (done == m_nUpPartCount)
					break;
			}
		}
//>>> CA
		if (pAntiLeechData)
		{
			if (complcount == m_nUpPartCount)
				pAntiLeechData->SetBadForThisSession(AT_FILEFAKER);
			else
				pAntiLeechData->ClearBadForThisSession(AT_FILEFAKER);
		}
//<<< CA
	}
	if (GetExtendedRequestsVersion() > 1)
	{
		uint16 nCompleteCountLast = GetUpCompleteSourcesCount();
		uint16 nCompleteCountNew = data->ReadUInt16();
		SetUpCompleteSourcesCount(nCompleteCountNew);
		if (nCompleteCountLast != nCompleteCountNew)
			tempreqfile->UpdatePartsInfo();
	}
	theApp.emuledlg->transferwnd->queuelistctrl.RefreshClient(this);
	return true;
}

//void CUpDownClient::CreateStandartPackets(byte* data,uint32 togo, Requested_Block_Struct* currentblock, bool bFromPF){
//	uint32 nPacketSize;
//	CMemFile memfile((BYTE*)data,togo);
//	if (togo > 10240) 
//		nPacketSize = togo/(uint32)(togo/10240);
//	else
//		nPacketSize = togo;
//	while (togo){
//		if (togo < nPacketSize*2)
//			nPacketSize = togo;
//		ASSERT( nPacketSize );
//		togo -= nPacketSize;
//
//		uint64 statpos = (currentblock->EndOffset - togo) - nPacketSize;
//		uint64 endpos = (currentblock->EndOffset - togo);
//		if (IsUploadingToPeerCache())
//		{
//			if (m_pPCUpSocket == NULL){
//				ASSERT(0);
//				CString strError;
//				strError.Format(_T("Failed to upload to PeerCache - missing socket; %s"), DbgGetClientInfo());
//				throw strError;
//			}
//			CSafeMemFile dataHttp(10240);
//			if (m_iHttpSendState == 0)
//			{
//				CKnownFile* srcfile = theApp.sharedfiles->GetFileByID(GetUploadFileID());
//				CStringA str;
//				str.AppendFormat("HTTP/1.0 206\r\n");
//				str.AppendFormat("Content-Range: bytes %I64u-%I64u/%I64u\r\n", currentblock->StartOffset, currentblock->EndOffset - 1, srcfile->GetFileSize());
//				str.AppendFormat("Content-Type: application/octet-stream\r\n");
//				str.AppendFormat("Content-Length: %u\r\n", (uint32)(currentblock->EndOffset - currentblock->StartOffset));
//				str.AppendFormat("Server: eMule/%s\r\n", CStringA(theApp.m_strCurVersionLong));
//				str.AppendFormat("\r\n");
//				dataHttp.Write((LPCSTR)str, str.GetLength());
//				theStats.AddUpDataOverheadFileRequest((UINT)dataHttp.GetLength());
//
//				m_iHttpSendState = 1;
//				if (thePrefs.GetDebugClientTCPLevel() > 0){
//					DebugSend("PeerCache-HTTP", this, GetUploadFileID());
//					Debug(_T("  %hs\n"), str);
//				}
//			}
//			dataHttp.Write(data, nPacketSize);
//			data += nPacketSize;
//
//			if (thePrefs.GetDebugClientTCPLevel() > 1){
//				DebugSend("PeerCache-HTTP data", this, GetUploadFileID());
//				Debug(_T("  Start=%I64u  End=%I64u  Size=%u\n"), statpos, endpos, nPacketSize);
//			}
//
//			UINT uRawPacketSize = (UINT)dataHttp.GetLength();
//			LPBYTE pRawPacketData = dataHttp.Detach();
//			CRawPacket* packet = new CRawPacket((char*)pRawPacketData, uRawPacketSize, bFromPF);
//			m_pPCUpSocket->SendPacket(packet, true, false, nPacketSize);
//			free(pRawPacketData);
//		}
//		else
//		{
//			Packet* packet;
//			if (statpos > 0xFFFFFFFF || endpos > 0xFFFFFFFF){
//				packet = new Packet(OP_SENDINGPART_I64,nPacketSize+32, OP_EMULEPROT, bFromPF);
//				md4cpy(&packet->pBuffer[0],GetUploadFileID());
//				PokeUInt64(&packet->pBuffer[16], statpos);
//				PokeUInt64(&packet->pBuffer[24], endpos);
//				memfile.Read(&packet->pBuffer[32],nPacketSize);
//				theStats.AddUpDataOverheadFileRequest(32);
//			}
//			else{
//				packet = new Packet(OP_SENDINGPART,nPacketSize+24, OP_EDONKEYPROT, bFromPF);
//				md4cpy(&packet->pBuffer[0],GetUploadFileID());
//				PokeUInt32(&packet->pBuffer[16], (uint32)statpos);
//				PokeUInt32(&packet->pBuffer[20], (uint32)endpos);
//				memfile.Read(&packet->pBuffer[24],nPacketSize);
//				theStats.AddUpDataOverheadFileRequest(24);
//			}
//
//			if (thePrefs.GetDebugClientTCPLevel() > 0){
//				DebugSend("OP__SendingPart", this, GetUploadFileID());
//				Debug(_T("  Start=%I64u  End=%I64u  Size=%u\n"), statpos, endpos, nPacketSize);
//			}
//			// put packet directly on socket
//			
//			socket->SendPacket(packet,true,false, nPacketSize);
//		}
//	}
//}
//
//void CUpDownClient::CreatePackedPackets(byte* data, uint32 togo, Requested_Block_Struct* currentblock, bool bFromPF){
//	BYTE* output = new BYTE[togo+300];
//	uLongf newsize = togo+300;
//	UINT result = compress2(output, &newsize, data, togo, 9);
//	if (result != Z_OK || togo <= newsize){
//		delete[] output;
//		CreateStandartPackets(data,togo,currentblock,bFromPF);
//		return;
//	}
//	CMemFile memfile(output,newsize);
//    uint32 oldSize = togo;
//	togo = newsize;
//	uint32 nPacketSize;
//    if (togo > 10240) 
//        nPacketSize = togo/(uint32)(togo/10240);
//    else
//        nPacketSize = togo;
//    
//    uint32 totalPayloadSize = 0;
//
//    while (togo){
//		if (togo < nPacketSize*2)
//			nPacketSize = togo;
//		ASSERT( nPacketSize );
//		togo -= nPacketSize;
//		uint64 statpos = currentblock->StartOffset;
//		Packet* packet;
//		if (currentblock->StartOffset > 0xFFFFFFFF || currentblock->EndOffset > 0xFFFFFFFF){
//			packet = new Packet(OP_COMPRESSEDPART_I64,nPacketSize+28,OP_EMULEPROT,bFromPF);
//			md4cpy(&packet->pBuffer[0],GetUploadFileID());
//			PokeUInt64(&packet->pBuffer[16], statpos);
//			PokeUInt32(&packet->pBuffer[24], newsize);
//			memfile.Read(&packet->pBuffer[28],nPacketSize);
//		}
//		else{
//			packet = new Packet(OP_COMPRESSEDPART,nPacketSize+24,OP_EMULEPROT,bFromPF);
//			md4cpy(&packet->pBuffer[0],GetUploadFileID());
//			PokeUInt32(&packet->pBuffer[16], (uint32)statpos);
//			PokeUInt32(&packet->pBuffer[20], newsize);
//			memfile.Read(&packet->pBuffer[24],nPacketSize);
//		}
//
//		if (thePrefs.GetDebugClientTCPLevel() > 0){
//			DebugSend("OP__CompressedPart", this, GetUploadFileID());
//			Debug(_T("  Start=%I64u  BlockSize=%u  Size=%u\n"), statpos, newsize, nPacketSize);
//		}
//        // approximate payload size
//        uint32 payloadSize = nPacketSize*oldSize/newsize;
//
//        if(togo == 0 && totalPayloadSize+payloadSize < oldSize) {
//            payloadSize = oldSize-totalPayloadSize;
//        }
//        totalPayloadSize += payloadSize;
//
//        // put packet directly on socket
//		theStats.AddUpDataOverheadFileRequest(24);
//        socket->SendPacket(packet,true,false, payloadSize);
//	}
//	delete[] output;
//}

void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile)
{
	CKnownFile* oldreqfile;
	//We use the knownfilelist because we may have unshared the file..
	//But we always check the download list first because that person may have decided to redownload that file.
	//Which will replace the object in the knownfilelist if completed.
	if ((oldreqfile = theApp.downloadqueue->GetFileByID(requpfileid)) == NULL)
		oldreqfile = theApp.knownfiles->FindKnownFileByID(requpfileid);

	if (newreqfile == oldreqfile)
		return;

	// clear old status
	delete[] m_abyUpPartStatus;
	m_abyUpPartStatus = NULL;
	m_nUpPartCount = 0;
	m_nUpCompleteSourcesCount= 0;

	if (newreqfile)
	{
		newreqfile->AddUploadingClient(this);
		md4cpy(requpfileid, newreqfile->GetFileHash());
	}
	else
		md4clr(requpfileid);

	if (oldreqfile)
		oldreqfile->RemoveUploadingClient(this);
}


void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock) {
    if(GetUploadState() != US_UPLOADING) {
        if(thePrefs.GetLogUlDlEvents())
            AddDebugLogLine(DLP_LOW, false, _T("UploadClient: Client tried to add req block when not in upload slot! Prevented req blocks from being added. %s"), DbgGetClientInfo());
		delete reqblock;
        return;
    }

	if(HasCollectionUploadSlot()){
		CKnownFile* pDownloadingFile = theApp.sharedfiles->GetFileByID(reqblock->FileID);
		if(pDownloadingFile != NULL){
			if ( !(CCollection::HasCollectionExtention(pDownloadingFile->GetFileName()) && pDownloadingFile->GetFileSize() < (uint64)MAXPRIORITYCOLL_SIZE) ){
				AddDebugLogLine(DLP_HIGH, false, _T("UploadClient: Client tried to add req block for non collection while having a collection slot! Prevented req blocks from being added. %s"), DbgGetClientInfo());
				delete reqblock;
				return;
			}
		}
		else
			ASSERT( false );
	}

    if(m_UploadDiskreaderThread) {
        m_UploadDiskreaderThread->AddReqBlock(reqblock);
    }
}

uint32 CUpDownClient::SendBlockData(){
    DWORD curTick = ::GetTickCount();
    bool sumavgUDRChanged = false;

    uint64 sentBytesCompleteFile = 0;
    uint64 sentBytesPartFile = 0;
    uint64 sentBytesPayload = 0;

    if (GetFileUploadSocket() && (m_ePeerCacheUpState != PCUS_WAIT_CACHE_REPLY))
	{
		CEMSocket* s = GetFileUploadSocket();
		UINT uUpStatsPort;
        if (m_pPCUpSocket && IsUploadingToPeerCache())
		{
			uUpStatsPort = (UINT)-1;

            // Check if filedata has been sent via the normal socket since last call.
            uint64 sentBytesCompleteFileNormalSocket = socket->GetSentBytesCompleteFileSinceLastCallAndReset();
            uint64 sentBytesPartFileNormalSocket = socket->GetSentBytesPartFileSinceLastCallAndReset();

			if(thePrefs.GetVerbose() && (sentBytesCompleteFileNormalSocket + sentBytesPartFileNormalSocket > 0)) {
                AddDebugLogLine(false, _T("Sent file data via normal socket when in PC mode. Bytes: %I64i."), sentBytesCompleteFileNormalSocket + sentBytesPartFileNormalSocket);
			}
        }
		else
			uUpStatsPort = GetUserPort();

	    // Extended statistics information based on which client software and which port we sent this data to...
	    // This also updates the grand total for sent bytes, etc.  And where this data came from.
        sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset();
        sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset();
		thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, (UINT)sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
		thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, (UINT)sentBytesPartFile, (IsFriend() && GetFriendSlot()));

		m_nTransferredUp = (UINT)(m_nTransferredUp + sentBytesCompleteFile + sentBytesPartFile);
        credits->AddUploaded((UINT)(sentBytesCompleteFile + sentBytesPartFile), GetIP());

//>>> CA
		CKnownFile* file = theApp.sharedfiles->GetFileByID(GetUploadFileID());
		if (pAntiLeechData)
		{
			UINT srccount = _UI32_MAX; //no file? then default to non-rare...
			if (file)
			{
				srccount = file->m_nCompleteSourcesCount;
				//srccount += file->GetRealQueuedCount();
			}
			pAntiLeechData->AddUploaded(sentBytesCompleteFile, false, srccount);
			pAntiLeechData->AddUploaded(sentBytesPartFile, true, srccount);
		}
//<<< CA

        sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset();
        m_nCurQueueSessionPayloadUp = (UINT)(m_nCurQueueSessionPayloadUp + sentBytesPayload);

        if(m_UploadDiskreaderThread) {
            bool anExceptionOccured = false;
            CString exceptionString = m_UploadDiskreaderThread->checkException(anExceptionOccured);

            if(anExceptionOccured) {
                theApp.uploadqueue->RemoveFromUploadQueue(this, exceptionString);
            }
        }

        if(GetUploadState() == US_UPLOADING) {
            bool wasRemoved = false;
            //if(!IsScheduledForRemoval() && GetQueueSessionPayloadUp() > SESSIONMAXTRANS+1*1024 && curTick-m_dwLastCheckedForEvictTick >= 5*1000) {
            //    m_dwLastCheckedForEvictTick = curTick;
            //    wasRemoved = theApp.uploadqueue->RemoveOrMoveDown(this, true);
            //}

            if(!IsScheduledForRemoval() && /*wasRemoved == false &&*/ GetQueueSessionPayloadUp() > GetCurrentSessionLimit()) {
                // Should we end this upload?

                // first clear the average speed, to show ?? as speed in upload slot display
                m_AvarageUDR_list.RemoveAll();
                m_nSumForAvgUpDataRate = 0;
                sumavgUDRChanged = true;

                // Give clients in queue a chance to kick this client out.
                // It will be kicked out only if queue contains a client
                // of same/higher class as this client, and that new
                // client must either be a high ID client, or a low ID
                // client that is currently connected.
                wasRemoved = theApp.uploadqueue->RemoveOrMoveDown(this);

                if(!wasRemoved) {
                    // It wasn't removed, so it is allowed to pass into the next amount.
                    m_curSessionAmountNumber++;
                }
            }

            if(wasRemoved) {
			    //if (thePrefs.GetDebugClientTCPLevel() > 0)
				   // DebugSend("OP__OutOfPartReqs", this);
			    //Packet* pCancelTransferPacket = new Packet(OP_OUTOFPARTREQS, 0);
			    //theStats.AddUpDataOverheadFileRequest(pCancelTransferPacket->size);
			    //socket->SendPacket(pCancelTransferPacket,true,true);
			} else {
            	// read blocks from file and put on socket
	            //CreateNextBlockPackage();
			}
        }
    }

    if(sentBytesCompleteFile + sentBytesPartFile > 0 ||
        m_AvarageUDR_list.GetCount() == 0 || (curTick - m_AvarageUDR_list.GetTail().timestamp) > 1*1000) {
        // Store how much data we've transferred this round,
        // to be able to calculate average speed later
        // keep sum of all values in list up to date
        TransferredData newitem = {(UINT)(sentBytesCompleteFile + sentBytesPartFile), curTick};
        m_AvarageUDR_list.AddTail(newitem);
        m_nSumForAvgUpDataRate = (UINT)(m_nSumForAvgUpDataRate + sentBytesCompleteFile + sentBytesPartFile);
        sumavgUDRChanged = true;
    }

    // remove to old values in list
    while (m_AvarageUDR_list.GetCount() > 0 && (curTick - m_AvarageUDR_list.GetHead().timestamp) > 10*1000) {
        // keep sum of all values in list up to date
        m_nSumForAvgUpDataRate -= m_AvarageUDR_list.RemoveHead().datalen;
        sumavgUDRChanged = true;
    }

    bool avgChanged = false;
    // Calculate average speed for this slot
    if(m_AvarageUDR_list.GetCount() > 1) {
        if(sumavgUDRChanged) {
            DWORD dwDuration = curTick - m_AvarageUDR_list.GetHead().timestamp;

            if(dwDuration > 0 && GetUpStartTimeDelay() > 2*1000) {
                m_nUpDatarate = (m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.GetHead().timestamp);
                avgChanged = true;
            } else  if(m_nUpDatarate != -1) {
                // not enough values to calculate trustworthy speed. Use -1 to tell this
                m_nUpDatarate = -1;
                avgChanged = true;
            }
        }
    } else if(m_nUpDatarate != -1) {
        // not enough values to calculate trustworthy speed. Use -1 to tell this
        m_nUpDatarate = -1;
        avgChanged = true;
    }

    // Check if it's time to update the display.
    if (avgChanged && curTick-m_lastRefreshedULDisplay > MINWAIT_BEFORE_ULDISPLAY_WINDOWUPDATE+(uint32)(rand()*800/RAND_MAX)) {
        // Update display
        theApp.emuledlg->transferwnd->uploadlistctrl.RefreshClient(this);
        theApp.emuledlg->transferwnd->clientlistctrl.RefreshClient(this);
        m_lastRefreshedULDisplay = curTick;
    }

    return (UINT)(sentBytesCompleteFile + sentBytesPartFile);
}

void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
{
	//OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
	//The main reason for this is that if we put the client back on queue and it goes
	//back to the upload before the socket times out... We get a situation where the
	//downloader thinks it already sent the requested blocks and the uploader thinks
	//the downloader didn't send any request blocks. Then the connection times out..
	//I did some tests with eDonkey also and it seems to work well with them also..
	if (thePrefs.GetDebugClientTCPLevel() > 0)
		DebugSend("OP__OutOfPartReqs", this);
	Packet* pPacket = new Packet(OP_OUTOFPARTREQS, 0);
	theStats.AddUpDataOverheadFileRequest(pPacket->size);
	socket->SendPacket(pPacket, true, true);
	m_fSentOutOfPartReqs = 1;
    theApp.uploadqueue->AddClientToQueue(this, true);
}

/**
 * See description for CEMSocket::TruncateQueues().
 */
void CUpDownClient::FlushSendBlocks(){ // call this when you stop upload, or the socket might be not able to send
    if (socket)      //socket may be NULL...
        socket->TruncateQueues();
}

void CUpDownClient::SendHashsetPacket(const uchar* forfileid)
{
	CKnownFile* file = theApp.sharedfiles->GetFileByID(forfileid);
	if (!file){
		CheckFailedFileIdReqs(forfileid);
		throw GetResString(IDS_ERR_REQ_FNF) + _T(" (SendHashsetPacket)");
	}

	CSafeMemFile data(1024);
	data.WriteHash16(file->GetFileHash());
	UINT parts = file->GetHashCount();
	data.WriteUInt16((uint16)parts);
	for (UINT i = 0; i < parts; i++)
		data.WriteHash16(file->GetPartHash(i));
	if (thePrefs.GetDebugClientTCPLevel() > 0)
		DebugSend("OP__HashSetAnswer", this, forfileid);
	Packet* packet = new Packet(&data);
	packet->opcode = OP_HASHSETANSWER;
	theStats.AddUpDataOverheadFileRequest(packet->size);
	socket->SendPacket(packet,true,true);
}

void CUpDownClient::ClearUploadBlockRequests()
{
	FlushSendBlocks();

	//for (POSITION pos = m_BlockRequests_queue.GetHeadPosition();pos != 0;)
	//	delete m_BlockRequests_queue.GetNext(pos);
	//m_BlockRequests_queue.RemoveAll();
	//
	//for (POSITION pos = m_DoneBlocks_list.GetHeadPosition();pos != 0;)
	//	delete m_DoneBlocks_list.GetNext(pos);
	//m_DoneBlocks_list.RemoveAll();
}

void CUpDownClient::SendRankingInfo(){
	if (!ExtProtocolAvailable())
		return;
	UINT nRank = theApp.uploadqueue->GetWaitingPosition(this);
	if (!nRank)
		return;
	Packet* packet = new Packet(OP_QUEUERANKING,12,OP_EMULEPROT);
	PokeUInt16(packet->pBuffer+0, (uint16)nRank);
	memset(packet->pBuffer+2, 0, 10);
	if (thePrefs.GetDebugClientTCPLevel() > 0)
		DebugSend("OP__QueueRank", this);
	theStats.AddUpDataOverheadFileRequest(packet->size);
	socket->SendPacket(packet,true,true);
}

void CUpDownClient::SendCommentInfo(/*const*/ CKnownFile *file)
{
	if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1)
		return;
	m_bCommentDirty = false;

	UINT rating = file->GetFileRating();
	const CString& desc = file->GetFileComment();
	if (file->GetFileRating() == 0 && desc.IsEmpty())
		return;

	CSafeMemFile data(256);
	data.WriteUInt8((uint8)rating);
	data.WriteLongString(desc, GetUnicodeSupport());
	if (thePrefs.GetDebugClientTCPLevel() > 0)
		DebugSend("OP__FileDesc", this, file->GetFileHash());
	Packet *packet = new Packet(&data,OP_EMULEPROT);
	packet->opcode = OP_FILEDESC;
	theStats.AddUpDataOverheadFileRequest(packet->size);
	socket->SendPacket(packet,true);
}

void CUpDownClient::AddRequestCount(const uchar* fileid)
{
	for (POSITION pos = m_RequestedFiles_list.GetHeadPosition(); pos != 0; ){
		Requested_File_Struct* cur_struct = m_RequestedFiles_list.GetNext(pos);
		if (!md4cmp(cur_struct->fileid,fileid)){
//>>> CA
			if (pAntiLeechData)
				pAntiLeechData->AddReask(::GetTickCount()-cur_struct->lastasked);
//<<< CA
			if (::GetTickCount() - cur_struct->lastasked < MIN_REQUESTTIME && !GetFriendSlot()){ 
				if (GetDownloadState() != DS_DOWNLOADING)
					cur_struct->badrequests++;
				if (cur_struct->badrequests == BADCLIENTBAN){
					Ban();
				}
			}
			else{
				if (cur_struct->badrequests)
					cur_struct->badrequests--;
			}
			cur_struct->lastasked = ::GetTickCount();
			return;
		}
	}
	Requested_File_Struct* new_struct = new Requested_File_Struct;
	md4cpy(new_struct->fileid,fileid);
	new_struct->lastasked = ::GetTickCount();
	new_struct->badrequests = 0;
	m_RequestedFiles_list.AddHead(new_struct);
}

void  CUpDownClient::UnBan()
{
	theApp.clientlist->AddTrackClient(this);
	theApp.clientlist->RemoveBannedClient(GetIP());
	SetUploadState(US_NONE);
	ClearWaitStartTime();
	theApp.emuledlg->transferwnd->ShowQueueCount(theApp.uploadqueue->GetWaitingUserCount());
	for (POSITION pos = m_RequestedFiles_list.GetHeadPosition();pos != 0;)
	{
		Requested_File_Struct* cur_struct = m_RequestedFiles_list.GetNext(pos);
		cur_struct->badrequests = 0;
		cur_struct->lastasked = 0;	
	}
}

void CUpDownClient::Ban(LPCTSTR pszReason)
{
	SetChatState(MS_NONE);
	theApp.clientlist->AddTrackClient(this);
//>>> SUQWT
	ClearWaitStartTime();
//<<< SUQWT
	if (!IsBanned()){
		if (thePrefs.GetLogBannedClients())
			AddDebugLogLine(false,_T("Banned: %s; %s"), pszReason==NULL ? _T("Aggressive behaviour") : pszReason, DbgGetClientInfo());
	}
#ifdef _DEBUG
	else{
		if (thePrefs.GetLogBannedClients())
			AddDebugLogLine(false,_T("Banned: (refreshed): %s; %s"), pszReason==NULL ? _T("Aggressive behaviour") : pszReason, DbgGetClientInfo());
	}
#endif
	theApp.clientlist->AddBannedClient(GetIP());
	SetUploadState(US_BANNED);
	theApp.emuledlg->transferwnd->ShowQueueCount(theApp.uploadqueue->GetWaitingUserCount());
	theApp.emuledlg->transferwnd->queuelistctrl.RefreshClient(this);
	if (socket != NULL && socket->IsConnected())
		socket->ShutDown(SD_RECEIVE); // let the socket timeout, since we dont want to risk to delete the client right now. This isnt acutally perfect, could be changed later
}

uint32 CUpDownClient::GetWaitStartTime() const
{
	if (credits == NULL){
		ASSERT ( false );
		return 0;
	}
	uint32 dwResult = credits->GetSecureWaitStartTime(GetIP());
//>>> SUQWT
	if (((int)(m_dwUploadTime - dwResult) < 0) && IsDownloading()){
//	if (dwResult > m_dwUploadTime && IsDownloading()){
//<<< SUQWT
		//this happens only if two clients with invalid securehash are in the queue - if at all
		dwResult = m_dwUploadTime-1;

		if (thePrefs.GetVerbose())
			DEBUG_ONLY(AddDebugLogLine(false,_T("Warning: CUpDownClient::GetWaitStartTime() waittime Collision (%s)"),GetUserName()));
	}
	return dwResult;
}

void CUpDownClient::SetWaitStartTime(){
	if (credits == NULL){
		return;
	}
	credits->SetSecWaitStartTime(GetIP());
}

void CUpDownClient::ClearWaitStartTime(){
	if (credits == NULL){
		return;
	}
	credits->ClearWaitStartTime();
}

bool CUpDownClient::GetFriendSlot() const
{
	if (credits && theApp.clientcredits->CryptoAvailable()){
		switch(credits->GetCurrentIdentState(GetIP())){
			case IS_IDFAILED:
			case IS_IDNEEDED:
			case IS_IDBADGUY:
				return false;
		}
	}
	return m_bFriendSlot;
}

CEMSocket* CUpDownClient::GetFileUploadSocket(bool bLog)
{
    if (m_pPCUpSocket && (IsUploadingToPeerCache() || m_ePeerCacheUpState == PCUS_WAIT_CACHE_REPLY))
	{
        if (bLog && thePrefs.GetVerbose())
            AddDebugLogLine(false, _T("%s got peercache socket."), DbgGetClientInfo());
        return m_pPCUpSocket;
    }
	else
	{
        if (bLog && thePrefs.GetVerbose())
            AddDebugLogLine(false, _T("%s got normal socket."), DbgGetClientInfo());
        return socket;
    }
}

void CUpDownClient::SetCollectionUploadSlot(bool bValue){
	ASSERT( !IsDownloading() || bValue == m_bCollectionUploadSlot );
	m_bCollectionUploadSlot = bValue;
}

void CUpDownClient::ResetSessionUp() {
	m_nCurSessionUp = m_nTransferredUp;
	m_addedPayloadQueueSession = GetQueueSessionPayloadUp();
	m_nCurSessionPayloadUp = m_addedPayloadQueueSession;

    //m_UploadDiskreaderThread->ResetSessionUp();
} 

bool CUpDownClient::HasBlocks() const {
    if(m_UploadDiskreaderThread) {
        return m_UploadDiskreaderThread->HasBlocks();
    } else {
        return false;
    }
}

UINT CUpDownClient::GetNumberOfRequestedBlocksInQueue() const {
    if(m_UploadDiskreaderThread) {
        return m_UploadDiskreaderThread->GetNumberOfRequestedBlocksInQueue();
    } else {
        return 0;
    }
}

void CUpDownClient::SendPacket(Packet* packet, bool delpacket, bool controlpacket, uint32 actualPayloadSize) {
    socket->SendPacket(packet, delpacket, controlpacket, actualPayloadSize);
}
