//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

#include "StdAfx.h"
#include "zlib/zlib.h"
#include "UpDownClient.h"
#include "opcodes.h"
#include "packets.h"
#include "emule.h"
#include "uploadqueue.h"
#include "otherstructs.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#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){ 
	COLORREF crBoth; 
	COLORREF crNeither; 
	COLORREF crClientOnly; 
	COLORREF crSending;
	COLORREF crNextSending;

	if(bFlat) { 
		crBoth = RGB(0, 0, 0);
		crNeither = RGB(224, 224, 224);
		crClientOnly = RGB(0, 220, 255);
		crSending = RGB(0,150,0);
		crNextSending = RGB(255,208,0);
	} else { 
		crBoth = RGB(104, 104, 104);
		crNeither = RGB(224, 224, 224);
		crClientOnly = RGB(0, 220, 255);
		crSending = RGB(0, 150, 0);
		crNextSending = RGB(255,208,0);
	} 

    uint32 filesize = PARTSIZE*(m_nUpPartCount);

	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 (uint32 i = 0;i < m_nUpPartCount;i++)
			if(m_abyUpPartStatus[i])
				s_UpStatusBar.FillRange(PARTSIZE*(i),PARTSIZE*(i+1),crBoth);
	}
	Requested_Block_Struct* block;
	if (!m_BlockRequests_queue.IsEmpty()){
		block = m_BlockRequests_queue.GetHead();
		if(block){
			uint32 start = block->StartOffset/PARTSIZE;
			s_UpStatusBar.FillRange(start*PARTSIZE, (start+1)*PARTSIZE, crNextSending);
		}
	}
	if (!m_DoneBlocks_list.IsEmpty()){
		block = m_DoneBlocks_list.GetTail();
		if(block){
			uint32 start = block->StartOffset/PARTSIZE;
			s_UpStatusBar.FillRange(start*PARTSIZE, (start+1)*PARTSIZE, crNextSending);
		}
	}
	if (!m_DoneBlocks_list.IsEmpty()){
		for(POSITION pos=m_DoneBlocks_list.GetHeadPosition();pos!=0;m_DoneBlocks_list.GetNext(pos)){
			Requested_Block_Struct* block = m_DoneBlocks_list.GetAt(pos);
			s_UpStatusBar.FillRange(block->StartOffset, block->EndOffset, crSending);
		}
	}
   	s_UpStatusBar.Draw(dc, rect->left, rect->top, bFlat); 
} 

