// This file is a part of the xMule Project.
//
// Copyright (c) 2004 Theodore R. Smith (donate@xmule.org / http://www.xmule.org/)
// RSA-1024 Fingerprint: 4145 9DFD 5338 4FCC 1636  86E5 2E5A 42D8 BA13 460B
//
// This file was a 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.

#ifdef PRECOMP
#include "pch.h"
#endif

#include "sys.h"
#include "zlib/zlib.h"            // Needed for zlib usage
#include <cmath>                  // Needed for std::exp

#include "UpDownClients.h"        // Interface declarations
#include "ClientUDPSocket.h"      // Needed for CClientUDPSocket
#include "ClientCredits.h"        // Needed for CClientCredits
#include "UploadListCtrl.h"       // Needed for CUploadListCtrl
#include "packets.h"              // Needed for Packet
#include "QueueListCtrl.h"        // Needed for CQueueListCtrl
#include "TransferWnd.h"          // Needed for CTransferWnd
#include "SafeFile.h"             // Needed for CSafeMemFile
#include "UploadQueue.h"          // Needed for CUploadQueue
#include "xmuleDlg.h"             // Needed for CxmuleDlg
#include "otherstructs.h"         // Needed for Requested_Block_Struct
#include "sockets.h"              // Needed for CServerConnect
#include "PartFile.h"             // Needed for PR_POWERSHARE
#include "KnownFile.h"            // Needed for CKnownFile
#include "ListenSocket.h"         // Needed for CClientReqSocket
#include "SharedFileList.h"       // Needed for CSharedFileList
#include "opcodes.h"              // Needed for PARTSIZE
#include "BarShader.h"            // Needed for CBarShader
#include "xmule.h"                // Needed for access to CxmuleApp members
#include "GetTickCount.h"         // Needed for GetTickCount
#include <wx/string.h>		      // Needed for wxString

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

CBarShader CUpDownClient::s_UpStatusBar(16);

void CUpDownClient::DrawUpStatusBar(wxMemoryDC* dc, wxRect rect, bool onlygreyrect, bool bFlat)
{
    RECT_t gaprect;
    gaprect.top = rect.y + 2;
    gaprect.bottom = (rect.y + rect.height) - 2;
    gaprect.left = rect.x;      //rect->left;
    gaprect.right = (rect.x + rect.width);      //rect->right;
    //dc->FillRect(&gaprect,&CBrush(RGB(220,220,220)));
    wxBrush suti(wxColour(220, 220, 220), wxSOLID);
    dc->SetPen(*wxTRANSPARENT_PEN);
    dc->SetBrush(suti);
    wxRect mygaprect;
    mygaprect.x = gaprect.left;
    mygaprect.y = gaprect.top;
    mygaprect.width = gaprect.right - gaprect.left;
    mygaprect.height = gaprect.bottom - gaprect.top;
    dc->DrawRectangle(mygaprect);
    dc->SetBrush(*wxWHITE_BRUSH);

    if (onlygreyrect)
        return;

    float blockpixel = (float)(rect.width) / ((float)(PARTSIZE * (m_nUpPartCount)) / 1024);
    for (wxUint32 i = 0; i != m_nUpPartCount; ++i)
    {
        if (m_abyUpPartStatus[i])
        {
            gaprect.right = rect.x + (wxUint32)(((float)PARTSIZE * i / 1024) * blockpixel);
            gaprect.left = rect.x + (wxUint32)((float)((float)PARTSIZE * (i + 1) / 1024) * blockpixel);
            //dc->FillRect(&gaprect,&CBrush(RGB(0,0,0)));
            dc->SetBrush(*wxBLACK_BRUSH);
            mygaprect.x = gaprect.left;
            mygaprect.y = gaprect.top;
            mygaprect.width = gaprect.right - gaprect.left;
            mygaprect.height = gaprect.bottom - gaprect.top;
            dc->DrawRectangle(mygaprect);
        }
    }
}

