//this file is part of eMule
//Copyright (C)2002-2006 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 "OtherFunctions.h"
//#include "UploadDiskReaderThread.h"
//#include "Packets.h"
//#include "UploadQueue.h"
//#include "SharedFileList.h"
//#include "KnownFileList.h"
//#include "PartFile.h"
//#include "Log.h"
//#include "Collection.h"
//#include <Mmsystem.h>

#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"
#include "UploadPacketCreaterThread.h"
#include <Mmsystem.h>

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

/**
 * The constructor starts the thread.
 */
UploadDiskreaderThread::UploadDiskreaderThread(CUpDownClient* owner, uint8 byDataCompVer) {
    //theApp.QueueDebugLogLine(false, _T("%ui UploadDiskreaderThread constructor Client: %s"), timeGetTime(), owner->DbgGetClientInfo());    
    threadEndedEvent = new CEvent(0, 1);

    m_owner = owner;
    m_byDataCompVer = byDataCompVer;

    m_minSendBufferSize = 10*1024;
    m_maxSendBufferSize = m_minSendBufferSize;
    m_dwLastDiskCountTick = ::GetTickCount();
    m_currentBlockDone = 0;
    m_bQueueWasEmptyAfterLastRead = true;

    m_bAnExceptionOccured = false;

    // start packet creater
    m_UploadPacketCreaterThread = new UploadPacketCreaterThread(m_owner);
    //

	doRun = true;
	AfxBeginThread(RunProc, (LPVOID)this);
}

/**
 * The destructor stops the thread. If the thread has already stoppped, destructor does nothing.
 */
UploadDiskreaderThread::~UploadDiskreaderThread(void) {
    //theApp.QueueDebugLogLine(false, _T("%ui UploadDiskreaderThread destructor. Client: %s"), timeGetTime(), m_owner->DbgGetClientInfo());    

    // stop thread and wait for it to stop
    EndThread();

    delete m_UploadPacketCreaterThread;

    // delete objects
	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();

    delete threadEndedEvent;

    //theApp.QueueDebugLogLine(false, _T("%ui UploadDiskreaderThread destructor done. Client: %s"), timeGetTime(), m_owner->DbgGetClientInfo());    
}

/**
 * Make the thread exit. This method will not return until the thread has stopped
 * looping.
 */
void UploadDiskreaderThread::EndThread() {
    //theApp.QueueDebugLogLine(false, _T("%ui UploadDiskreaderThread EndThread() Client: %s"), timeGetTime(), m_owner->DbgGetClientInfo());    

    // signal the thread to stop looping and exit.
	doRun = false;
    pauseEvent.SetEvent();

	// wait for the thread to signal that it has stopped looping.
	threadEndedEvent->Lock();
    //theApp.QueueDebugLogLine(false, _T("%ui UploadDiskreaderThread EndThread() complete Client: %s"), timeGetTime(), m_owner->DbgGetClientInfo());    
}

UINT AFX_CDECL UploadDiskreaderThread::RunProc(LPVOID pParam) {
	DbgSetThreadName("UploadDiskreaderThread");
	InitThreadLocale();
	UploadDiskreaderThread* uploadDiskreaderThread = (UploadDiskreaderThread*)pParam;

	return uploadDiskreaderThread->RunInternal();
}

UINT UploadDiskreaderThread::RunInternal() {
	while(doRun) {
      CreateNextBlockPackage();
      pauseEvent.Lock(10);
	}

	threadEndedEvent->SetEvent();

	return 0;
}