void CUpDownClient::SetUploadState(EUploadState news){
	m_nUploadState = news;
	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() {
    ASSERT(credits != NULL);

    return (uint32)(10.0f*credits->GetScoreRatio()*float(GetFilePrioAsNumber()));
}

/**
 * Gets the file multiplier for the file this client has requested.
 */
int CUpDownClient::GetFilePrioAsNumber() {
	// 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
	if(GetUploadFileID() != NULL){ 
		if(theApp.sharedfiles->GetFileByID(GetUploadFileID()) != NULL){ 
			switch(theApp.sharedfiles->GetFileByID(GetUploadFileID())->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){
	//TODO: complete this (friends, uploadspeed, emuleuser etc etc)
	if (!m_pszUsername)
		return 0;

	CKnownFile* currequpfile = theApp.sharedfiles->GetFileByID(requpfileid);
	if(!currequpfile)
		return 0;

	// friend slot
	if (IsFriend() && GetFriendSlot() && !HasLowID())
		return 0x0FFFFFFF;

	if (IsBanned())
		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()-m_dwWaitTime)/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-m_dwWaitTime);
		ASSERT ( m_dwUploadTime-m_dwWaitTime >= 0 ); //oct 28, 02: changed this from "> 0" to ">= 0"
		fBaseValue += (float)(::GetTickCount() - m_dwUploadTime > 900000)? 900000:1800000;
		fBaseValue /= 1000;
	}
	ASSERT(credits != NULL);
	if(theApp.glob_prefs->UseCreditSystem())
		fBaseValue *= credits->GetScoreRatio();
	if (!onlybasevalue)
		fBaseValue *= (float(filepriority)/10.0f); 
	if (!isdownloading && !onlybasevalue){
		if (sysvalue && HasLowID() && !(socket && socket->IsConnected()) ){
			if (!theApp.serverconnect->IsConnected() || theApp.serverconnect->IsLowID() || theApp.listensocket->TooManySockets())
				return 0;
		}
	}
	if( (IsEmuleClient() || this->GetClientSoft() < 10) && m_byEmuleVersion <= 0x19 )
		fBaseValue *= 0.5f;
	return (uint32)fBaseValue;
}

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

// Checks if it is next requested block from another chunk of the actual file or from another file 
// 
// [Returns] 
//   true : Next requested block is from another different chunk or file than last downloaded block 
//   false: Next requested block is from same chunk that last downloaded block 
bool CUpDownClient::IsDifferentPartBlock()
{ 
	Requested_Block_Struct* lastBlock;
	Requested_Block_Struct* currBlock;
	uint32 lastDone = 0;
	uint32 currRequested = 0;
	
	bool different = false;
	
	try {
		// Check if we have good lists and proceed to check for different chunks
		if (!m_BlockRequests_queue.IsEmpty() && !m_DoneBlocks_list.IsEmpty())
		{
			// Calculate corresponding parts to blocks
			lastBlock = (Requested_Block_Struct*)m_DoneBlocks_list.GetTail();
			lastDone = lastBlock->StartOffset / PARTSIZE;
			currBlock = (Requested_Block_Struct*)m_BlockRequests_queue.GetHead(); 
			currRequested = currBlock->StartOffset / PARTSIZE; 
             
			// Test is we are asking same file and same part
			if ( lastDone != currRequested)
			{ 
				different = true;
				//theApp.emuledlg->AddDebugLogLine(false, "%s: Upload session ended due to new chunk.", this->GetUserName());
			}
			if (md4cmp(lastBlock->FileID, currBlock->FileID) != 0)
			{ 
				different = true;
				//theApp.emuledlg->AddDebugLogLine(false, "%s: Upload session ended due to different file.", this->GetUserName());
			}
		} 
   	}
   	catch(...)
   	{ 
			theApp.emuledlg->AddDebugLogLine(false, "%s: Upload session ended due to error.", this->GetUserName());
      		different = true; 
   	} 
//	theApp.emuledlg->AddDebugLogLine(false, "Debug: User %s, last_done_part (%u) %s (%u) next_requested_part, sent %u Kbs.", GetUserName(), last_done_part, different_part? "!=": "==", next_requested_part, this->GetTransferedUp() / 1024); 

	return different; 
}

bool CUpDownClient::CreateNextBlockPackage(){
	// time critical
	// check if we should kick this client
    // Never kick friends
    // Always kick if transfer is longer than 80 minutes
    // Never allow more than the size of a chunk (+500 KBytes for small resends of invalid data) to be transfered
	// VQB to provide full chunk transfers (modified by Tarod)
    // Note: Full chunk transfers really should be enforced, not an option
	if (!(IsFriend() && GetFriendSlot()) && IsDifferentPartBlock()) {

		return false;
	}

    if(m_BlockRequests_queue.IsEmpty()) {
        // There are no new blocks requested, but we will happily transfer more
        // if the client just requests something
        return true;
    }

    CFile file;
	byte* filedata = 0;
	char* fullname = 0;
	// -khaos--+++> Statistic to breakdown uploaded data by complete file vs. partfile.
	bool bFromPF = true;
	// <-----khaos-
	try{
        // repeat as long as next requested block is in the same chunk as previous blocks
        // and as long as there is a next requested block
		while (!m_BlockRequests_queue.IsEmpty() &&
               (IsDifferentPartBlock() == false ||
                IsFriend() && GetFriendSlot())){
			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){
				fullname = nstrdup(((CPartFile*)srcfile)->GetFullName());
				fullname[strlen(fullname)-4] = 0;			
			} else {
				fullname = new char[strlen(srcfile->GetPath())+strlen(srcfile->GetFileName())+10];
				sprintf(fullname,"%s\\%s",srcfile->GetPath(),srcfile->GetFileName());
			}

            uint32 togo;

            // Friends get special treatment. Check if it's a friend with a friends slot
            if(IsFriend() && GetFriendSlot() /*|| true*/) {
                // Friends are not limited to a single chunk

                // Is this a wraparound request?
                if (currentblock->StartOffset > currentblock->EndOffset){
                    // If StartOffset is later in the file than EndOffset, it means
                    // that the request is a wraparound request. It reads to the end of
                    // the file, then it wraps around and continues in the beginning of the file.

                    // Calculate how much the client is requesting, knowing that this is a wraparound request
				    togo = currentblock->EndOffset + (srcfile->GetFileSize() - currentblock->StartOffset);
			    } else {
                    // not a wraparound request. This makes it easier to calculate how many bytes is
                    // being requested.
				    togo = currentblock->EndOffset - currentblock->StartOffset;
			    }
            } else {
                // Everyone else are limited to a single chunk

                // Check that StartOffset and EndOffset is in the same chunk
                if(currentblock->StartOffset/PARTSIZE != currentblock->EndOffset/PARTSIZE) {
                    // The EndOffset goes into the next chunk. Set it to the end of the chunk
                    // that StartOffset is in.
                    currentblock->EndOffset = ((uint32)(currentblock->StartOffset/PARTSIZE)+1)*PARTSIZE;
                }

                // Make sure we don't pass end of file
			    if (currentblock->StartOffset > currentblock->EndOffset){
                    currentblock->EndOffset = srcfile->GetFileSize();
                }

                // This can't be a wrapped around request, since it has been limited to
                // a single chunk.
                togo = currentblock->EndOffset - currentblock->StartOffset;
            }

			if (srcfile->IsPartFile() && !((CPartFile*)srcfile)->IsComplete(currentblock->StartOffset,currentblock->EndOffset-1))
				throw GetResString(IDS_ERR_INCOMPLETEBLOCK);

			if (!srcfile->IsPartFile()){
				// -khaos--+++> This is not a part file...
				bFromPF = false;
				// <-----khaos-
				if (!file.Open(fullname,CFile::modeRead|CFile::osSequentialScan|CFile::shareDenyNone))
					throw GetResString(IDS_ERR_OPEN);
				delete[] fullname;
				fullname = 0;
				file.Seek(currentblock->StartOffset,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;
				delete[] fullname;
				fullname = 0;

				//TODO: This seems to fire an exception (because of invalid file handle) after the file was completed!?
				partfile->m_hpartfile.Seek(currentblock->StartOffset,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);
				}
			}

			SetUploadFileID(currentblock->FileID);

			// check extention to decide whether to compress or not
			CString ext=srcfile->GetFileName();ext.MakeLower();
			int pos=ext.ReverseFind('.');
			if (pos>-1) ext=ext.Mid(pos);
			bool compFlag=(ext!=".zip" && ext!=".rar" && ext!=".ace" && ext!=".ogm" );
			if (ext==".avi" && theApp.glob_prefs->GetDontCompressAvi()) compFlag=false;

			// -khaos--+++> We're going to add bFromPF as a parameter to the calls to create packets...
			if (m_byDataCompVer == 1 && compFlag )
				CreatePackedPackets(filedata,togo,currentblock,bFromPF);
			else
				CreateStandartPackets(filedata,togo,currentblock,bFromPF);
			// <-----khaos-
			
			// file statistic
			srcfile->statistic.AddTransferred(togo);

			m_DoneBlocks_list.AddHead(m_BlockRequests_queue.RemoveHead());
			delete[] filedata;
			filedata = 0;
		}
	}
	catch(CString error){
		OUTPUT_DEBUG_TRACE();
		theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_ERR_CLIENTERRORED),GetUserName(),error.GetBuffer());
		theApp.uploadqueue->RemoveFromUploadQueue(this);
		SetUploadFileID(NULL);
		if (filedata)
			delete filedata;
		if (fullname)
			delete[] fullname;
		return false;
	}
	catch(CFileException* e){
		TCHAR szError[MAX_CFEXP_ERRORMSG];
		e->GetErrorMessage(szError, ARRSIZE(szError));
		theApp.emuledlg->AddDebugLogLine(false,_T("Failed to create upload package for %s - %s"),GetUserName(),szError);
		theApp.uploadqueue->RemoveFromUploadQueue(this);
		SetUploadFileID(NULL);
		if (filedata)
			delete filedata;
		if (fullname)
			delete[] fullname;
		e->Delete();
		return false;
	}