wxUint32 CUpDownClient::GetScore(bool sysvalue, bool isdownloading, bool onlybasevalue)
{
    //TODO: complete this (friends, uploadspeed, xmuleuser etc etc)
    if (!m_pszUsername)
        return 0;

    // friend slot
    if (IsFriend() && GetFriendSlot())
        return 0x0fffffff;

    if (IsBanned())
        return 0;

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

    // TODO coded by tecxx & herbert, one yet unsolved problem here:
    // sometimes a client asks for 2 files and there is no way to decide, which file the 
    // client finally gets. so it could happen that he is queued first because of a 
    // high prio file, but then asks for something completely different.
    int     filepriority = 10;  // standard
    CKnownFile* pFile = theApp.sharedfiles->GetFileByID(GetUploadFileID());
    if (pFile != NULL)
    {
        switch (pFile->GetUpPriority())
        {
            case PR_VERYHIGH:
                if (pFile->IsAutoUpPriority())
                    filepriority = 35;
                else
                    filepriority = 50;
                break;
            case PR_HIGH:
                filepriority = 20;
                break;
            case PR_LOW:
                filepriority = 5;
                break;
            case PR_VERYLOW:
                filepriority = 2;
                break;
            case PR_NORMAL:
            default:
                filepriority = 10;
                break;
        }
    }
    // 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);
        fBaseValue += (float)(::GetTickCount() - m_dwUploadTime > 900000) ? 900000 : 1800000;
        fBaseValue /= 1000;
    }
    fBaseValue *= credits->GetScoreRatio();
    if (!onlybasevalue)
        fBaseValue *= (float (filepriority) / 10.0f);
    if (!isdownloading && !onlybasevalue)
    {
        if (HasLowID() && !(socket && socket->IsConnected()))
        {
            if (!theApp.serverconnect->IsConnected() || theApp.serverconnect->IsLowID() ||
                    theApp.listensocket->TooManySockets())
                return 0;
        }
    }
    return (wxUint32)fBaseValue;
}

// 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(void)  // [Tarod 12/22/2002] 
{
    Requested_Block_Struct* last_done_block;
    Requested_Block_Struct* next_requested_block;
    wxUint32 last_done_part = 0xffffffff;
    wxUint32 next_requested_part = 0xffffffff;

    bool different_part = false;

    try
    {
        // Check if we have good lists and proceed to check for different chunks
        if (!m_BlockRequests_queue.IsEmpty() && !m_DoneBlocks_list.IsEmpty())
        {
            // Get last block and next pending
            last_done_block = (Requested_Block_Struct*)m_DoneBlocks_list.GetHead();
            next_requested_block = (Requested_Block_Struct*)m_BlockRequests_queue.GetHead();

            // Calculate corresponding parts to blocks
            last_done_part = last_done_block->StartOffset / PARTSIZE;
            next_requested_part = next_requested_block->StartOffset / PARTSIZE;

            // Test is we are asking same file and same part
            if (last_done_part != next_requested_part)
            {
                different_part = true;
                theApp.xmuledlg->AddDebugLogLine(false, wxT("Session ended due to new chunk."));
            }
            if (memcmp(last_done_block->FileID, next_requested_block->FileID, 16) != 0)
            {
                different_part = true;
                theApp.xmuledlg->AddDebugLogLine(false, wxT("Session ended due to different file."));
            }
        }
    }
    catch(...)
    {
        different_part = true;
    }
//      theApp.xmuledlg->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_part;
}