// --- moved from UploadClient

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 UploadDiskreaderThread::CreateNextBlockPackage() {
    UINT payloadInBuffer = GetPayloadInBuffer();

    //theApp.QueueDebugLogLine(false, _T("%i CUpDownClient::CreateNextBlockPackage() started. Buffer limits: %s-%s Buffered amount: %s Client: %s"), timeGetTime(), CastItoXBytes(m_minSendBufferSize), CastItoXBytes(m_maxSendBufferSize), CastItoXBytes(GetPayloadInBuffer()), 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), CastItoXBytes(GetPayloadInBuffer()), DbgGetClientInfo());
    //}

    // See if we can do an early return. 

    if(payloadInBuffer >= 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), CastItoXBytes(GetPayloadInBuffer()), 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), CastItoXBytes(GetPayloadInBuffer()), DbgGetClientInfo());
        return;
    }

    // Check if current min buffer size and max buffer size are OK.
    if(GetQueueSessionPayloadUp() > 0 && !m_bQueueWasEmptyAfterLastRead) {
        if(m_minSendBufferSize < 10*1024*1024 && (payloadInBuffer == 0)) {
            // buffer was completely eaten since last fillup. This is unwanted.
            m_minSendBufferSize = min(m_minSendBufferSize*2, 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), CastItoXBytes(GetPayloadInBuffer()), DbgGetClientInfo());

            m_dwLastDiskCountTick = ::GetTickCount();
        } else if(::GetTickCount()-m_dwLastDiskCountTick > 10*1000) {
            // more than 10 secs since last fillup. That's what we want.
            m_dwLastDiskCountTick = ::GetTickCount();
        } else if(m_maxSendBufferSize < 10*1024*1024) {
            // it was less than 10 seconds since the buffer needed filling. Try make it at least 10 secs until next fillup is needed.
            m_maxSendBufferSize = min(m_maxSendBufferSize+20*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), CastItoXBytes(GetPayloadInBuffer()), DbgGetClientInfo());

            m_dwLastDiskCountTick = ::GetTickCount();
        }
    }

	byte* filedata = 0;
	CString fullname;
	bool bFromPF = true; // Statistic to breakdown uploaded data by complete file vs. partfile.
	CSyncHelper lockFile;
	try{
        //uint64 totalBytesRead = 0;

        blockLocker.Lock();
        // Buffer new data if current buffer has too little in it
        while (doRun &&
               !m_BlockRequests_queue.IsEmpty() &&
               (GetPayloadInBuffer() < m_maxSendBufferSize)) {

            payloadInBuffer = GetPayloadInBuffer(); // consumer can consume while we are producing. Get current value for this loop.

			Requested_Block_Struct* currentblock = m_BlockRequests_queue.GetHead();
            blockLocker.Unlock();

			CKnownFile* srcfile = theApp.sharedfiles->GetFileByID(currentblock->FileID); // TODO: The access of srcfile is not threadsafe
			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() < 400*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?");
			} 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 - payloadInBuffer; // payloadInBuffer < m_maxSendBufferSize is guaranteed due to loop condition
                    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...

                CFile 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) { // slow
					file.SeekToBegin();
                    file.Read(filedata + done,togo-done); // slow
				}
				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) { // slow
					partfile->m_hpartfile.SeekToBegin();
					partfile->m_hpartfile.Read(filedata + done,togo-done); // slow
				}
			}

            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->FileID, currentblock->StartOffset, currentblock->EndOffset, bFromPF);
			else
                CreateStandartPackets(filedata, togo, currentblock->FileID, currentblock->StartOffset, currentblock->EndOffset, m_currentBlockDone, bFromPF);
			
			// file statistic
			srcfile->statistic.AddTransferred(togo); // TODO: Poll this from main thread?

            m_owner->AddToAddedPayloadQueueSession(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), CastItoXBytes(GetPayloadInBuffer()), DbgGetClientInfo());

            m_bQueueWasEmptyAfterLastRead = false; // may be set to true in the check below

            blockLocker.Lock();

            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), CastItoXBytes(GetPayloadInBuffer()), 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), CastItoXBytes(GetPayloadInBuffer()), DbgGetClientInfo());
                    m_bQueueWasEmptyAfterLastRead = true;
                }
            }
		}
        blockLocker.Unlock();

        //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), CastItoXBytes(GetPayloadInBuffer()), DbgGetClientInfo());
	}
	catch(CString error)
	{
		if (thePrefs.GetVerbose())
			DebugLogWarning(GetResString(IDS_ERR_CLIENTERRORED), GetUserName(), error);
		//theApp.uploadqueue->RemoveFromUploadQueue(m_owner, _T("Client error: ") + error); // TODO: This is probably not thread safe

        m_ExceptionLocker.Lock();
        m_bAnExceptionOccured = true;
        m_ExceptionString = ((CString)_T("Client error: ")) + error;
        m_ExceptionLocker.Unlock();
        
        delete[] filedata;

        doRun = false;

		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(m_owner, ((CString)_T("Failed to create upload package.")) + szError); // TODO: This is probably not thread safe

        m_ExceptionLocker.Lock();
        m_bAnExceptionOccured = true;
        m_ExceptionString = ((CString)_T("Failed to create upload package.")) + szError;
        m_ExceptionLocker.Unlock();

		delete[] filedata;
		e->Delete();

        doRun = false;
        
		return;
	}
}