//	theApp.emuledlg->AddDebugLogLine(false,"Debug: Packet done. Size: %i",blockpack->GetLength());
	return true;
}

void CUpDownClient::ProcessUpFileStatus(char* packet,uint32 size){
	if (m_abyUpPartStatus) {
		delete[] m_abyUpPartStatus;
		m_abyUpPartStatus = NULL;	// added by jicxicmic
	}
	m_nUpPartCount = 0;
	if( size == 16 )
		return;
	CSafeMemFile data((BYTE*)packet,size);
	uchar cfilehash[16];
	data.Read(cfilehash,16);
	CKnownFile* tempreqfile = theApp.sharedfiles->GetFileByID(cfilehash);
	data.Read(&m_nUpPartCount,2);
	if (!m_nUpPartCount){
		m_nUpPartCount = tempreqfile->GetPartCount();
		m_abyUpPartStatus = new uint8[m_nUpPartCount];
		memset(m_abyUpPartStatus,0,m_nUpPartCount);
	}

	else{
		if (tempreqfile->GetPartCount() != m_nUpPartCount){
			m_nUpPartCount = 0;
			return;
		}
		m_abyUpPartStatus = new uint8[m_nUpPartCount];
		uint16 done = 0;
		while (done != m_nUpPartCount){
			uint8 toread;
			data.Read(&toread,1);
			for (sint32 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(done*PARTSIZE,((done+1)*PARTSIZE)-1))
//					bPartsNeeded = true;
				done++;
				if (done == m_nUpPartCount)
					break;
			}
		}

	}
	tempreqfile->NewAvailPartsInfo();
	theApp.emuledlg->transferwnd.queuelistctrl.RefreshClient(this);
}