bool CUpDownClient::CreateNextBlockPackage()
{
    // time critical
    // check if we should kick this client
    // VQB Full Chunk Trans..
    if (theApp.glob_prefs->TransferFullChunks())
    {
        // VQB to provide full chunk transfers (modified by Tarod)
        if (theApp.uploadqueue->CheckForTimeOver(this) || IsDifferentPartBlock())
        {
            SetWaitStartTime();
            theApp.uploadqueue->RemoveFromUploadQueue(this);
            theApp.uploadqueue->AddClientToQueue(this, true);
            return false;
        }
    }
    else
    {
        if (theApp.uploadqueue->CheckForTimeOver(this))
        {
            // back on the waitqueue
            SetWaitStartTime();
            theApp.uploadqueue->RemoveFromUploadQueue(this);
            theApp.uploadqueue->AddClientToQueue(this, true);
            return false;
        }
    }
    if (m_BlockRequests_queue.IsEmpty())
        return false;

    CFile file;
    wxByte* filedata = 0;
    char* fullname = 0;
    try
    {
        while (!m_BlockRequests_queue.IsEmpty())
        {
            Requested_Block_Struct* currentblock = m_BlockRequests_queue.GetHead();
            CKnownFile* srcfile = theApp.sharedfiles->GetFileByID(currentblock->FileID);
            if (!srcfile)
                throw   wxT("requested file not found");

            if (srcfile->IsPartFile() && ((CPartFile*)srcfile)->GetStatus() != PS_COMPLETE)
            {
                fullname = strdup(((CPartFile*)srcfile)->GetFullName());
                fullname[strlen(fullname) - 4] = 0;
            }
            else
            {
                fullname = new char[strlen(srcfile->GetPath()) + srcfile->GetFileName().length() + 10];
                sprintf(fullname, "%s/%s", srcfile->GetPath(), (char *)srcfile->GetFileName().c_str());
            }

            wxUint32 togo;
            if (currentblock->StartOffset > currentblock->EndOffset)
            {
                togo = currentblock->EndOffset + (srcfile->GetFileSize() - currentblock->StartOffset);
            }
            else
            {
                togo = currentblock->EndOffset - currentblock->StartOffset;
                if (srcfile->IsPartFile() &&
                        !((CPartFile *)srcfile)->IsComplete(currentblock->StartOffset, currentblock->EndOffset - 1))
                    throw   wxT("asked for incomplete block ");
            }

            if (togo > 184320)
                throw   wxT("Client requested too large of a block.");

            if (!srcfile->IsPartFile())
            {
                if (!file.Open(wxString(fullname, *wxConvCurrent), CFile::read))  //CFile::modeRead|CFile::osSequentialScan|CFile::shareDenyNone))
                    throw   wxT("failed to open requested file");
                delete [] fullname;
                fullname = 0;
                file.Seek(currentblock->StartOffset);

                filedata = new wxByte[togo + 500];
                if (wxUint32 done = file.Read(filedata, togo) != togo)
                {
                    file.Seek(0);
                    file.Read(filedata + done, togo - done);
                }
                file.Close();
            }
            else
            {
                CPartFile* partfile = (CPartFile *)srcfile;
                delete [] fullname;
                fullname = 0;
                partfile->m_hpartfile.Seek(currentblock->StartOffset);

                filedata = new wxByte[togo + 500];
                if (wxUint32 done = partfile->m_hpartfile.Read(filedata, togo) != togo)
                {
                    partfile->m_hpartfile.Seek(0);
                    partfile->m_hpartfile.Read(filedata + done, togo - done);
                }
            }

            SetUploadFileID(currentblock->FileID);
            if (m_byDataCompVer == 1 && (!strstr((char *)srcfile->GetFileName().c_str(), ".zip")) &&
                    (!strstr((char *)srcfile->GetFileName().c_str(), ".rar")) && (!strstr((char *)srcfile->GetFileName().c_str(), ".ace")))
                CreatePackedPackets(filedata, togo, currentblock);
            else
                CreateStandartPackets(filedata, togo, currentblock);

            // file statistic
            srcfile->statistic.AddTransferred(togo);

            m_DoneBlocks_list.AddHead(m_BlockRequests_queue.RemoveHead());
            delete [] filedata;
            filedata = 0;
        }
    }
    catch(wxString error) {
        theApp.xmuledlg->AddDebugLogLine(false, wxT("Client '%s' caused error while creating package (%s) - disconnecting client"), GetUsername(), error.c_str());
        theApp.uploadqueue->RemoveFromUploadQueue(this);
        if (filedata)
            delete filedata;
        if (fullname)
            delete [] fullname;
        return false;
    }
//      theApp.xmuledlg->AddDebugLogLine(false,"Debug: Packet done. Size: %i",blockpack->GetLength());
    return true;

}