CString UploadDiskreaderThread::checkException(bool& anExceptionOccured) {
    CString returnString;

    m_ExceptionLocker.Lock();

    anExceptionOccured = m_bAnExceptionOccured;

    if(m_bAnExceptionOccured) {
        returnString = m_ExceptionString;
    }

    m_ExceptionLocker.Unlock();

    return returnString;
}

void UploadDiskreaderThread::CreateStandartPackets(byte* data,uint32 togo, const uchar* FileID, uint64 StartOffset, uint64 EndOffset, uint64 currentBlockDone, bool bFromPF){
    m_UploadPacketCreaterThread->CreateStandartPackets(data, togo, FileID, StartOffset, EndOffset, currentBlockDone, bFromPF, PACKETSPLITSIZE);
}

void UploadDiskreaderThread::CreatePackedPackets(byte* data, uint32 togo, const uchar* FileID, uint64 StartOffset, uint64 EndOffset, bool bFromPF){
    m_UploadPacketCreaterThread->CreatePackedPackets(data, togo, FileID, StartOffset, EndOffset, bFromPF, PACKETSPLITSIZE);
}

void UploadDiskreaderThread::AddReqBlock(Requested_Block_Struct* reqblock)
{
    blockLocker.Lock();

    for (POSITION pos = m_DoneBlocks_list.GetHeadPosition(); pos != 0; ){
        const Requested_Block_Struct* cur_reqblock = m_DoneBlocks_list.GetNext(pos);
        if (reqblock->StartOffset == cur_reqblock->StartOffset && reqblock->EndOffset == cur_reqblock->EndOffset && md4cmp(reqblock->FileID, cur_reqblock->FileID) == 0){
            delete reqblock;
            blockLocker.Unlock();
            return;
        }
    }
    for (POSITION pos = m_BlockRequests_queue.GetHeadPosition(); pos != 0; ){
        const Requested_Block_Struct* cur_reqblock = m_BlockRequests_queue.GetNext(pos);
        if (reqblock->StartOffset == cur_reqblock->StartOffset && reqblock->EndOffset == cur_reqblock->EndOffset && md4cmp(reqblock->FileID, cur_reqblock->FileID) == 0){
            delete reqblock;
            blockLocker.Unlock();
            return;
        }
    }

    m_BlockRequests_queue.AddTail(reqblock);
    //theApp.QueueDebugLogLine(false, _T("%i UploadClient: Added req block. Now %i blocks in queue. Client: %s"), timeGetTime(), m_BlockRequests_queue.GetCount(), DbgGetClientInfo());

    blockLocker.Unlock();

    // make the reader thread wake up at once, if it's sleeping
    pauseEvent.SetEvent();
}

CBarShader UploadDiskreaderThread::s_UpStatusBar(16);