// -khaos--+++> Added new parameter: bool bFromPF
void CUpDownClient::CreateStandartPackets(byte* data,uint32 togo, Requested_Block_Struct* currentblock, bool bFromPF){
	// <-----khaos-
	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;
		togo -= nPacketSize;
		// -khaos--+++> Create the packet with the new boolean.
		Packet* packet = new Packet(OP_SENDINGPART,nPacketSize+24, OP_EDONKEYPROT, bFromPF);
		// <-----khaos-
		md4cpy(&packet->pBuffer[0],GetUploadFileID());
		uint32 statpos = (currentblock->EndOffset - togo) - nPacketSize;
		memcpy(&packet->pBuffer[16],&statpos,4);
		uint32 endpos = (currentblock->EndOffset - togo);
		memcpy(&packet->pBuffer[20],&endpos,4);
		memfile.Read(&packet->pBuffer[24],nPacketSize);
        m_BlockSend_queue.AddTail(packet);
	}
}

// -khaos--+++> Added new parameter: bool bFromPF
void CUpDownClient::CreatePackedPackets(byte* data,uint32 togo, Requested_Block_Struct* currentblock, bool bFromPF){
	// <-----khaos-
	BYTE* output = new BYTE[togo+300];
	uLongf newsize = togo+300;
	uint16 result = compress2(output,&newsize,data,togo,9);
	if (result != Z_OK || togo <= newsize){
		delete[] output;
		// -khaos--+++>  Our new boolean...
		CreateStandartPackets(data,togo,currentblock,bFromPF);
		// <-----khaos-
		return;
	}
	m_bUsedComprUp = true;
	CMemFile memfile(output,newsize);
	togo = newsize;
	uint32 nPacketSize;
	if (togo > 10240) 
		nPacketSize = togo/(uint32)(togo/10240);
	else
		nPacketSize = togo;
	while (togo){
		if (togo < nPacketSize*2)
			nPacketSize = togo;
		togo -= nPacketSize;
		// -khaos--+++> Create the packet with the new boolean.
		Packet* packet = new Packet(OP_COMPRESSEDPART,nPacketSize+24,OP_EMULEPROT,bFromPF);
		// <-----khaos-
		md4cpy(&packet->pBuffer[0],GetUploadFileID());
		uint32 statpos = currentblock->StartOffset;
		memcpy(&packet->pBuffer[16],&statpos,4);
		memcpy(&packet->pBuffer[20],&newsize,4);
		memfile.Read(&packet->pBuffer[24],nPacketSize);
		m_BlockSend_queue.AddTail(packet);
	}
	delete[] output;
}