void CUpDownClient::ProcessUpFileStatus(unsigned char* payload, wxUint32 size)
{
    if (m_abyUpPartStatus)
    {
        delete [] m_abyUpPartStatus;
        m_abyUpPartStatus = NULL;       // added by jicxicmic
    }
    m_nUpPartCount = 0;
    if (size == 16)
        return;
    CSafeMemFile* data = new CSafeMemFile (payload, size);
    wxByte  cfilehash[16];
    data->ReadRaw(cfilehash, 16);
    CKnownFile* tempreqfile = theApp.sharedfiles->GetFileByID(cfilehash);
    data->ERead(m_nUpPartCount);
    if (!m_nUpPartCount)
    {
        m_nUpPartCount = tempreqfile->GetPartCount();
        m_abyUpPartStatus = new wxUint8[m_nUpPartCount];
        memset(m_abyUpPartStatus, 0, m_nUpPartCount);
    }
    else
    {
        if (tempreqfile->GetPartCount() != m_nUpPartCount)
        {
            delete data;       //mf
            m_nUpPartCount = 0;
            return;
        }
        m_abyUpPartStatus = new wxUint8[m_nUpPartCount];
        wxUint16 done = 0;
        while (done != m_nUpPartCount)
        {
            wxUint8 toread;
            data->ERead(toread);
            for (wxInt32 i = 0; i != 8; i++)
            {
                m_abyUpPartStatus[done] = ((toread >> i) & 1) ? 1 : 0;
                // We may want to use this for another feature..
#if 0
                if (m_abyUpPartStatus[done] && !tempreqfile->IsComplete(done * PARTSIZE, ((done + 1) * PARTSIZE) - 1))
                    bPartsNeeded = true;
#endif
                done++;
                if (done == m_nUpPartCount)
                    break;
            }
        }

    }
    tempreqfile->NewAvailPartsInfo();
    theApp.xmuledlg->transferwnd->queuelistctrl->RefreshClient(this);
    delete data;
}

void CUpDownClient::CreateStandartPackets(wxByte* data, wxUint32 togo, Requested_Block_Struct* currentblock)
{
    wxUint32 nPacketSize;
    CMemFile memfile(data, togo);
    if (togo > 10240)
        nPacketSize = togo / (wxUint32)(togo / 10240);
    else
        nPacketSize = togo;
    while (togo)
    {
        if (togo < nPacketSize * 2)
            nPacketSize = togo;
        togo -= nPacketSize;
        CMemFile outdata;
        outdata.WriteRaw(GetUploadFileID(), 16);
        wxUint32 startpos = currentblock->EndOffset - togo - nPacketSize;
        outdata.EWrite(startpos);
        wxUint32 endpos = currentblock->EndOffset - togo;
        outdata.EWrite(endpos);
        outdata.WriteRaw(data, nPacketSize);
        Packet * packet = new Packet(OP_SENDINGPART, outdata);
        m_BlockSend_queue.AddTail(packet);
    }
}