void UploadDiskreaderThread::DrawUpStatusBar(CBarShader& s_UpStatusBar, UINT sessionPayloadUp, COLORREF crNextSending, COLORREF crSent, COLORREF crSending, COLORREF crBuffer)
{
    blockLocker.Lock();

 	const Requested_Block_Struct* block;
	if (!m_BlockRequests_queue.IsEmpty()){
		block = m_BlockRequests_queue.GetHead();
		if(block){
			uint32 start = (uint32)(block->StartOffset/PARTSIZE);
			s_UpStatusBar.FillRange((uint64)start*PARTSIZE, (uint64)(start+1)*PARTSIZE, crNextSending);
		}
	}
	if (!m_DoneBlocks_list.IsEmpty()){
		block = m_DoneBlocks_list.GetHead();
		if(block){
			uint32 start = (uint32)(block->StartOffset/PARTSIZE);
			s_UpStatusBar.FillRange((uint64)start*PARTSIZE, (uint64)(start+1)*PARTSIZE, crNextSending);
		}
	}
	if (!m_DoneBlocks_list.IsEmpty()){
		for(POSITION pos=m_DoneBlocks_list.GetHeadPosition();pos!=0;){
			block = m_DoneBlocks_list.GetNext(pos);
			s_UpStatusBar.FillRange(block->StartOffset, block->EndOffset + 1, crSent);
		}

        // Also show what data is buffered (with color crBuffer)
        uint64 total = 0;

		for(POSITION pos=m_DoneBlocks_list.GetTailPosition();pos!=0; ){
			Requested_Block_Struct* block = m_DoneBlocks_list.GetPrev(pos);

            if(total + (block->EndOffset-block->StartOffset) <= sessionPayloadUp) {
                // block is sent
			    s_UpStatusBar.FillRange(block->StartOffset, block->EndOffset, crSent);
                total += block->EndOffset-block->StartOffset;
            }
            else if (total < sessionPayloadUp){
                // block partly sent, partly in buffer
                total += block->EndOffset-block->StartOffset;
                uint64 rest = total - sessionPayloadUp;
                uint64 newEnd = block->EndOffset-rest;

    			s_UpStatusBar.FillRange(block->StartOffset, newEnd, crSending);
    			s_UpStatusBar.FillRange(newEnd, block->EndOffset, crBuffer);
            }
            else{
                // entire block is still in buffer
                total += block->EndOffset-block->StartOffset;
    			s_UpStatusBar.FillRange(block->StartOffset, block->EndOffset, crBuffer);
            }
		}
	}

    blockLocker.Unlock();
} 