void CUpDownClient::SetUploadFileID(uchar* tempreqfileid){
	CKnownFile* newreqfile = NULL;
	if( tempreqfileid )
		newreqfile = theApp.sharedfiles->GetFileByID(tempreqfileid);
	CKnownFile* oldreqfile = theApp.sharedfiles->GetFileByID(requpfileid);
	if(newreqfile == oldreqfile)
		return;
	if(newreqfile){
		newreqfile->AddQueuedCount();
		newreqfile->AddUploadingClient(this);
		md4cpy(requpfileid,tempreqfileid);
	}
	else{
		md4clr(requpfileid);
	}
	if(oldreqfile){
		oldreqfile->SubQueuedCount();
		oldreqfile->RemoveUploadingClient(this);
	}
}


void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock){

	for (POSITION pos = m_DoneBlocks_list.GetHeadPosition();pos != 0;m_DoneBlocks_list.GetNext(pos)){
		if (reqblock->StartOffset == m_DoneBlocks_list.GetAt(pos)->StartOffset && reqblock->EndOffset == m_DoneBlocks_list.GetAt(pos)->EndOffset){
			delete reqblock;
			return;
		}
	}
	for (POSITION pos = m_BlockRequests_queue.GetHeadPosition();pos != 0;m_BlockRequests_queue.GetNext(pos)){
		if (reqblock->StartOffset == m_BlockRequests_queue.GetAt(pos)->StartOffset && reqblock->EndOffset == m_BlockRequests_queue.GetAt(pos)->EndOffset){
			delete reqblock;
			return;
		}
	}
	m_BlockRequests_queue.AddTail(reqblock);

}

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

    if (socket && GetUploadState() == US_UPLOADING) {
            if (m_BlockSend_queue.IsEmpty() && !CreateNextBlockPackage() && !socket->HasQueues()) {
                m_AvarageUDR_list.RemoveAll();
                sumavgUDR = 0;

                if((IsFriend() == false || GetFriendSlot() == false) && HasReleasePrio() == false ||
                m_BlockRequests_queue.IsEmpty()) {
                    // no valid blocks to transfer. Put the client back on queue
                    SetWaitStartTime();
		            theApp.uploadqueue->RemoveFromUploadQueue(this, true);
		            theApp.uploadqueue->AddClientToQueue(this,true);
		            return 0;
                } else {
                    theApp.uploadqueue->RemoveOrMoveDown(this);
                    return 0;
                }
		    }

            bool blocked = false;

            // Feed data to this slot, one block at a time, as long as the
            // sockets accepted the last block.
            // There must also be blocks to send (not empty request queue)
		    while (!m_BlockSend_queue.IsEmpty() && blocked == false) {
                // Get next block and give it to the socket for transfer
			    Packet* tosend = m_BlockSend_queue.RemoveHead();

                int sent = socket->SendPacket(tosend,true,false);

                // If the socket didn't accept the data, put it back on head of the queue
                if(sent == 0) {
                    m_BlockSend_queue.AddHead(tosend);
                    blocked = true;
                } else {
                    tosend = NULL;

                    // If we're out of prepared blocks, create some more
	                if (m_BlockSend_queue.IsEmpty())
		                CreateNextBlockPackage();
                }
		    }
    }

    uint64 sentBytesCompleteFile = socket->GetSentBytesCompleteFileSinceLastCallAndReset();
    uint64 sentBytesPartFile = socket->GetSentBytesPartFileSinceLastCallAndReset();

    // -khaos--+++>
	// 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.  Yeesh.
	theApp.glob_prefs->Add2SessionTransferData(GetClientSoft(), GetUserPort(), false, true, sentBytesCompleteFile, (IsFriend() == true && GetFriendSlot() == true));
	theApp.glob_prefs->Add2SessionTransferData(GetClientSoft(), GetUserPort(), true, true, sentBytesPartFile, (IsFriend() == true && GetFriendSlot() == true));
	m_nTransferedUp += sentBytesCompleteFile + sentBytesPartFile;
    credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile);

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

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

    if(m_AvarageUDR_list.GetCount() > 0 && (::GetTickCount() - m_AvarageUDR_list.GetHead().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) {
        m_nUpDatarate = (sumavgUDR*1000) / (::GetTickCount()-m_AvarageUDR_list.GetHead().timestamp);
    } else {
        // not enough values to calculate trustworthy speed. Use -1 to tell this
        m_nUpDatarate = -1;
    }

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

    return sentBytesCompleteFile + sentBytesPartFile;
}

