//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 <math.h>
#include <Mmsystem.h>
#include "emule.h"
#include "UploadBandwidthThrottler.h"
#include "EMSocket.h"
#include "opcodes.h"
#include "LastCommonRouteFinder.h"
#include "OtherFunctions.h"
#include "emuledlg.h"
#include "uploadqueue.h"
#include "preferences.h"

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


/**
 * The constructor starts the thread.
 */
UploadBandwidthThrottler::UploadBandwidthThrottler(void) {
    //MORPH - Changed by SiRoB, Upload Splitting Class
	/*
	m_SentBytesSinceLastCall = 0;
	m_SentBytesSinceLastCallOverhead = 0;
    m_highestNumberOfFullyActivatedSlots = 0;
	*/
	memset(m_SentBytesSinceLastCallClass,0,sizeof(m_SentBytesSinceLastCallClass));
	memset(m_SentBytesSinceLastCallOverheadClass,0,sizeof(m_SentBytesSinceLastCallOverheadClass));
	memset(m_highestNumberOfFullyActivatedSlotsClass,0,sizeof(m_highestNumberOfFullyActivatedSlotsClass));
	memset(slotCounterClass,0,sizeof(slotCounterClass));
		
	threadEndedEvent = new CEvent(0, 1);
    pauseEvent = new CEvent(TRUE, TRUE);

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

/**
 * The destructor stops the thread. If the thread has already stoppped, destructor does nothing.
 */
UploadBandwidthThrottler::~UploadBandwidthThrottler(void) {
    EndThread();
    delete threadEndedEvent;
	delete pauseEvent;
}

void UploadBandwidthThrottler::GetStats(uint64* SentBytes, uint64* SentBytesOverhead, uint32* HighestNumberOfFullyActivatedSlotsSinceLastCallClass) {
    sendLocker.Lock();

	for (uint8 i = 0; i < NB_SPLITTING_CLASS; i++) {
		*(SentBytes++) = m_SentBytesSinceLastCallClass[i];
		m_SentBytesSinceLastCallClass[i] = 0;
		*(SentBytesOverhead++) = m_SentBytesSinceLastCallOverheadClass[i];
		m_SentBytesSinceLastCallOverheadClass[i] = 0;
		*(HighestNumberOfFullyActivatedSlotsSinceLastCallClass++) = m_highestNumberOfFullyActivatedSlotsClass[i];
		m_highestNumberOfFullyActivatedSlotsClass[i] = (UINT)0;
	}

	sendLocker.Unlock();
}

/**
 * Find out how many bytes that has been put on the sockets since the last call to this
 * method. Excludes overhead of control packets.
 *
 * @return the number of bytes that has been put on the sockets since the last call
 */
//MORPH - Removed by SiRoB, See above
/*
uint64 UploadBandwidthThrottler::GetNumberOfSentBytesOverheadSinceLastCallAndReset() {
    sendLocker.Lock();
    
	uint64 numberOfSentBytesSinceLastCall = m_SentBytesSinceLastCallOverhead;
	m_SentBytesSinceLastCallOverhead = 0;

    sendLocker.Unlock();

    return numberOfSentBytesSinceLastCall;
}
*/

/**
 * Find out the highest number of slots that has been fed data in the normal standard loop
 * of the thread since the last call of this method. This means all slots that haven't
 * been in the trickle state during the entire time since the last call.
 *
 * @return the highest number of fully activated slots during any loop since last call
 */
/*void UploadBandwidthThrottler::GetHighestNumberOfFullyActivatedSlotsSinceLastCallAndReset(uint32 * HighestNumberOfFullyActivatedSlotsSinceLastCallClass) {
    sendLocker.Lock();
    
    //if(m_highestNumberOfFullyActivatedSlots > (uint32)m_StandardOrder_list.GetSize()) {
    //    theApp.QueueDebugLogLine(true, _T("UploadBandwidthThrottler: Throttler wants new slot when get-method called. m_highestNumberOfFullyActivatedSlots: %i m_StandardOrder_list.GetSize(): %i tick: %i"), m_highestNumberOfFullyActivatedSlots, m_StandardOrder_list.GetSize(), ::GetTickCount());
    //}
GetStats(
    	for (uint8 i = 0; i < NB_SPLITTING_CLASS; i++) {
		*(HighestNumberOfFullyActivatedSlotsSinceLastCallClass++) = m_highestNumberOfFullyActivatedSlotsClass[i];
		m_highestNumberOfFullyActivatedSlotsClass[i] = (UINT)0;
	}
	sendLocker.Unlock();
}
*/
/**
 * Add a socket to the list of sockets that have upload slots. The main thread will
 * continously call send on these sockets, to give them chance to work off their queues.
 * The sockets are called in the order they exist in the list, so the top socket (index 0)
 * will be given a chance first to use bandwidth, and then the next socket (index 1) etc.
 *
 * It is possible to add a socket several times to the list without removing it inbetween,
 * but that should be avoided.
 *
 * @param index insert the socket at this place in the list. An index that is higher than the
 *              current number of sockets in the list will mean that the socket should be inserted
 *              last in the list.
 *
 * @param socket the address to the socket that should be added to the list. If the address is NULL,
 *               this method will do nothing.
 */
void UploadBandwidthThrottler::AddToStandardList(uint32 index, ThrottledFileSocket* socket, uint32 classID, bool scheduled) {
    if(socket != NULL) {
        sendLocker.Lock();

		RemoveFromStandardListNoLock(socket);
		if(index > (uint32)m_StandardOrder_list.GetSize()) {
			index = m_StandardOrder_list.GetSize();
		}
		m_StandardOrder_list.InsertAt(index, socket);
		//MORPH START - Added by SiRoB & AndCycle, Upload Splitting Class
		Socket_stat* cur_socket_stat = NULL;
		if (!m_stat_list.Lookup(socket,cur_socket_stat)){
			cur_socket_stat = new Socket_stat;
			m_stat_list.SetAt(socket,cur_socket_stat);
			cur_socket_stat->realBytesToSpend = _I64_MAX;
			cur_socket_stat->dwLastBusySince = 0;
		}
		cur_socket_stat->scheduled = scheduled;
		cur_socket_stat->classID = classID;
		++slotCounterClass[classID];
		//MORPH END - Added by SiRoB & AndCycle, Upload Splitting Class

        sendLocker.Unlock();
//	} else {
//		if (thePrefs.GetVerbose())
//		DebugLogError(LOG_STATUSBAR,_T("Tried to add NULL socket to UploadBandwidthThrottler Standard list! Prevented."));
    }
}

/**
 * Remove a socket from the list of sockets that have upload slots.
 *
 * If the socket has mistakenly been added several times to the list, this method
 * will return all of the entries for the socket.
 *
 * @param socket the address of the socket that should be removed from the list. If this socket
 *               does not exist in the list, this method will do nothing.
 */
bool UploadBandwidthThrottler::RemoveFromStandardList(ThrottledFileSocket* socket, bool resort) {
	bool returnValue;
    sendLocker.Lock();

	returnValue = RemoveFromStandardListNoLock(socket);
	if (returnValue && !resort)
		QueueForSendingControlPacket(socket);
	
    sendLocker.Unlock();

    return returnValue;
}

/**
 * Remove a socket from the list of sockets that have upload slots. NOT THREADSAFE!
 * This is an internal method that doesn't take the necessary lock before it removes
 * the socket. This method should only be called when the current thread already owns
 * the sendLocker lock!
 *
 * @param socket address of the socket that should be removed from the list. If this socket
 *               does not exist in the list, this method will do nothing.
 */
bool UploadBandwidthThrottler::RemoveFromStandardListNoLock(ThrottledFileSocket* socket) {
	// Find the slot
    int slotCounter = 0;
    bool foundSocket = false;
	uint32 classID = NULL;
	while(slotCounter < m_StandardOrder_list.GetSize() && foundSocket == false) {
        if(m_StandardOrder_list.GetAt(slotCounter) == socket) {
			// Remove the slot
            m_StandardOrder_list.RemoveAt(slotCounter);
			// Remove the slot
			Socket_stat* stat = NULL;
			m_stat_list.Lookup(socket,stat);
			classID = stat->classID;
			--slotCounterClass[classID];
			foundSocket = true;
        } else {
            slotCounter++;
        }
    }

	//MORPH START - Added by SiRoB, Upload Splitting Class
	if(foundSocket) {
		if (m_highestNumberOfFullyActivatedSlotsClass[classID] > slotCounterClass[classID])
			m_highestNumberOfFullyActivatedSlotsClass[classID] = slotCounterClass[classID];
	}
	//MORPH END  - Added by SiRoB, Upload Splitting Class

	return foundSocket;
}

void UploadBandwidthThrottler::SignalNoLongerBusy() {
    busyEvent.SetEvent();
}

/**
* Notifies the send thread that it should try to call controlpacket send
* for the supplied socket. It is allowed to call this method several times
* for the same socket, without having controlpacket send called for the socket
* first. The doublette entries are never filtered, since it is incurs less cpu
* overhead to simply call Send() in the socket for each double. Send() will
* already have done its work when the second Send() is called, and will just
* return with little cpu overhead.
*
* @param socket address to the socket that requests to have controlpacket send
*               to be called on it
*/
void UploadBandwidthThrottler::QueueForSendingControlPacket(ThrottledControlSocket* socket, bool hasSent) {
	// Get critical section
    tempQueueLocker.Lock();

    if(doRun) {
        if(hasSent) {
            m_TempControlQueueFirst_list.AddTail(socket);
        } else {
        	m_TempControlQueue_list.AddTail(socket);
    	}
    }

	// End critical section
    tempQueueLocker.Unlock();
}

/**
 * Remove the socket from all lists and queues. This will make it safe to
 * erase/delete the socket. It will also cause the main thread to stop calling
 * send() for the socket.
 *
 * @param socket address to the socket that should be removed
 */
void UploadBandwidthThrottler::RemoveFromAllQueues(ThrottledControlSocket* socket, bool lock) {
    if(lock) {
		// Get critical section
   		sendLocker.Lock();
    }

    if(doRun) {
        // Remove this socket from control packet queue
        {
            POSITION pos1, pos2;
	        for (pos1 = m_ControlQueue_list.GetHeadPosition();( pos2 = pos1 ) != NULL;) {
		        m_ControlQueue_list.GetNext(pos1);
		        ThrottledControlSocket* socketinQueue = m_ControlQueue_list.GetAt(pos2);

                if(socketinQueue == socket) {
                    m_ControlQueue_list.RemoveAt(pos2);
                }
            }
        }
        
        {
            POSITION pos1, pos2;
	        for (pos1 = m_ControlQueueFirst_list.GetHeadPosition();( pos2 = pos1 ) != NULL;) {
		        m_ControlQueueFirst_list.GetNext(pos1);
		        ThrottledControlSocket* socketinQueue = m_ControlQueueFirst_list.GetAt(pos2);

                if(socketinQueue == socket) {
                    m_ControlQueueFirst_list.RemoveAt(pos2);
                }
            }
        }

        tempQueueLocker.Lock();
        {
            POSITION pos1, pos2;
	        for (pos1 = m_TempControlQueue_list.GetHeadPosition();( pos2 = pos1 ) != NULL;) {
		        m_TempControlQueue_list.GetNext(pos1);
		        ThrottledControlSocket* socketinQueue = m_TempControlQueue_list.GetAt(pos2);

                if(socketinQueue == socket) {
                    m_TempControlQueue_list.RemoveAt(pos2);
                }
            }
        }

        {
            POSITION pos1, pos2;
	        for (pos1 = m_TempControlQueueFirst_list.GetHeadPosition();( pos2 = pos1 ) != NULL;) {
		        m_TempControlQueueFirst_list.GetNext(pos1);
		        ThrottledControlSocket* socketinQueue = m_TempControlQueueFirst_list.GetAt(pos2);

                if(socketinQueue == socket) {
                    m_TempControlQueueFirst_list.RemoveAt(pos2);
                }
            }
        }
        tempQueueLocker.Unlock();


    }

    if(lock) {
		// End critical section
        sendLocker.Unlock();
    }
}

void UploadBandwidthThrottler::RemoveFromAllQueues(ThrottledFileSocket* socket) {
	// Get critical section
    sendLocker.Lock();

    if(doRun) {
        RemoveFromAllQueues(socket, false);

		// And remove it from upload slots
        RemoveFromStandardListNoLock(socket);

		//MORPH START - Added by SiRoB, Upload Splitting Class
		Socket_stat* stat = NULL;
		if (m_stat_list.Lookup(socket,stat)) {
			delete stat;
			m_stat_list.RemoveKey(socket);
		}
		//MORPH END   - Added by SiRoB, Upload Splitting Class
    }

	// End critical section
    sendLocker.Unlock();
}

/**
 * Make the thread exit. This method will not return until the thread has stopped
 * looping. This guarantees that the thread will not access the CEMSockets after this
 * call has exited.
 */
void UploadBandwidthThrottler::EndThread() {
    sendLocker.Lock();

	// signal the thread to stop looping and exit.
    doRun = false;

    sendLocker.Unlock();

    Pause(false);

	// wait for the thread to signal that it has stopped looping.
    threadEndedEvent->Lock();
}

void UploadBandwidthThrottler::Pause(bool paused) {
    if(paused) {
        pauseEvent->ResetEvent();
    } else {
        pauseEvent->SetEvent();
    }
}

uint32 UploadBandwidthThrottler::GetSlotLimit(uint32 currentUpSpeed) {
    uint32 upPerClient = UPLOAD_CLIENT_DATARATE;

    // if throttler doesn't require another slot, go with a slightly more restrictive method
	if( currentUpSpeed > 20*1024 )
		upPerClient += currentUpSpeed/43;

	if( upPerClient > 7680 )
		upPerClient = 7680;

	//now the final check

	uint16 nMaxSlots;
	if (currentUpSpeed > 12*1024)
		nMaxSlots = (uint16)(((float)currentUpSpeed) / upPerClient);
	else if (currentUpSpeed > 7*1024)
		nMaxSlots = MIN_UP_CLIENTS_ALLOWED + 2;
	else if (currentUpSpeed > 3*1024)
		nMaxSlots = MIN_UP_CLIENTS_ALLOWED + 1;
	else
		nMaxSlots = MIN_UP_CLIENTS_ALLOWED;

    return max(nMaxSlots, MIN_UP_CLIENTS_ALLOWED);
}

uint32 UploadBandwidthThrottler::CalculateChangeDelta(uint32 numberOfConsecutiveChanges) const {
    switch(numberOfConsecutiveChanges) {
        case 0: return 50;
        case 1: return 50;
        case 2: return 128;
        case 3: return 256;
        case 4: return 512;
        case 5: return 512+256;
        case 6: return 1*1024;
        case 7: return 1*1024+256;
        default: return 1*1024+512;
    }
}

/**
 * Start the thread. Called from the constructor in this class.
 *
 * @param pParam
 *
 * @return
 */
UINT AFX_CDECL UploadBandwidthThrottler::RunProc(LPVOID pParam) {
	DbgSetThreadName("UploadBandwidthThrottler");

	// SLUGFILLER: SafeHash
	CReadWriteLock lock(&theApp.m_threadlock);
	if (!lock.ReadLock(0))
		return 0;
	// SLUGFILLER: SafeHash

	InitThreadLocale();

    UploadBandwidthThrottler* uploadBandwidthThrottler = (UploadBandwidthThrottler*)pParam;

    return uploadBandwidthThrottler->RunInternal();
}

/**
 * The thread method that handles calling send for the individual sockets.
 *
 * Control packets will always be tried to be sent first. If there is any bandwidth leftover
 * after that, send() for the upload slot sockets will be called in priority order until we have run
 * out of available bandwidth for this loop. Upload slots will not be allowed to go without having sent
 * called for more than a defined amount of time (i.e. two seconds).
 *
 * @return always returns 0.
 */
UINT UploadBandwidthThrottler::RunInternal() {
	DWORD lastLoopTick = timeGetTime();
	//MORPH START - Added by SiRoB, Upload Splitting Class
	DWORD lastLoopTickTryTosend = lastLoopTick;
	uint32 allowedDataRateClass[NB_SPLITTING_CLASS];
	uint32 ClientDataRate[NB_SPLITTING_CLASS];
	sint64 realBytesToSpendClass[NB_SPLITTING_CLASS];
	memset(realBytesToSpendClass,0,sizeof(realBytesToSpendClass));
	//MORPH END   - Added by SiRoB, Upload Splitting Class
	uint32 nEstiminatedLimit = 0;
	int nSlotsBusyLevel = 0;
	DWORD nUploadStartTime = 0;
    uint32 numberOfConsecutiveUpChanges = 0;
    uint32 numberOfConsecutiveDownChanges = 0;
    uint32 changesCount = 0;
    uint32 loopsCount = 0;

    //bool estimateChangedLog = false;
	bool estimateChangedLog = true;
    bool lotsOfLog = false; 

    while(doRun) {
        pauseEvent->Lock();

		theApp.lastCommonRouteFinder->GetClassByteToSend(allowedDataRateClass,ClientDataRate);

		// check busy level for all the slots (WSAEWOULDBLOCK status)
		uint32 cBusy = 0;
		uint32 nCanSend = 0;
		DWORD  cBusyTime = 0;
		DWORD timeSinceLastLoop = timeGetTime() - lastLoopTick;
		sendLocker.Lock();
		/*
		for (int i = 0; i < m_StandardOrder_list.GetSize() && (i < 3 || (UINT)i < GetSlotLimit(theApp.uploadqueue->GetDatarate())); i++){
		*/
		for (int i = m_StandardOrder_list.GetSize()/2; i < m_StandardOrder_list.GetSize(); i++){
			Socket_stat* stat = NULL;
			if (m_stat_list.Lookup(m_StandardOrder_list[i], stat)) {
				if (m_StandardOrder_list[i] != NULL && m_StandardOrder_list[i]->HasQueues() && stat->scheduled == false) {
					nCanSend++;
				
					DWORD newBusySince = m_StandardOrder_list[i]->GetBusyTimeSince();
					if(newBusySince > 0) {
						cBusy++;
						if (newBusySince == stat->dwLastBusySince)
							cBusyTime += timeSinceLastLoop;
						else
							cBusyTime += timeGetTime() - newBusySince;
						stat->dwLastBusySince = newBusySince;
					}
				}
			}
		}
		sendLocker.Unlock();

/*
		// if this is kept, the loop above can be a little optimized (don't count nCanSend, just use nCanSend = GetSlotLimit(theApp.uploadqueue->GetDatarate())
		if(theApp.uploadqueue) {
			nCanSend = min(nCanSend, GetSlotLimit(theApp.uploadqueue->GetDatarate()));
			//nCanSend = min(max(GetSlotLimit(theApp.uploadqueue->GetDatarate()),1), (UINT)m_StandardOrder_list.GetSize());
		}
*/
		bool bUploadUnlimited = (thePrefs.GetMaxUpload() == UNLIMITED);
		// When no upload limit has been set in options, try to guess a good upload limit.
		if (bUploadUnlimited) {
			if (timeSinceLastLoop > 0) {
				loopsCount++;

				//if(lotsOfLog) theApp.QueueDebugLogLine(false,_T("Throttler: busy: %i/%i nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount: %i loopsCount: %i"), cBusy, nCanSend, nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);

				if(nCanSend > 0) {
					float fBusyPercent = ((float)cBusyTime/(float)nCanSend/(float)timeSinceLastLoop) * 100;
					if (cBusy > 2 && fBusyPercent > 75.00f && nSlotsBusyLevel < 255){
						nSlotsBusyLevel+=cBusyTime;
						changesCount+=cBusyTime;
						if(thePrefs.GetVerbose() && lotsOfLog && nSlotsBusyLevel%25==0) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount: %i loopsCount: %i"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);
					}
					else if ((cBusy <= 2 || fBusyPercent < 25.00f) && nSlotsBusyLevel > (-255)){
						nSlotsBusyLevel-=timeSinceLastLoop;
						changesCount+=timeSinceLastLoop;
						if(thePrefs.GetVerbose() && lotsOfLog && nSlotsBusyLevel%25==0) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount %i loopsCount: %i"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);
					}
				}

				if(nUploadStartTime == 0) {
					if (m_StandardOrder_list.GetSize() >= 3)
						nUploadStartTime = timeGetTime();
				} else if(timeGetTime()- nUploadStartTime > SEC2MS(60)) {
					if (theApp.uploadqueue){
						if (nEstiminatedLimit == 0){ // no autolimit was set yet
							if (nSlotsBusyLevel >= 250){ // sockets indicated that the BW limit has been reached
								nEstiminatedLimit = theApp.uploadqueue->GetDatarate();
								allowedDataRateClass[LAST_CLASS] = min(nEstiminatedLimit, allowedDataRateClass[LAST_CLASS]);
						    nSlotsBusyLevel = -200;
								if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: Set inital estimated limit to %0.5f changesCount: %i loopsCount: %i"), (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);
								changesCount = 0;
								loopsCount = 0;
							}
						}
						else{
							if (nSlotsBusyLevel > 250){
								if(changesCount > 500 || changesCount > 300 && loopsCount > 1000 || loopsCount > 2000) {
									numberOfConsecutiveDownChanges = 0;
								}
								numberOfConsecutiveDownChanges+=timeSinceLastLoop;
								uint32 changeDelta = CalculateChangeDelta(numberOfConsecutiveDownChanges);

								// Don't lower speed below 1 KBytes/s
								// leuk_he don't move below minupload
								if(nEstiminatedLimit < changeDelta + 1024*thePrefs.minupload) {
									if(nEstiminatedLimit > 1024*thePrefs.minupload) {
										changeDelta = nEstiminatedLimit - 1024*thePrefs.minupload;
									} else {
										changeDelta = 0;
									}
								}
								ASSERT(nEstiminatedLimit >= changeDelta + 1024*thePrefs.minupload);
    							nEstiminatedLimit -= changeDelta;

								if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: REDUCED limit #%i with %i bytes to: %0.5f changesCount: %i loopsCount: %i"), numberOfConsecutiveDownChanges, changeDelta, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);

								numberOfConsecutiveUpChanges = 0;
								nSlotsBusyLevel = 0;
								changesCount = 0;
								loopsCount = 0;
							}
							else if (nSlotsBusyLevel < (-250)){
								if(changesCount > 500 || changesCount > 300 && loopsCount > 1000 || loopsCount > 2000) {
									numberOfConsecutiveUpChanges = 0;
								}
								numberOfConsecutiveUpChanges+=timeSinceLastLoop;
								uint32 changeDelta = CalculateChangeDelta(numberOfConsecutiveUpChanges);

								// Don't raise speed unless we are under current allowedDataRate
								if(nEstiminatedLimit+changeDelta > allowedDataRateClass[LAST_CLASS]) {
									if(nEstiminatedLimit < allowedDataRateClass[LAST_CLASS]) {
										changeDelta = allowedDataRateClass[LAST_CLASS] - nEstiminatedLimit;
									} else {
										changeDelta = 0;
									}
								}
								ASSERT(nEstiminatedLimit < allowedDataRateClass[LAST_CLASS] && nEstiminatedLimit+changeDelta <= allowedDataRateClass[LAST_CLASS] || nEstiminatedLimit >= allowedDataRateClass[LAST_CLASS] && changeDelta == 0);
								nEstiminatedLimit += changeDelta;

								if(thePrefs.GetVerbose() && estimateChangedLog) theApp.QueueDebugLogLine(false,_T("Throttler: INCREASED limit #%i with %i bytes to: %0.5f changesCount: %i loopsCount: %i"), numberOfConsecutiveUpChanges, changeDelta, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);

								numberOfConsecutiveDownChanges = 0;
								nSlotsBusyLevel = 0;
								changesCount = 0;
								loopsCount = 0;
							}

							allowedDataRateClass[LAST_CLASS] = min(nEstiminatedLimit, allowedDataRateClass[LAST_CLASS]);
						} 
					}
				}
			} else if (nEstiminatedLimit) {
				allowedDataRateClass[LAST_CLASS] = min(nEstiminatedLimit, allowedDataRateClass[LAST_CLASS]);
			}
		} else {
			nEstiminatedLimit = 0;
		}
		uint32 minFragSize = 1300;
		uint32 doubleSendSize = minFragSize*2; // send two packages at a time so they can share an ACK
		if(allowedDataRateClass[LAST_CLASS] < 6*1024) {
			minFragSize = 536;
			doubleSendSize = minFragSize; // don't send two packages at a time at very low speeds to give them a smoother load
		}

       	if(cBusy >= nCanSend && realBytesToSpendClass[LAST_CLASS] > 999 /*&& m_StandardOrder_list.GetSize() > 0*/) {
				
			if(nSlotsBusyLevel < 125 && bUploadUnlimited) {
				nSlotsBusyLevel = 125;
				if(thePrefs.GetVerbose() && lotsOfLog) theApp.QueueDebugLogLine(false,_T("Throttler: nSlotsBusyLevel: %i Guessed limit: %0.5f changesCount %i loopsCount: %i (set due to all slots busy)"), nSlotsBusyLevel, (float)nEstiminatedLimit/1024.00f, changesCount, loopsCount);
			}

			busyEvent.Lock(1);
		}
		
		#define TIME_BETWEEN_UPLOAD_LOOPS 0
        /*
		uint32 TIME_BETWEEN_UPLOAD_LOOPS = 0;
        if(cBusy >= nCanSend) {
            TIME_BETWEEN_UPLOAD_LOOPS = 1;
        }
		*/

        uint32 sleepTime;
        if(allowedDataRateClass[LAST_CLASS] == _UI32_MAX || realBytesToSpendClass[LAST_CLASS] >= 1000 || allowedDataRateClass[LAST_CLASS] == 0 && nEstiminatedLimit == 0) {
            // we can send at once
            sleepTime = 1;
        } else if(allowedDataRateClass[LAST_CLASS] == 0) {
            sleepTime = max((uint32)ceil(((double)doubleSendSize*1000)/nEstiminatedLimit), TIME_BETWEEN_UPLOAD_LOOPS);
        } else {
            // sleep for just as long as we need to get back to having one byte to send
            sleepTime = max((uint32)ceil((double)(-realBytesToSpendClass[LAST_CLASS] + 1000)/allowedDataRateClass[LAST_CLASS]), TIME_BETWEEN_UPLOAD_LOOPS);
        }

		timeSinceLastLoop = timeGetTime() - lastLoopTick;
		
        if(timeSinceLastLoop < sleepTime) {
            //DWORD tickBeforeSleep = ::GetTickCount();
            //DWORD tickBeforeSleep2 = timeGetTime();
            Sleep(sleepTime-timeSinceLastLoop);
            //DWORD tickAfterSleep = ::GetTickCount();
            //DWORD tickAfterSleep2 = timeGetTime();

            //theApp.QueueDebugLogLine(false,_T("UploadBandwidthThrottler: Requested sleep time = %i ms. Actual sleep time %i/%i ms."), sleepTime-timeSinceLastLoop, tickAfterSleep-tickBeforeSleep, tickAfterSleep2-tickBeforeSleep2);
        }

		const DWORD thisLoopTick = timeGetTime();
        timeSinceLastLoop = thisLoopTick - lastLoopTick;

		for (uint32 classID = 0; classID < NB_SPLITTING_CLASS; classID++) {
			uint32 allowedDataRate = allowedDataRateClass[classID];
			if(allowedDataRate > 0 && allowedDataRate != _UI32_MAX) {
				if (timeSinceLastLoop > 0) {
					if (realBytesToSpendClass[classID] > 999 && slotCounterClass[classID] == 0) {
						m_highestNumberOfFullyActivatedSlotsClass[classID] = 1;
						realBytesToSpendClass[classID] = 999;
					}
					sint64 limit = -((sint64)(sleepTime + 2000)*allowedDataRate);
					if (realBytesToSpendClass[classID] < limit)
						realBytesToSpendClass[classID] = limit;

					if (_I64_MAX/timeSinceLastLoop > allowedDataRate && _I64_MAX-allowedDataRate*timeSinceLastLoop > realBytesToSpendClass[classID]) {
						realBytesToSpendClass[classID] += allowedDataRate*min(sleepTime + 2000, timeSinceLastLoop);
					} else {
						realBytesToSpendClass[classID] = _I64_MAX;
					}
				}
			} else {
				if (timeSinceLastLoop > 0) {
					if (realBytesToSpendClass[classID] > 999)
						m_highestNumberOfFullyActivatedSlotsClass[classID] = slotCounterClass[classID]+1;
					realBytesToSpendClass[classID] = 1000;
				}
			}
		}
			
		lastLoopTick = thisLoopTick;

		sint64 BytesToSpend = realBytesToSpendClass[LAST_CLASS] / 1000;
		if(BytesToSpend >= 1 || allowedDataRateClass[LAST_CLASS] == 0) {
			timeSinceLastLoop = thisLoopTick - lastLoopTickTryTosend;

			sendLocker.Lock();
        	tempQueueLocker.Lock();
			
			// are there any sockets in m_TempControlQueue_list? Move them to normal m_ControlQueue_list;
			while(!m_TempControlQueueFirst_list.IsEmpty()) {
				ThrottledControlSocket* moveSocket = m_TempControlQueueFirst_list.RemoveHead();
				m_ControlQueueFirst_list.AddTail(moveSocket);
			}
			while(!m_TempControlQueue_list.IsEmpty()) {
				ThrottledControlSocket* moveSocket = m_TempControlQueue_list.RemoveHead();
       			m_ControlQueue_list.AddTail(moveSocket);
			}
   			tempQueueLocker.Unlock();

	    
			uint64 ControlspentBytes = 0;
			uint64 ControlspentOverhead = 0;

			// Send any queued up control packets first
			while((BytesToSpend > 0 && ControlspentBytes < (uint64)BytesToSpend|| allowedDataRateClass[LAST_CLASS] == 0 && ControlspentBytes < 500) && (!m_ControlQueueFirst_list.IsEmpty() || !m_ControlQueue_list.IsEmpty())) {
				ThrottledControlSocket* socket = NULL;
					
				if(!m_ControlQueueFirst_list.IsEmpty()) {
					socket = m_ControlQueueFirst_list.RemoveHead();
				} else if(!m_ControlQueue_list.IsEmpty()) {
					socket = m_ControlQueue_list.RemoveHead();
				}

				if (socket != NULL) {
					/*
					SocketSentBytes socketSentBytes = socket->SendControlData(allowedDataRateClass[LAST_CLASS] > 0?(UINT)(BytesToSpend - ControlspentBytes):1, minFragSize);
					*/
					SocketSentBytes socketSentBytes = socket->SendControlData(1, minFragSize);
					uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets;
					if (lastSpentBytes) {
						Socket_stat* stat = NULL;
						if (m_stat_list.Lookup(socket, stat)) {
							stat->realBytesToSpend -= 1000*lastSpentBytes;
							if (stat->classID < LAST_CLASS) {
								realBytesToSpendClass[stat->classID] -= 1000*socketSentBytes.sentBytesStandardPackets;
								m_SentBytesSinceLastCallClass[stat->classID] += lastSpentBytes;
								m_SentBytesSinceLastCallOverheadClass[stat->classID] += socketSentBytes.sentBytesControlPackets;
							}
						}
						ControlspentBytes += lastSpentBytes;
						ControlspentOverhead += socketSentBytes.sentBytesControlPackets;
					}
       			}
			}
			uint32 numberofclientinhigherclass = 0;
			for (uint32 classID = 0; classID < NB_SPLITTING_CLASS; classID++) {
				if (slotCounterClass[classID]) {
					uint32 allowedclientdatarate = _UI32_MAX;
					if (allowedDataRateClass[LAST_CLASS])	
						allowedclientdatarate = allowedDataRateClass[LAST_CLASS]; 
					if (allowedDataRateClass[classID])
						allowedclientdatarate = min(allowedclientdatarate,allowedDataRateClass[classID]); 
					if (ClientDataRate[classID])
						allowedclientdatarate = min(allowedclientdatarate,ClientDataRate[classID]);
					for(uint32 slotCounter = 0; slotCounter < slotCounterClass[classID]; slotCounter++) {
						ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(numberofclientinhigherclass+slotCounter);
						if(socket != NULL) {
							Socket_stat* stat = NULL;
							if (m_stat_list.Lookup(socket, stat)) {
								ASSERT(stat->classID == classID);
								if (timeSinceLastLoop > 0) {
									if (stat->realBytesToSpend > (sint64)1000*((allowedclientdatarate == _UI32_MAX)?doubleSendSize:allowedclientdatarate))
										stat->realBytesToSpend = (sint64)1000*((allowedclientdatarate == _UI32_MAX)?doubleSendSize:allowedclientdatarate);
									sint64 limit = -((sint64)(sleepTime + 2000)*((allowedclientdatarate == _UI32_MAX)?doubleSendSize:allowedclientdatarate));
									if (stat->realBytesToSpend < limit)
										stat->realBytesToSpend = limit;

									if (_I64_MAX/timeSinceLastLoop > stat->realBytesToSpend && _I64_MAX-allowedclientdatarate*timeSinceLastLoop > stat->realBytesToSpend)
										stat->realBytesToSpend += allowedclientdatarate*min(sleepTime + 2000, timeSinceLastLoop);
									else
										stat->realBytesToSpend = _I64_MAX;
								}
								if(BytesToSpend > 0 && ControlspentBytes < (uint64)BytesToSpend) {
									SocketSentBytes socketSentBytes = socket->SendFileAndControlData(0, minFragSize);
									uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets;
									if (lastSpentBytes) {
										stat->realBytesToSpend -= 1000*lastSpentBytes;
										if(m_highestNumberOfFullyActivatedSlotsClass[classID] > slotCounter)
											m_highestNumberOfFullyActivatedSlotsClass[classID] = slotCounter;
										if (classID<LAST_CLASS) {
											realBytesToSpendClass[classID] -= 1000*lastSpentBytes;
											for (uint32 i = classID+1; classID < LAST_CLASS; classID++)
												realBytesToSpendClass[classID] -= 1000*socketSentBytes.sentBytesControlPackets;
				
											m_SentBytesSinceLastCallClass[classID] += lastSpentBytes;
											m_SentBytesSinceLastCallOverheadClass[classID] += socketSentBytes.sentBytesControlPackets;
										}
										ControlspentBytes += lastSpentBytes;
										ControlspentOverhead += socketSentBytes.sentBytesControlPackets;
									}
								}
							}
						}
					}
					numberofclientinhigherclass += slotCounterClass[classID];
				}
			}
			if (ControlspentBytes) {
				realBytesToSpendClass[LAST_CLASS] -= 1000*ControlspentBytes;
				m_SentBytesSinceLastCallClass[LAST_CLASS] += ControlspentBytes;
				m_SentBytesSinceLastCallOverheadClass[LAST_CLASS] += ControlspentOverhead;
			}
			
			numberofclientinhigherclass = 0;
			for (uint32 classID = 0; classID < NB_SPLITTING_CLASS; classID++) {
				if(slotCounterClass[classID]) {
					if (realBytesToSpendClass[classID] > realBytesToSpendClass[LAST_CLASS])
						BytesToSpend = realBytesToSpendClass[LAST_CLASS] / 1000;
					else
						BytesToSpend = realBytesToSpendClass[classID] / 1000;
					uint32 allowedclientdatarate = _UI32_MAX;
					if (allowedDataRateClass[LAST_CLASS])	
						allowedclientdatarate = allowedDataRateClass[LAST_CLASS]; 
					if (allowedDataRateClass[classID])
						allowedclientdatarate = min(allowedclientdatarate,allowedDataRateClass[classID]); 
					if (ClientDataRate[classID])
						allowedclientdatarate = min(allowedclientdatarate,ClientDataRate[classID]);
					if (BytesToSpend < ((allowedclientdatarate==_UI32_MAX)?doubleSendSize:allowedclientdatarate)/4) {
						numberofclientinhigherclass += slotCounterClass[classID];
						continue;
					}
					
					uint64 spentBytes = 0;
					uint64 spentOverhead = 0;
					for(uint32 slotCounter = 0; slotCounter < slotCounterClass[classID] && BytesToSpend > 0 && spentBytes + ((allowedclientdatarate==_UI32_MAX)?doubleSendSize:allowedclientdatarate/4) < (uint64)BytesToSpend; slotCounter++) {
						ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(numberofclientinhigherclass+slotCounter);
						if(socket != NULL) {
							Socket_stat* stat = NULL;
							if (m_stat_list.Lookup(socket,stat)) {
								//Try to send client allowed data for a client but not more than class allowed data
								if (stat->realBytesToSpend > 999 && stat->scheduled == false) {
#if !defined DONT_USE_SOCKET_BUFFERING
									uint32 BytesToSend = (allowedclientdatarate==_UI32_MAX)?doubleSendSize:allowedclientdatarate/4;
									SocketSentBytes socketSentBytes = socket->SendFileAndControlData(BytesToSend, doubleSendSize);
#else
									SocketSentBytes socketSentBytes = socket->SendFileAndControlData(doubleSendSize, doubleSendSize);
#endif
									uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets;
									if (lastSpentBytes) {
										stat->realBytesToSpend -= 1000*lastSpentBytes;
										if(slotCounter+1 > m_highestNumberOfFullyActivatedSlotsClass[classID]) // || lastSpentBytes > 0 && spentBytes == bytesToSpend /*|| slotCounter+1 == (uint32)m_StandardOrder_list.GetSize())*/))
											m_highestNumberOfFullyActivatedSlotsClass[classID] = slotCounter+1;
										spentBytes += lastSpentBytes;
										spentOverhead += socketSentBytes.sentBytesControlPackets;
									}
								}
							}
						}
					}
					//send remain data to scheduled slot
					for(uint32 slotCounter = 0; slotCounter < slotCounterClass[classID] && BytesToSpend > 0 && spentBytes + ((allowedclientdatarate==_UI32_MAX)?doubleSendSize:allowedclientdatarate/4)< (uint64)BytesToSpend; slotCounter++) {
						ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(numberofclientinhigherclass+slotCounter);
						if(socket != NULL) {
							Socket_stat* stat = NULL;
							if (m_stat_list.Lookup(socket,stat)) {
								if (stat->realBytesToSpend > 999) {
#if !defined DONT_USE_SOCKET_BUFFERING
									uint32 BytesToSend = min((UINT)(BytesToSpend - spentBytes), stat->realBytesToSpend/1000);
									SocketSentBytes socketSentBytes = socket->SendFileAndControlData(BytesToSend, doubleSendSize);
#else
									SocketSentBytes socketSentBytes = socket->SendFileAndControlData((UINT)(BytesToSpend - spentBytes), doubleSendSize);
#endif
									uint32 lastSpentBytes = socketSentBytes.sentBytesControlPackets + socketSentBytes.sentBytesStandardPackets;
									if (lastSpentBytes) {
										stat->realBytesToSpend -= 1000*lastSpentBytes;
										if(slotCounter+1 > m_highestNumberOfFullyActivatedSlotsClass[classID]) // || lastSpentBytes > 0 && spentBytes == bytesToSpend /*|| slotCounter+1 == (uint32)m_StandardOrder_list.GetSize())*/))
											m_highestNumberOfFullyActivatedSlotsClass[classID] = slotCounter+1;
										spentBytes += lastSpentBytes;
										spentOverhead += socketSentBytes.sentBytesControlPackets;
									}
								}
							}
						}
					}
					//for(uint32 slotCounter = 0; slotCounter < slotCounterClass[classID] && BytesToSpend <= 0; slotCounter++) {
					//	ThrottledFileSocket* socket = m_StandardOrder_list.GetAt(numberofclientinhigherclass+slotCounter);
					//	if(socket != NULL) {
					//		Socket_stat* stat = NULL;
					//		if (m_stat_list.Lookup(socket,stat)) {
					//			if (stat->realBytesToSpend > 999) {
					//				stat->realBytesToSpend = 999;
					//			}
					//		}
					//	}
					//}
					if (spentBytes) {
						if (classID < LAST_CLASS) {
							for (uint32 i = classID+1; i < LAST_CLASS; i++)
								realBytesToSpendClass[i] -= 1000*spentOverhead;
							realBytesToSpendClass[classID] -= spentBytes*1000;
							m_SentBytesSinceLastCallClass[classID] += spentBytes;
							m_SentBytesSinceLastCallOverheadClass[classID] += spentOverhead;
						}
						realBytesToSpendClass[LAST_CLASS] -= spentBytes*1000;
						m_SentBytesSinceLastCallClass[LAST_CLASS] += spentBytes;
						m_SentBytesSinceLastCallOverheadClass[LAST_CLASS] += spentOverhead;
					}
					numberofclientinhigherclass += slotCounterClass[classID];
					if (timeSinceLastLoop > 0 && realBytesToSpendClass[classID] > 200*((allowedclientdatarate==_UI32_MAX)?doubleSendSize:allowedclientdatarate)) {
						if (m_highestNumberOfFullyActivatedSlotsClass[classID] <=  slotCounterClass[classID]) {
							++m_highestNumberOfFullyActivatedSlotsClass[classID];
							realBytesToSpendClass[classID] = 200*((allowedclientdatarate==_UI32_MAX)?doubleSendSize:allowedclientdatarate);
						}
					}
				}
			}
			sendLocker.Unlock();
			lastLoopTickTryTosend = thisLoopTick;
		}
	}

    threadEndedEvent->SetEvent();

    tempQueueLocker.Lock();
    m_TempControlQueue_list.RemoveAll();
	m_TempControlQueueFirst_list.RemoveAll();
    tempQueueLocker.Unlock();

    sendLocker.Lock();

    m_ControlQueue_list.RemoveAll();
    POSITION			pos = m_stat_list.GetStartPosition();
	ThrottledControlSocket* socket;
	Socket_stat* stat;
	while (pos)
	{
		m_stat_list.GetNextAssoc(pos, socket, stat);
		delete stat;
	}
	m_stat_list.RemoveAll();
	m_StandardOrder_list.RemoveAll();
    sendLocker.Unlock();

    return 0;
}