void UploadDiskreaderThread::DrawUpStatusBarChunk(CBarShader& s_UpStatusBar, CDC* dc, RECT* rect, EMFileSize filesize, bool bFlat, UINT sessionPayloadUp, COLORREF crNextSending, COLORREF crSent, COLORREF crSending, COLORREF crBuffer, COLORREF crNeither)
{
    blockLocker.Lock();

    if (!m_BlockRequests_queue.IsEmpty() || !m_DoneBlocks_list.IsEmpty()) {
		uint32 cur_chunk = (uint32)-1;
		uint64 start = (uint64)-1;
		uint64 end = (uint64)-1;
		const Requested_Block_Struct* block;
		if (!m_DoneBlocks_list.IsEmpty()){
		    block = m_DoneBlocks_list.GetHead();
			if (cur_chunk == (uint32)-1) {
				cur_chunk = (uint32)(block->StartOffset/PARTSIZE);
				start = end = cur_chunk*PARTSIZE;
				end += PARTSIZE-1;
				s_UpStatusBar.SetFileSize(PARTSIZE);
				s_UpStatusBar.SetHeight(rect->bottom - rect->top); 
				s_UpStatusBar.SetWidth(rect->right - rect->left); 
				if (end > filesize) {
					end = filesize;
					s_UpStatusBar.Reset();
					s_UpStatusBar.Fill(RGB(255, 255, 255));
					s_UpStatusBar.FillRange(0, end%PARTSIZE, crNeither);
				} else
					s_UpStatusBar.Fill(crNeither);
			}
		}
		if (!m_BlockRequests_queue.IsEmpty()){
			for(POSITION pos=m_BlockRequests_queue.GetHeadPosition();pos!=0;){
				block = m_BlockRequests_queue.GetNext(pos);
			    if (cur_chunk == (uint32)-1) {
				    cur_chunk = (uint32)(block->StartOffset/PARTSIZE);
				    start = end = cur_chunk*PARTSIZE;
				    end += PARTSIZE-1;
				    s_UpStatusBar.SetFileSize(PARTSIZE);
				    s_UpStatusBar.SetHeight(rect->bottom - rect->top); 
				    s_UpStatusBar.SetWidth(rect->right - rect->left); 
				    if (end > filesize) {
					    end = filesize;
					    s_UpStatusBar.Reset();
    					s_UpStatusBar.Fill(RGB(255, 255, 255));
					    s_UpStatusBar.FillRange(0, end%PARTSIZE, crNeither);
				    } else
					    s_UpStatusBar.Fill(crNeither);
			    }

				if (block->StartOffset <= end && block->EndOffset >= start) {
					s_UpStatusBar.FillRange((block->StartOffset > start)?block->StartOffset%PARTSIZE:(uint64)0, ((block->EndOffset < end)?block->EndOffset+1:end)%PARTSIZE, crNextSending);
				}
			}
		}
		
		// Also show what data is buffered (with color crBuffer)
        uint64 total = 0;
    
		if (!m_DoneBlocks_list.IsEmpty() && cur_chunk != (uint32)-1){
		    for(POSITION pos=m_DoneBlocks_list.GetTailPosition();pos!=0; ){
			    block = m_DoneBlocks_list.GetPrev(pos);
				if (block->StartOffset <= end && block->EndOffset >= start) {
					if(total + (block->EndOffset-block->StartOffset) <= sessionPayloadUp) {
						// block is sent
						s_UpStatusBar.FillRange((block->StartOffset > start)?block->StartOffset%PARTSIZE:(uint64)0, ((block->EndOffset < end)?block->EndOffset+1:end)%PARTSIZE, crSent);
						total += block->EndOffset-block->StartOffset;
					}
					else if (total < sessionPayloadUp){
						// block partly sent, partly in buffer
						total += block->EndOffset-block->StartOffset;
						uint64 rest = total -  sessionPayloadUp;
						uint64 newEnd = (block->EndOffset-rest);
						if (newEnd>=start) {
							if (newEnd<=end) {
								uint64 uNewEnd = newEnd%PARTSIZE;
								s_UpStatusBar.FillRange(block->StartOffset%PARTSIZE, uNewEnd, crSending);
								if (block->EndOffset <= end)
									s_UpStatusBar.FillRange(uNewEnd, block->EndOffset%PARTSIZE, crBuffer);
								else
									s_UpStatusBar.FillRange(uNewEnd, end%PARTSIZE, crBuffer);
							} else 
								s_UpStatusBar.FillRange(block->StartOffset%PARTSIZE, end%PARTSIZE, crSending);
						} else if (block->EndOffset <= end)
							s_UpStatusBar.FillRange((uint64)0, block->EndOffset%PARTSIZE, crBuffer);
					}
					else{
						// entire block is still in buffer
						total += block->EndOffset-block->StartOffset;
						s_UpStatusBar.FillRange((block->StartOffset>start)?block->StartOffset%PARTSIZE:(uint64)0, ((block->EndOffset < end)?block->EndOffset:end)%PARTSIZE, crBuffer);
					}
				} else
					total += block->EndOffset-block->StartOffset;
		    }
	    }

        if(!m_BlockRequests_queue.IsEmpty() && m_currentBlockDone > 0) {
            block = m_BlockRequests_queue.GetHead();
            uint64 endOffset = block->StartOffset+m_currentBlockDone;
			if (block->StartOffset <= end && endOffset >= start) {
				if(total + m_currentBlockDone <= sessionPayloadUp) {
					// block is sent
					s_UpStatusBar.FillRange((block->StartOffset > start)?block->StartOffset%PARTSIZE:(uint64)0, ((endOffset < end)?endOffset+1:end)%PARTSIZE, crSent);
					total += m_currentBlockDone;
				}
				else if (total < sessionPayloadUp){
					// block partly sent, partly in buffer
					total += m_currentBlockDone;
					uint64 rest = total -  sessionPayloadUp;
					uint64 newEnd = (endOffset-rest);
					if (newEnd>=start) {
						if (newEnd<=end) {
							uint64 uNewEnd = newEnd%PARTSIZE;
							s_UpStatusBar.FillRange(block->StartOffset%PARTSIZE, uNewEnd, crSending);
							if (endOffset <= end)
								s_UpStatusBar.FillRange(uNewEnd, endOffset%PARTSIZE, crBuffer);
							else
								s_UpStatusBar.FillRange(uNewEnd, end%PARTSIZE, crBuffer);
						} else 
							s_UpStatusBar.FillRange(block->StartOffset%PARTSIZE, end%PARTSIZE, crSending);
					} else if (endOffset <= end)
						s_UpStatusBar.FillRange((uint64)0, endOffset%PARTSIZE, crBuffer);
				}
				else{
					// entire block is still in buffer
					total += m_currentBlockDone;
					s_UpStatusBar.FillRange((block->StartOffset>start)?block->StartOffset%PARTSIZE:(uint64)0, ((endOffset < end)?endOffset:end)%PARTSIZE, crBuffer);
				}
			} else
				total += m_currentBlockDone;
        }

        s_UpStatusBar.Draw(dc, rect->left, rect->top, bFlat);
        
		s_UpStatusBar.SetHeight(3); 
		s_UpStatusBar.SetWidth(1); 
		s_UpStatusBar.SetFileSize((uint64)1);
		s_UpStatusBar.Fill(crNeither);
		uint32	w=rect->right-rect->left+1;

        if (!m_BlockRequests_queue.IsEmpty()){
			for(POSITION pos=m_BlockRequests_queue.GetHeadPosition();pos!=0;){
				block = m_BlockRequests_queue.GetNext(pos);
				if (block->StartOffset <= end && block->EndOffset >= start) {
					if (block->StartOffset >= start) {
						if (block->EndOffset <= end) {
							s_UpStatusBar.Draw(dc,rect->left+(int)((double)(block->StartOffset%PARTSIZE)*w/PARTSIZE), rect->top, bFlat);
							s_UpStatusBar.Draw(dc,rect->left+(int)((double)(block->EndOffset%PARTSIZE)*w/PARTSIZE), rect->top, bFlat);
						} else
							s_UpStatusBar.Draw(dc,rect->left+(int)((double)(block->StartOffset%PARTSIZE)*w/PARTSIZE), rect->top, bFlat);
					} else if (block->EndOffset <= end)
						s_UpStatusBar.Draw(dc,rect->left+(int)((double)(block->EndOffset%PARTSIZE)*w/PARTSIZE), rect->top, bFlat);
				}
			}
		}
		if (!m_DoneBlocks_list.IsEmpty()){
			for(POSITION pos=m_DoneBlocks_list.GetHeadPosition();pos!=0;){
				block = m_DoneBlocks_list.GetNext(pos);
				if (block->StartOffset <= end && block->EndOffset >= start) {
					if (block->StartOffset >= start) {
						if (block->EndOffset <= end) {
							s_UpStatusBar.Draw(dc,rect->left+(int)((double)(block->StartOffset%PARTSIZE)*w/PARTSIZE), rect->top, bFlat);
							s_UpStatusBar.Draw(dc,rect->left+(int)((double)(block->EndOffset%PARTSIZE)*w/PARTSIZE), rect->top, bFlat);
						} else
							s_UpStatusBar.Draw(dc,rect->left+(int)((double)(block->StartOffset%PARTSIZE)*w/PARTSIZE), rect->top, bFlat);
					} else if (block->EndOffset <= end)
						s_UpStatusBar.Draw(dc,rect->left+(int)((double)(block->EndOffset%PARTSIZE)*w/PARTSIZE), rect->top, bFlat);
				}
			}
		}
    }

    blockLocker.Unlock();
}

CString			UploadDiskreaderThread::DbgGetClientInfo(bool bFormatIP) const          { return m_owner->DbgGetClientInfo(bFormatIP); }
UINT			UploadDiskreaderThread::GetPayloadInBuffer() const					    { return m_owner->GetPayloadInBuffer(); }
UINT			UploadDiskreaderThread::GetQueueSessionPayloadUp() const			    { return m_owner->GetQueueSessionPayloadUp(); }
INT				UploadDiskreaderThread::GetDatarate() const							    { return m_owner->GetDatarate(); }	
bool            UploadDiskreaderThread::IsUploadingToPeerCache() const                  { return m_owner->IsUploadingToPeerCache(); }
LPCTSTR			UploadDiskreaderThread::GetUserName() const								{ return m_owner->GetUserName(); }
void			UploadDiskreaderThread::SetUploadFileID(CKnownFile* newreqfile)         { return m_owner->SetUploadFileID(newreqfile); }