void CUpDownClient::CreatePackedPackets(wxByte* data, wxUint32 togo, Requested_Block_Struct* currentblock)
{
    wxByte* output = new wxByte[togo + 300];
    uLongf  newsize = togo + 300;
    wxUint16 result = compress2(output, &newsize, data, togo, 9);
    if (result != Z_OK || togo <= newsize)
    {
        delete [] output;
        CreateStandartPackets(data, togo, currentblock);
        return;
    }
    m_bUsedComprUp = true;
    togo = newsize;
    wxUint32 nPacketSize;
    if (togo > 10240)
        nPacketSize = togo / (wxUint32)(togo / 10240);
    else
        nPacketSize = togo;
    while (togo)
    {
        if (togo < nPacketSize * 2)
            nPacketSize = togo;
        togo -= nPacketSize;
        CMemFile outdata;
        outdata.WriteRaw(GetUploadFileID(), 16);
        outdata.EWrite(currentblock->StartOffset);
        outdata.EWrite((wxUint32)newsize);
        outdata.WriteRaw(output, nPacketSize);
        Packet * packet = new Packet(OP_COMPRESSEDPART, outdata, OP_EMULEPROT);
        m_BlockSend_queue.AddTail(packet);
    }
    delete [] output;
}

void CUpDownClient::SetUploadFileID(wxByte* 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);
        memcpy(requpfileid, tempreqfileid, 16);
    }
    else
    {
        memset(requpfileid, 0, 16);
    }
    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);

}

void CUpDownClient::SetUpStartTime(wxUint32 dwTime)
{
    if (dwTime)
        m_dwUploadTime = dwTime;
    else
        m_dwUploadTime = ::GetTickCount();
}

void CUpDownClient::SetWaitStartTime(wxUint32 dwTime)
{
    if (dwTime)
        m_dwWaitTime = dwTime;
    else
        m_dwWaitTime = ::GetTickCount();
}

wxUint32 CUpDownClient::SendBlockData(wxUint32 nMaxAmmount)
{
    m_AvarageUDR_list.AddTail(dataratems);
    m_nSumForAvgUpDataRate += dataratems;
    if (m_AvarageUDR_list.GetCount() > 500)
        m_nSumForAvgUpDataRate -= m_AvarageUDR_list.RemoveHead();
    if (m_AvarageUDR_list.GetCount() > 10)
        m_nUpDatarate = 10 * m_nSumForAvgUpDataRate / m_AvarageUDR_list.GetCount();
    else
        m_nUpDatarate = 0;

    m_cSendblock++;
    if (m_cSendblock == 30)
    {
        m_cSendblock = 0;
        theApp.xmuledlg->transferwnd->uploadlistctrl->RefreshClient(this);
    }
    dataratems = 0;
    if (socket && !socket->IsBusy())
    {

        if (m_BlockSend_queue.IsEmpty())
        {
            if (!CreateNextBlockPackage())
                return 0;
        }
        m_nMaxSendAllowed += nMaxAmmount;
        if ( /* m_BlockSend_queue.GetHead()->GetRealPacketSize() > m_nMaxSendAllowed * 3 && */
                m_BlockSend_queue.GetHead()->GetRealPacketSize() > MAXFRAGSIZE * 2)
        {
            // splitting packets
            wxUint32 nSize = m_BlockSend_queue.GetHead()->GetRealPacketSize();
            unsigned char* packet = m_BlockSend_queue.GetHead()->DetachPacket();
            delete m_BlockSend_queue.RemoveHead();

            wxUint32 nPos = nSize;
            bool    bLast = true;
            while (nPos)
            {
                wxUint32 nNewSize = (nPos < MAXFRAGSIZE) ? nPos : MAXFRAGSIZE;
                nPos -= nNewSize;
                unsigned char* pBuffer2 = new unsigned char[nNewSize];
                memcpy(pBuffer2, packet + nPos, nNewSize);
                m_BlockSend_queue.AddHead(new Packet(pBuffer2, nNewSize, bLast));
                bLast = false;
            }
            delete [] packet;
        }
        while (!m_BlockSend_queue.IsEmpty() && m_BlockSend_queue.GetHead()->GetRealPacketSize() <= m_nMaxSendAllowed)
        {

            Packet* tosend = m_BlockSend_queue.RemoveHead();
            wxUint32 nBlockSize = tosend->GetRealPacketSize();
            m_nMaxSendAllowed -= nBlockSize;
//          theApp.uploadqueue->AddUpDataOverheadOther(0, 24);
            socket->SendPacket(*tosend, false);
            delete tosend;
            m_nTransferedUp += nBlockSize;
            theApp.UpdateSentBytes(nBlockSize);
            credits->AddUploaded(nBlockSize);
            if (m_BlockSend_queue.IsEmpty())
                CreateNextBlockPackage();
        }
        dataratems = nMaxAmmount;
        return nMaxAmmount;
    }
    return 0;
}

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