void CUpDownClient::FlushSendBlocks(){ // call this when you stop upload, or the socket might be not able to send
	while (!m_BlockSend_queue.IsEmpty() && m_BlockSend_queue.GetHead()->IsSplitted() && socket && socket->IsConnected()){	
		Packet* tosend = m_BlockSend_queue.RemoveHead();
		theApp.uploadqueue->AddUpDataOverheadOther(tosend->size);
		socket->SendPacket(tosend,true,false, true);
	}
}

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

	CSafeMemFile data(1024);
	data.Write(file->GetFileHash(),16);
	uint16 parts = file->GetHashCount();
	data.Write(&parts,2);
	for (int i = 0; i < parts; i++)
		data.Write(file->GetPartHash(i),16);
	Packet* packet = new Packet(&data);
	packet->opcode = OP_HASHSETANSWER;
	theApp.uploadqueue->AddUpDataOverheadFileRequest(packet->size);
	socket->SendPacket(packet,true,true);
}

void CUpDownClient::ClearUploadBlockRequests(){
	FlushSendBlocks();
    for (POSITION pos = m_BlockRequests_queue.GetHeadPosition();pos != 0;m_BlockRequests_queue.GetNext(pos))
		delete m_BlockRequests_queue.GetAt(pos);
	m_BlockRequests_queue.RemoveAll();
	
	for (POSITION pos = m_DoneBlocks_list.GetHeadPosition();pos != 0;m_DoneBlocks_list.GetNext(pos))
		delete m_DoneBlocks_list.GetAt(pos);
	m_DoneBlocks_list.RemoveAll();
	
	for (POSITION pos = m_BlockSend_queue.GetHeadPosition();pos != 0;m_BlockSend_queue.GetNext(pos))
		delete m_BlockSend_queue.GetAt(pos);
	m_BlockSend_queue.RemoveAll();
}

void CUpDownClient::ClearUploadDoneBlocks() {
	for (POSITION pos = m_DoneBlocks_list.GetHeadPosition();pos != 0;m_DoneBlocks_list.GetNext(pos))
		delete m_DoneBlocks_list.GetAt(pos);
	m_DoneBlocks_list.RemoveAll();
}

void CUpDownClient::SendRankingInfo(){
	if (!ExtProtocolAvailable())
		return;
	uint16 nRank = theApp.uploadqueue->GetWaitingPosition(this);
	if (!nRank)
		return;
	Packet* packet = new Packet(OP_QUEUERANKING,12,OP_EMULEPROT);
	memset(packet->pBuffer,0,12);
	memcpy(packet->pBuffer+0,&nRank,2);
	theApp.uploadqueue->AddUpDataOverheadOther(packet->size);
	socket->SendPacket(packet,true,true);
}

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

	int8 rating=file->GetFileRate();
	CString desc=file->GetFileComment();
	if(file->GetFileRate() == 0 && desc.IsEmpty())
		return;

	CSafeMemFile data(256);
	data.Write(&rating,sizeof(rating));
	int length=desc.GetLength();
	if (length>128)
		length=128;
	data.Write(&length,sizeof(length));
	if (length>0)
		data.Write(desc.GetBuffer(),length);
	Packet *packet = new Packet(&data,OP_EMULEPROT);
	packet->opcode = OP_FILEDESC;
	theApp.uploadqueue->AddUpDataOverheadOther(packet->size);
	socket->SendPacket(packet,true);
}

void  CUpDownClient::AddRequestCount(uchar* fileid){
	for (POSITION pos = m_RequestedFiles_list.GetHeadPosition();pos != 0;m_RequestedFiles_list.GetNext(pos)){
		Requested_File_Struct* cur_struct = m_RequestedFiles_list.GetAt(pos);
		if (!md4cmp(cur_struct->fileid,fileid)){
			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;
	memset(new_struct,0,sizeof(Requested_File_Struct));
	md4cpy(new_struct->fileid,fileid);
	new_struct->lastasked = ::GetTickCount();
	m_RequestedFiles_list.AddHead(new_struct);
}

void  CUpDownClient::UnBan(){
	m_bBanned = false;
	m_dwBanTime = 0;
	SetWaitStartTime();
	theApp.uploadqueue->UpdateBanCount();
	theApp.emuledlg->transferwnd.ShowQueueCount(theApp.uploadqueue->GetWaitingUserCount());
	for (POSITION pos = m_RequestedFiles_list.GetHeadPosition();pos != 0;m_RequestedFiles_list.GetNext(pos)){
		Requested_File_Struct* cur_struct = m_RequestedFiles_list.GetAt(pos);
		cur_struct->badrequests = 0;
		cur_struct->lastasked = 0;	
	}
//	theApp.emuledlg->transferwnd.queuelistctrl.RefreshClient(this, true, true);
}

void CUpDownClient::Ban(){
	m_bBanned = true;
	theApp.uploadqueue->UpdateBanCount();
	theApp.emuledlg->transferwnd.ShowQueueCount(theApp.uploadqueue->GetWaitingUserCount());
	m_dwBanTime = ::GetTickCount();
	theApp.emuledlg->transferwnd.queuelistctrl.RefreshClient(this);
	theApp.emuledlg->AddDebugLogLine(false,GetResString(IDS_CLIENTBLOCKED),GetUserName());
}

void CUpDownClient::UDPFileReasked(){
	AddAskedCount();
	SetLastUpRequest();
	uint16 nRank = theApp.uploadqueue->GetWaitingPosition(this);
	Packet* response = new Packet(OP_REASKACK,2,OP_EMULEPROT);
	memcpy(response->pBuffer,&nRank,2);
	theApp.uploadqueue->AddUpDataOverheadFileRequest(response->size);
	theApp.clientudp->SendPacket(response,GetIP(),GetUDPPort());
}

/**
 * See description for CEMSocket::TruncateQueues().
 */
void CUpDownClient::TruncateSocketQueues() {
    socket->TruncateQueues();
}