void CUpDownClient::SendHashsetPacket(unsigned char* forfileid)
{
    CKnownFile* file = theApp.sharedfiles->GetFileByID(forfileid);
    if (!file)
    {
        theApp.xmuledlg->AddLogLine(false, wxT("requested file not found"));
        return;
    }

    CMemFile data;
    data.WriteRaw(file->GetFileHash(), 16);
    wxUint16 parts = file->GetHashCount();
    data.EWrite(parts);
    for (int i = 0; i != parts; i++)
        data.WriteRaw(file->GetPartHash(i), 16);
    Packet packet(OP_HASHSETANSWER, data);
    theApp.uploadqueue->AddUpDataOverheadFileRequest(packet.get_size());
    socket->SendPacket(packet, 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::SendRankingInfo()
{
    if (!ExtProtocolAvailable())
        return;
    wxUint16 nRank = theApp.uploadqueue->GetWaitingPosition(this);
    if (!nRank)
        return;
    Packet packet(OP_QUEUERANKING, 12, OP_EMULEPROT);
    CMemFile data(packet);
    data.EWrite(nRank);
    static unsigned char const zeroes[10] = { 0, };
    data.WriteRaw(zeroes, 10);
    theApp.uploadqueue->AddUpDataOverheadOther(packet.get_size());
    socket->SendPacket(packet, true);
}

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

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

    CMemFile data;
    data.EWrite(rating);
    wxUint32 length = desc.Length();
    if (length > 128)
        length = 128;
    data.EWrite(length);
    if (length > 0)
        data.WriteRaw(desc.GetData(), length);
    Packet packet(OP_FILEDESC, data, OP_EMULEPROT);
    theApp.uploadqueue->AddUpDataOverheadOther(packet.get_size());
    socket->SendPacket(packet, true);
}

void CUpDownClient::AddRequestCount(wxByte* 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 (!memcmp(cur_struct->fileid, fileid, 16))
        {
            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));
    memcpy(new_struct->fileid, fileid, 16);
    new_struct->lastasked = ::GetTickCount();
    m_RequestedFiles_list.AddHead(new_struct);
}

void CUpDownClient::UnBan()
{
    m_bBanned = false;
    m_dwBanTime = 0;
    SetWaitStartTime();
    theApp.uploadqueue->UpdateBanCount();
    theApp.xmuledlg->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.xmuledlg->transferwnd->queuelistctrl->RefreshClient(this, true, true);
}

void CUpDownClient::Ban()
{
    m_bBanned = true;
    theApp.uploadqueue->UpdateBanCount();
    theApp.xmuledlg->transferwnd->ShowQueueCount(theApp.uploadqueue->GetWaitingUserCount());
    m_dwBanTime = ::GetTickCount();
    theApp.xmuledlg->transferwnd->queuelistctrl->RefreshClient(this);
    theApp.xmuledlg->AddDebugLogLine(false, wxT("Client '%s' seems to be an aggressive client and is banned from the uploadqueue"), GetUsername());
}

void CUpDownClient::UDPFileReasked()
{
    AddAskedCount();
    SetLastUpRequest();
    wxUint16 nRank = theApp.uploadqueue->GetWaitingPosition(this);
    Packet* response = new Packet(OP_REASKACK, 2, OP_EMULEPROT);
    CMemFile data(*response);
    data.EWrite(nRank);
    theApp.clientudp->SendClientUDPPacket(response, GetIP(), GetUDPPort());
}
