// This file is a part of the xMule Project.
//
// Copyright (c) 2004 Theodore R. Smith (hopeseekr@xmule.ws / http://www.xmule.ws/)
// DSA-1024 Fingerprint: 10A0 6372 9092 85A2 BB7F 907B CB8B 654B E33B F1ED
//
// Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.xmule-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 "xmule-headers.h"
#else
#	include "updownclient.h"
#include "ClientList.h"                     // CClientList
#	include "ClientUDPSocket.h"
#include "DownloadListCtrl.h"               // Needed for CDownloadListCtrl - audited 5 Nov 2004
#	include "DownloadQueue.h"
#	include "ListenSocket.h"
#include "NewSockets.h"                     // Needed for NewSocket_SendPacketOPdata - audited 5 Nov 2004
#	include "opcodes.h"
#	include "packets.h"
#	include "PartFile.h"
#include "SafeFile.h"                       // CSafeMemFile
#include "ServerSocket.h"                   // CServerConnect
#include "sockets.h"                        // Needed for CServerConnect - audited 5 Nov 2004
#	include "TransferWnd.h"
#	include "UploadQueue.h"
#	include "wintypes.h"
#	include "xmule.h"
#	include "xmuleDlg.h"
#endif
#include "ClientCredits.h"                  // CClientCredits
#include "otherstructs.h"                   // Needed for Requested_Block_Struct - audited 5 Nov 2004
#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences

#include <zlib.h>

//	members of CUpDownClient
//	which are mainly used for downloading functions
extern int newprefs02_opt[];

CBarShader CUpDownClient::s_StatusBar(16);

void CUpDownClient::DrawStatusBar(wxMemoryDC *dc, wxRect rect, bool onlygreyrect, bool bFlat)
{
    DWORD crBoth;
    DWORD crNeither;
    DWORD crClientOnly;
    DWORD crPending;
    DWORD crNextPending;
    if (bFlat)
    {
        crBoth = RGB(0, 150, 0);
        crNeither = RGB(224, 224, 224);
        crClientOnly = RGB(0, 0, 0);
        crPending = RGB(255, 208, 0);
        crNextPending = RGB(255, 255, 100);
    }
    else
    {
        crBoth = RGB(0, 192, 0);
        crNeither = RGB(240, 240, 240);
        crClientOnly = RGB(104, 104, 104);
        crPending = RGB(255, 208, 0);
        crNextPending = RGB(255, 255, 100);
    }
    s_StatusBar.SetFileSize(reqfile->GetFileSize());
    s_StatusBar.SetHeight(rect.height - rect.y);
    s_StatusBar.SetWidth(rect.width - rect.x);
    s_StatusBar.Fill(crNeither);
    // Barry - was only showing one part from client, even when reserved bits from 2 parts
    wxString gettingParts;
    ShowDownloadingParts(gettingParts);
    if (!onlygreyrect &&reqfile &&m_abyPartStatus)
    {
        for (uint32_t i = 0; i != m_nPartCount; i++)
        {
            if (m_abyPartStatus[i])
            {
                uint32_t uEnd;
                if (PARTSIZE * (i + 1) > reqfile->GetFileSize())
                uEnd = reqfile->GetFileSize();
                else
                uEnd = PARTSIZE * (i + 1);
                if (reqfile->IsComplete(PARTSIZE *i, PARTSIZE * (i + 1) - 1))
                s_StatusBar.FillRange(PARTSIZE *i, uEnd, crBoth);
                else if(m_nDownloadState == DS_DOWNLOADING && m_nLastBlockOffset < uEnd &&
                m_nLastBlockOffset >= PARTSIZE *i)
                s_StatusBar.FillRange(PARTSIZE *i, uEnd, crPending);
                else if(gettingParts.GetChar(i) == 'Y')
                s_StatusBar.FillRange(PARTSIZE *i, uEnd, crNextPending);
                else
                s_StatusBar.FillRange(PARTSIZE *i, uEnd, crClientOnly);
            }
        }
    }
    s_StatusBar.Draw(dc, rect.x, rect.y, bFlat);
}

bool CUpDownClient::Compare(CUpDownClient *tocomp)
{
    if (HasValidHash() &&tocomp->HasValidHash())
    return !memcmp(this->GetUserHash(), tocomp->GetUserHash(), 16);
    if (HasLowID())
    return((this->GetUserID() == tocomp->GetUserID()) && (GetServerIP() == tocomp->GetServerIP()));
    else
    return(this->GetUserID() == tocomp->GetUserID() || (this->GetIP() &&this->GetIP() == tocomp->GetIP()));
}

void CUpDownClient::AskForDownload()
{
    int rank = GetRemoteQueueRank();
    if (GetDownloadState() == DS_CONNECTING)
    {
        ++m_cFailed;
    }
    else
    {
        m_cFailed = 0;
    }
    bool hotrank = (rank &&rank < 5);
    if (theApp.listensocket->TooManySockets(hotrank) && ! (socket &&socket->IsConnected()))
    {
        if (GetDownloadState() != DS_TOOMANYCONNS)
        {
            SetDownloadState(DS_TOOMANYCONNS);
        }
    }
    else
    {
        if (socket)
        {
            socket->hotrank = hotrank;
        }
        m_bUDPPending = false;
        m_dwLastAskedTime =::GetTickCount();
        if (m_cFailed < 3)
        {
            SetDownloadState(DS_CONNECTING);
            (void) TryToConnect(hotrank);
        }
        else
        {
            SetDownloadState(DS_ERROR);
            theApp.clientlist->RemoveClient(this);
            delete this;
        }
    }
}

bool CUpDownClient::IsSourceRequestAllowed()
{
    DWORD dwTickCount =::GetTickCount() + CONNECTION_LATENCY;
    int nTimePassedClient = dwTickCount - GetLastSrcAnswerTime();
    int nTimePassedFile = dwTickCount - reqfile->GetLastAnsweredTime();
    bool bNeverAskedBefore = GetLastSrcReqTime() == 0;
    return(
    //if client has the correct extended protocol
    ExtProtocolAvailable() &&m_bySourceExchangeVer >= 1 &&
    //AND if we need more sources
    (theApp.dynprefs->Get<long>("max-sources") * 0.85) > reqfile->GetSourceCount() &&
    //AND if...
    (
    //source is not complete and file is rare, allow once every 10 minutes
    (!m_bCompleteSource &&
    (reqfile->GetSourceCount() - reqfile->GetValidSourcesCount() <= RARE_FILE / 4 ||
    reqfile->GetSourceCount() <= RARE_FILE *2
    ) &&
    (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASK)
    ) ||
    // otherwise, allow every 90 minutes, but only if we haven't
    //   asked someone else in last 10 minutes
    ((bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASK *reqfile->GetCommonFilePenalty()) &&
    (nTimePassedFile > SOURCECLIENTREASK)
    )
    )
    );
}

void CUpDownClient::SendFileRequest()
{
    if (reqfile)
    {
        AddAskedCountDown();
        CSafeMemFile *dataFileReq;
        if (newprefs02_opt[1])
        {
            dataFileReq = new CSafeMemFile(32);
            dataFileReq->Write(reqfile->GetFileHash(), 16);
            if (GetExtendedRequestsVersion() > 0)
            {
                reqfile->WritePartStatus(dataFileReq);
            }
            else
            {
                //packet->size=16;
            }
            if (GetExtendedRequestsVersion() > 1)
            {
                //reqfile->WriteCompleteSourcesCount(dataFileReq);
            }
        }
        else
        {
            dataFileReq = new CSafeMemFile(16);
            dataFileReq->Write(reqfile->GetFileHash(), 16);
        }
        NewSocket_SendPacketOPdata(2, OP_FILEREQUEST, dataFileReq, (uint32_t) this, 0);
        delete dataFileReq;
        if (reqfile->GetPartCount() > 0)
        {
            CSafeMemFile dataSetReqFileID(16);
            dataSetReqFileID.Write(reqfile->GetFileHash(), 16);
            NewSocket_SendPacketOPdata(2, OP_SETREQFILEID, &dataSetReqFileID, (uint32_t) this, 0);
        }
        if (IsEmuleClient())
        {
            SetRemoteQueueFull(true);
            SetRemoteQueueRank(0);
        }
        if (theApp.dynprefs->Get<bool>("source-exchange") == true)
        {
            if (IsSourceRequestAllowed())
            {
                reqfile->SetLastAnsweredTimeTimeout();
                Packet *packet = new Packet(OP_REQUESTSOURCES, 16, OP_EMULEPROT);
                md4cpy(packet->pBuffer, reqfile->GetFileHash());
                NewSocket_SendPacketOP(2, OP_REQUESTSOURCES, packet, (uint32_t) this, 0);
                SetLastAskedForSources();
            }
        }
    }
}

void CUpDownClient::ProcessFileInfo(char *packet, uint32_t size)
{
    CSafeMemFile *data = new CSafeMemFile((BYTE *) packet, size);
    unsigned char cfilehash[16];
    data->Read(cfilehash, 16);
    uint16_t namelength;
    data->Read( &namelength, 2);
    if (m_pszClientFilename) {
    delete[] m_pszClientFilename;
	}
    m_pszClientFilename = new char[namelength + 1];
    memset(m_pszClientFilename, 0, namelength + 1);
    // TODO why does this overwrite the end of the buffer:
    data->Read(m_pszClientFilename, namelength);
    //- because sizeof(uint16_t) is always 2
    delete data;
}

void CUpDownClient::ProcessFileStatus(char *packet, uint32_t size)
{
    CSafeMemFile *data = new CSafeMemFile((BYTE *) packet, size);
    unsigned char cfilehash[16];
    data->Read(cfilehash, 16);
    if ((reqfile) && (! memcmp(cfilehash, reqfile->GetFileHash(), 16)))
    {
        data->Read( &m_nPartCount, 2);
        if (m_abyPartStatus)
        {
            delete[] m_abyPartStatus;
            m_abyPartStatus = NULL;
        }
        bool bPartsNeeded = false;
        if (!m_nPartCount)
        {
            m_nPartCount = reqfile->GetPartCount();
            m_abyPartStatus = new uint8_t[m_nPartCount];
            memset(m_abyPartStatus, 1, m_nPartCount);
            bPartsNeeded = true;
            m_bCompleteSource = true;
        }
        else
        {
            if (reqfile->GetPartCount() == m_nPartCount)
            {
                m_bCompleteSource = false;
                m_abyPartStatus = new uint8_t[m_nPartCount];
                uint16_t done = 0;
                while (done != m_nPartCount)
                {
                    uint8_t toread;
                    data->Read( &toread, 1);
                    for (int32_t i = 0; i != 8; i++)
                    {
                        m_abyPartStatus[done] = ((toread >> i) &1) ? 1: 0;
                        if (m_abyPartStatus[done] && !reqfile->IsComplete(done *PARTSIZE, ((done + 1) *PARTSIZE) - 1))
                        bPartsNeeded = true;
                        done++;
                        if (done == m_nPartCount)
                        break;
                    }
                }
            }
        }
        //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
        UpdateDisplayedInfo();
        reqfile->UpdateAvailablePartsCount();
        if (!bPartsNeeded)
        {
            SetDownloadState(DS_NONEEDEDPARTS);
        }
        else if(reqfile->hashsetneeded)
        {
            Packet *packet = new Packet(OP_HASHSETREQUEST, 16);
            memcpy(packet->pBuffer, reqfile->GetFileHash(), 16);
            NewSocket_SendPacketOP(2, OP_HASHSETREQUEST, packet, (uint32_t) this, 0);
            SetDownloadState(DS_REQHASHSET);
            reqfile->hashsetneeded = false;
            m_bHashsetRequested = true;
        }
        else
        {
            delete data;
            SetDownloadState(DS_ONQUEUE);
            data = new CSafeMemFile(16);
            data->Write(this->reqfile->GetFileHash(), 16);
            NewSocket_SendPacketOPdata(2, OP_STARTUPLOADREQ, data, (uint32_t) this, 0);
        }
        reqfile->NewSrcPartsInfo();
    }
    delete data;
}

bool CUpDownClient::AddRequestForAnotherFile(CPartFile *file)
{
    for (POSITION pos = m_OtherNoNeeded_list.GetHeadPosition(); pos != 0; m_OtherNoNeeded_list.GetNext(pos))
    {
        if (m_OtherNoNeeded_list.GetAt(pos) == file)
        return false;
    }
    for (POSITION pos = m_OtherRequests_list.GetHeadPosition(); pos != 0; m_OtherRequests_list.GetNext(pos))
    {
        if (m_OtherRequests_list.GetAt(pos) == file)
        return false;
    }
    m_OtherRequests_list.AddTail(file);
    file->A4AFSourcesList.AddTail(this);
    /* Razor 1a - Modif by MikaelB */
    return true;
}

void CUpDownClient::SetDownloadState(uint8_t byNewState)
{
    if (m_nDownloadState != byNewState)
    {
    /* Razor 1a - Modif by MikaelB */
        if (byNewState == DS_DOWNLOADING)
        {
            reqfile->AddDownloadingSource(this);
        }
        else if(m_nDownloadState == DS_DOWNLOADING)
        {
            reqfile->RemoveDownloadingSource(this);
        }
    /* End modif */
        if (m_nDownloadState == DS_DOWNLOADING)
        {
            m_nDownloadState = byNewState;
            for (POSITION pos = m_DownloadBlocks_list.GetHeadPosition(); pos != 0; m_DownloadBlocks_list.GetNext(pos))
            {
                Requested_Block_Struct *cur_block = m_DownloadBlocks_list.GetAt(pos);
                reqfile->RemoveBlockFromList(cur_block->StartOffset, cur_block->EndOffset);
                delete m_DownloadBlocks_list.GetAt(pos);
            }
            m_DownloadBlocks_list.RemoveAll();
            for (POSITION pos = m_PendingBlocks_list.GetHeadPosition(); pos != 0; m_PendingBlocks_list.GetNext(pos))
            {
                Pending_Block_Struct *pending = m_PendingBlocks_list.GetAt(pos);
                if (reqfile)
                reqfile->RemoveBlockFromList(pending->block->StartOffset, pending->block->EndOffset);
                delete pending->block;
                // Not always allocated
                if (pending->zStream)
                {
                    inflateEnd(pending->zStream);
                    delete pending->zStream;
                }
                delete pending;
            }
            m_PendingBlocks_list.RemoveAll();
            m_nDownDatarate = 0;
            if (byNewState == DS_NONE)
            {
                if (m_abyPartStatus)
                delete[] m_abyPartStatus;
                m_abyPartStatus = 0;
            }
            if (socket &&byNewState != DS_ERROR)
            socket->DisableDownloadLimit();
        }
        m_nDownloadState = byNewState;
        switch (GetDownloadState())
        {
        case DS_DOWNLOADING:
            {
                if (IsEmuleClient())
                SetRemoteQueueFull(false);
                SetRemoteQueueRank(1);
                SetAskedCountDown(0);
            }
            break;
        case DS_CONNECTING:
            break;
        default:
            break;
        }
        UpdateDisplayedInfo(true);
        SetLastStateChange();
    }
}

void CUpDownClient::ProcessHashSet(unsigned char *packet, uint32_t size)
{
    if (reqfile && (!memcmp(packet, reqfile->GetFileHash(), 16)))
    {
        if (m_bHashsetRequested)
        {
            m_bHashsetRequested = false;
            CSafeMemFile *data = new CSafeMemFile((BYTE *) packet, size);
            if (reqfile->LoadHashsetFromFile(data, true))
            {
                SetDownloadState(DS_ONQUEUE);
                delete data;
                data = new CSafeMemFile(16);
                data->Write(this->reqfile->GetFileHash(), 16);
                NewSocket_SendPacketOPdata(2, OP_STARTUPLOADREQ, data, (uint32_t) this, 1);
            }
            else
            {
                reqfile->hashsetneeded = true;
            }
            delete data;
        }
    }
}

void CUpDownClient::SendBlockRequests()
{
    m_dwLastBlockReceived =::GetTickCount();
    if (reqfile)
    {
        if (m_DownloadBlocks_list.IsEmpty())
        {
            if (m_PendingBlocks_list.GetCount() < 3) 
            {
                // Barry - instead of getting 3, just get how many is needed
                uint16_t count = 3 - m_PendingBlocks_list.GetCount();
                Requested_Block_Struct **toadd = new Requested_Block_Struct *[count];
                if (reqfile->GetNextRequestedBlock(this, toadd, &count))
                {
                    for (int i = 0; i != count; i++) 
                    {
                        m_DownloadBlocks_list.AddTail(toadd[i]);
                    }
                }
                delete[] toadd;
            }
        }
        // Barry - Why are unfinished blocks requested again, not just new ones?
        while (m_PendingBlocks_list.GetCount() < 3 && !m_DownloadBlocks_list.IsEmpty())
        {
            Pending_Block_Struct *pblock = new Pending_Block_Struct;
            pblock->block = m_DownloadBlocks_list.RemoveHead();
            pblock->zStream = NULL;
            pblock->totalUnzipped = 0;
            pblock->bZStreamError = false;
            m_PendingBlocks_list.AddTail(pblock);
        }
        if (m_PendingBlocks_list.IsEmpty())
        {
            NewSocket_SendPacketOPdata(2, OP_CANCELTRANSFER, NULL, (uint32_t) this, 0);
            SetDownloadState(DS_NONEEDEDPARTS);
        }
        else
        {
            CMemFile *data = new CMemFile(40);
            data->Write(reqfile->GetFileHash(), 16);
            POSITION pos = m_PendingBlocks_list.GetHeadPosition();
            uint32_t null = 0;
            Requested_Block_Struct *block;
            for (uint32_t i = 0; i != 3; i++)
            {
                if (pos)
                {
                    block = m_PendingBlocks_list.GetAt(pos)->block;
                    m_PendingBlocks_list.GetNext(pos);
                    data->Write( &block->StartOffset, 4);
                }
                else
                {
                    data->Write( &null, 4);
                }
            }
            pos = m_PendingBlocks_list.GetHeadPosition();
            for (uint32_t i = 0; i != 3; i++)
            {
                if (pos)
                {
                    block = m_PendingBlocks_list.GetAt(pos)->block;
                    m_PendingBlocks_list.GetNext(pos);
                    uint32_t endpos = block->EndOffset + 1;
                    data->Write( &endpos, 4);
                }
                else
                {
                    data->Write( &null, 4);
                }
            }
            NewSocket_SendPacketOPdata(2, OP_REQUESTPARTS, data, (uint32_t) this, 0);
            delete data;
        }
    }
}

void CUpDownClient::ProcessBlockPacket(char *packet, uint32_t size, bool packed)
{
    // Ignore if no data required
    if (! (GetDownloadState() == DS_DOWNLOADING || GetDownloadState() == DS_NONEEDEDPARTS))
    return;
    const int HEADER_SIZE = 24;
    // Update stats
    m_dwLastBlockReceived =::GetTickCount();
    // Read data from packet
    CSafeMemFile *data = new CSafeMemFile((BYTE *) packet, size);
    unsigned char fileID[16];
    data->Read(fileID, 16);
    // Check that this data is for the correct file
    if (reqfile && (! memcmp(packet, reqfile->GetFileHash(), 16)))
    {
        // Find the start & end positions, and size of this chunk of data
        uint32_t nStartPos;
        uint32_t nEndPos;
        uint32_t nBlockSize = 0;
        data->Read( &nStartPos, 4);
        if (packed)
        {
            data->Read( &nBlockSize, 4);
            nEndPos = nStartPos + (size - HEADER_SIZE);
            usedcompressiondown = true;
        }
        else
        {
            data->Read( &nEndPos, 4);
        }
        // Check that packet size matches the declared data size + header size (24)
        if (size == ((nEndPos - nStartPos) + HEADER_SIZE))
        {
            // Move end back one, should be inclusive
            theApp.UpdateReceivedBytes(size - HEADER_SIZE);
            m_nDownDataRateMS += size - HEADER_SIZE;
            credits->AddDownloaded(size - HEADER_SIZE, GetIP());
            nEndPos--;
            // Loop through to find the reserved block that this is within
            Pending_Block_Struct *cur_block;
            for (POSITION pos = m_PendingBlocks_list.GetHeadPosition(); pos != NULL; m_PendingBlocks_list.GetNext(pos))
            {
                cur_block = m_PendingBlocks_list.GetAt(pos);
                if ((cur_block->block->StartOffset <= nStartPos) && (cur_block->block->EndOffset >= nStartPos))
                {
                    // Found reserved block
                    if (cur_block->bZStreamError)
                    {
                        reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
                        delete data;
                        return;
                    }
                    // Remember this start pos, used to draw part downloading in list
                    m_nLastBlockOffset = nStartPos;
                    // Occasionally packets are duplicated, no point writing it twice
                    // This will be 0 in these cases, or the length written otherwise
                    uint32_t lenWritten = 0;
                    // Handle differently depending on whether packed or not
                    if (!packed)
                    {
                        // Write to disk (will be buffered in part file class)
                        lenWritten = reqfile->WriteToBuffer(size - HEADER_SIZE,
                        (BYTE *)(packet + HEADER_SIZE),
                        nStartPos,
                        nEndPos,
                        cur_block->block);
                        // Packed:
                    }
                    else
                    {
                        // Create space to store unzipped data, the size is only an initial guess, will be resized in unzip() if not big enough
                        uint32_t lenUnzipped = (size *2);
                        // Don't get too big
                        if (lenUnzipped > (GAPBLOCKSIZE + 300))
                        lenUnzipped = (GAPBLOCKSIZE + 300);
                        BYTE *unzipped = new BYTE[lenUnzipped];
                        // Try to unzip the packet
                        int result = unzip(cur_block, (BYTE *)(packet + HEADER_SIZE), (size - HEADER_SIZE), &unzipped, &lenUnzipped);
                        if (result == Z_OK)
                        {
                            // Write any unzipped data to disk
                            if (lenUnzipped > 0)
                            {
                                // Use the current start and end positions for the uncompressed data
                                nStartPos = cur_block->block->StartOffset + cur_block->totalUnzipped - lenUnzipped;
                                nEndPos = cur_block->block->StartOffset + cur_block->totalUnzipped - 1;
                                if (nStartPos > cur_block->block->EndOffset || nEndPos > cur_block->block->EndOffset)
                                {
                                    reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
                                }
                                else
                                {
                                    // Write uncompressed data to file
                                    lenWritten = reqfile->WriteToBuffer(size - HEADER_SIZE,
                                    unzipped,
                                    nStartPos,
                                    nEndPos,
                                    cur_block->block);
                                }
                            }
                        }
                        else
                        {
//                          wxString strZipError;
//
//                          if (cur_block->zStream && cur_block->zStream->msg)
//                          {
//                              strZipError.Printf(_T(" - %s"), cur_block->zStream->msg);
//                          }

                            reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
                            // If we had an zstream error, there is no chance that we could recover from it nor that we
                            // could use the current zstream (which is in error state) any longer.
                            if (cur_block->zStream)
                            {
                                inflateEnd(cur_block->zStream);
                                delete cur_block->zStream;
                                cur_block->zStream = NULL;
                            }
                            // Although we can't further use the current zstream, there is no need to disconnect the sending
                            // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
                            // valid again. Just ignore all further blocks for the current zstream.
                            cur_block->bZStreamError = true;
                        }
                        delete [] unzipped;
                    }
                    // These checks only need to be done if any data was written
                    if (lenWritten > 0)
                    {
                        m_nTransferedDown += lenWritten;
                        // If finished reserved block
                        if (nEndPos == cur_block->block->EndOffset)
                        {
                            reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
                            delete cur_block->block;
                            // Not always allocated
                            if (cur_block->zStream)
                            {
                                inflateEnd(cur_block->zStream);
                                delete cur_block->zStream;
                            }
                            delete cur_block;
                            m_PendingBlocks_list.RemoveAt(pos);
                            // Request next block
                            SendBlockRequests();
                        }
                    }
                    // Stop looping and exit method
                    delete data;
                    return;
                }
            }
        }
    }
    if (data != NULL)
    {
        delete data;
    }
}

int CUpDownClient::unzip(Pending_Block_Struct *block, BYTE *zipped, uint32_t lenZipped, BYTE **unzipped, uint32_t *lenUnzipped, bool recursive)
{
    int err = Z_DATA_ERROR;
    try
    {
        // Save some typing
        z_stream *zS = block->zStream;
        // Is this the first time this block has been unzipped
        if (zS == NULL)
        {
            // Create stream
            block->zStream = new z_stream;
            zS = block->zStream;
            // Initialise stream values
            zS->zalloc = (alloc_func) 0;
            zS->zfree = (free_func) 0;
            zS->opaque = (voidpf) 0;
            // Set output data streams, do this here to avoid overwriting on recursive calls
            zS->next_out = ( *unzipped);
            zS->avail_out = ( *lenUnzipped);
            // Initialise the z_stream
            err = inflateInit(zS);
            if (err != Z_OK)
            return err;
        }
        // Use whatever input is provided
        zS->next_in = zipped;
        zS->avail_in = lenZipped;
        // Only set the output if not being called recursively
        if (!recursive)
        {
            zS->next_out = ( *unzipped);
            zS->avail_out = ( *lenUnzipped);
        }
        // Try to unzip the data
        err = inflate(zS, Z_SYNC_FLUSH);
        // Is zip finished reading all currently available input and writing all generated output
        if (err == Z_STREAM_END)
        {
            // Finish up
            err = inflateEnd(zS);
            if (err != Z_OK)
            return err;
            // Got a good result, set the size to the amount unzipped in this call (including all recursive calls)
            ( *lenUnzipped) = (zS->total_out - block->totalUnzipped);
            block->totalUnzipped = zS->total_out;
        }
        else if((err == Z_OK) && (zS->avail_out == 0))
        {
            // Output array was not big enough, call recursively until there is enough space
            // What size should we try next
            uint32_t newLength = ( *lenUnzipped) *= 2;
            if (newLength == 0)
            newLength = lenZipped *2;
            // Copy any data that was successfully unzipped to new array
            BYTE *temp = new BYTE[newLength];
            memcpy(temp, ( *unzipped), (zS->total_out - block->totalUnzipped));
            delete [] ( *unzipped);
            ( *unzipped) = temp;
            ( *lenUnzipped) = newLength;
            // Position stream output to correct place in new array
            zS->next_out = ( *unzipped) + (zS->total_out - block->totalUnzipped);
            zS->avail_out = ( *lenUnzipped) - (zS->total_out - block->totalUnzipped);
            // Try again
            err = unzip(block, zS->next_in, zS->avail_in, unzipped, lenUnzipped, true);
        }
        else if((err == Z_OK) && (zS->avail_in == 0))
        {
            // All available input has been processed, everything ok.
            // Set the size to the amount unzipped in this call (including all recursive calls)
            ( *lenUnzipped) = (zS->total_out - block->totalUnzipped);
            block->totalUnzipped = zS->total_out;
        }
        else
        {
            // Should not get here unless input data is corrupt
            //		DebugBreak(); I removed this so that we could let the client run for more the five minutes.. Barry needs to see if there is or isn't a preoblem here..
        }
        if (err != Z_OK)
        ( *lenUnzipped) = 0;
    }
    catch(...)
    {
    }
    return err;
}

uint32_t CUpDownClient::CalculateDownloadRate()
{
    m_AvarageDDR_list.AddTail(m_nDownDataRateMS);
    m_nSumForAvgDownDataRate += m_nDownDataRateMS;
    if (m_AvarageDDR_list.GetCount() > 300)
    m_nSumForAvgDownDataRate -= m_AvarageDDR_list.RemoveHead();
    m_nDownDataRateMS = 0;
    if (m_AvarageDDR_list.GetCount() > 10)
    m_nDownDatarate = 10 *m_nSumForAvgDownDataRate / m_AvarageDDR_list.GetCount();
    else
    m_nDownDatarate = 0;
    m_cShowDR++;
    if (m_cShowDR == 30)
    {
        m_cShowDR = 0;
        //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
        UpdateDisplayedInfo();
    }
    if ((::GetTickCount() - m_dwLastBlockReceived) > DOWNLOADTIMEOUT)
    {
        NewSocket_SendPacketOPdata(2, OP_CANCELTRANSFER, NULL, (uint32_t) this, 1);
        SetDownloadState(DS_ONQUEUE);
    }
    return m_nDownDatarate;
}

uint16_t CUpDownClient::GetAvailablePartCount()
{
    uint16_t result = 0;
    for (int i = 0; i != m_nPartCount; i++)
    {
        if (IsPartAvailable(i))
        result++;
    }
    return result;
}

void CUpDownClient::SetRemoteQueueRank(uint16_t nr)
{
    m_nRemoteQueueRank = nr;
    theApp.xmuledlg->transferwnd->downloadlistctrl->UpdateItem(this);
}

void CUpDownClient::UDPReaskACK(uint16_t nNewQR)
{
    m_bUDPPending = false;
    SetRemoteQueueRank(nNewQR);
    m_dwLastAskedTime =::GetTickCount();
}

void CUpDownClient::UDPReaskFNF()
{
    m_bUDPPending = false;
    theApp.downloadqueue->RemoveSource(this);
    if (!socket)
    {
        Disconnected();
    }
}

void CUpDownClient::UDPReaskForDownload()
{
    if (reqfile && (!m_bUDPPending))
    {
        //the line "m_bUDPPending = true;" use to be here
        if ((m_nUDPPort != 0) && (theApp.dynprefs->Get<long>("udp-port") != 0) && !theApp.serverconnect->IsLowID() && !HasLowID() && !(socket && socket->IsConnected()))
        {
            //don't use udp to ask for sources
            if (IsSourceRequestAllowed())
            {
                m_bUDPPending = true;
                uint16_t packetsize;
                if (GetUDPVersion() >= 3)
                {
                    packetsize = 18;
                }
                else
                {
                    packetsize = 16;
                }
                Packet *response = new Packet(OP_REASKFILEPING, packetsize, OP_EMULEPROT);
                md4cpy(response->pBuffer, reqfile->GetFileHash());
                // #zegzav:completesrc (modify):
                if (GetUDPVersion() >= 3)
                {
                    uint16_t completecount = reqfile->m_nCompleteSourcesCount;
                    memcpy(response->pBuffer + 16, &completecount, 2);
                }
                NewSocket_SendPacketOP(1, OP_REASKFILEPING, response, GetIP(), GetUDPPort());
            }
        }
    }
}

// Barry - Sets string to show parts downloading, eg NNNYNNNNYYNYN
void CUpDownClient::ShowDownloadingParts(wxString &partsYN)
{
    Requested_Block_Struct *cur_block;
    unsigned int x;
    partsYN.Alloc(m_nPartCount + 1);
    for (x = 0; x <= m_nPartCount; x++) partsYN.SetChar(x, 'N');
    for (POSITION pos = m_PendingBlocks_list.GetHeadPosition(); pos != 0; m_PendingBlocks_list.GetNext(pos))
    {
        cur_block = m_PendingBlocks_list.GetAt(pos)->block;
        x = (cur_block->StartOffset / PARTSIZE);
        if (x <= partsYN.Len())
        {
            partsYN.SetChar(x, 'Y');
        }
        else
        {
            //printf("CUpDownClient::ShowDownloadingParts x(%ld) > partsYN.Len()(%ld) pc=%ld\n",
            //x, partsYN.Len(), m_nPartCount);
        }
    }
}

void CUpDownClient::UpdateDisplayedInfo(bool force)
{
    DWORD curTick =::GetTickCount();
    if (force || curTick - m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE + (uint32_t)(rand() / (RAND_MAX/1000)))
    {
        theApp.xmuledlg->transferwnd->downloadlistctrl->UpdateItem(this);
        m_lastRefreshedDLDisplay = curTick;
    }
}

 /* Razor 1a - Modif by MikaelB
    SwapToThisFile function */

void CUpDownClient::SwapToThisFile(CPartFile *file)
{
    // Get the position of the file in other requests' list
    POSITION position = this->m_OtherRequests_list.Find(file);
    // If position isn't null
    if (position)
    {
        // Remove file at position in other requests' list
        this->m_OtherRequests_list.RemoveAt(position);
        // Add file at the head of other requests' list
        this->m_OtherRequests_list.AddHead(file);
        // Some files
        CPartFile *SwapFile, *TempFile, *RequestedFile;
        TempFile = this->m_OtherRequests_list.GetHead();
        RequestedFile = this->reqfile;
        // If the file at the head of other requests' list isn't the requested file
        // and isn't a file of download queue list
        // and either file state is ready or empty...
        if ((TempFile != RequestedFile) &&
        (theApp.downloadqueue->IsPartFile(TempFile)) &&
        ((TempFile->GetStatus(false) == PS_READY) ||
        (TempFile->GetStatus(false) == PS_EMPTY)))
        {
            // Then we can remove head file in other requests' list and save it into SwapFile
            SwapFile = this->m_OtherRequests_list.RemoveHead();
        }
        // If we've found a file to swap
        // and requested file's state is ready or empty ...
        if ((SwapFile) &&
        ((RequestedFile->GetStatus(false) == PS_READY) ||
        (RequestedFile->GetStatus(false) == PS_EMPTY)))
        {
            // Remove source from client in the download list
            theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(this, SwapFile);
            // Get position of this client in the sources list of requested file
            position = RequestedFile->srclist.Find(this);
            // if position isn't null...
            if (position)
            {
                // Remove source at position from the sources list of requested file
                RequestedFile->srclist.RemoveAt(position);
                // Remove source of this client from requested file in the download list
                theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(this, RequestedFile);
                // Add this source in the A4AF sources list of the requested file
                RequestedFile->A4AFSourcesList.AddTail(this);
                // Add the requested file in the other requests' list
                this->m_OtherRequests_list.AddTail(RequestedFile);
                // Add source of requested file from this client in the download list
                theApp.xmuledlg->transferwnd->downloadlistctrl->AddSource(RequestedFile, this, true);
                // Update requested file's sources infos
                RequestedFile->NewSrcPartsInfo();
                // Update requested file's parts counts
                RequestedFile->UpdateAvailablePartsCount();
                // Set download state of this client to no
                this->SetDownloadState(DS_NONE);
                // Set requested file to 0
                RequestedFile = 0;
                // Set remote queue rank to 0
                this->m_nRemoteQueueRank = 0;
                // If  abyPartStatus isn't null
                if (this->m_abyPartStatus)
                {
                    // Delete abyPartStatus
                    delete[] this->m_abyPartStatus;
                    // Set it to null
                    this->m_abyPartStatus = 0;
                }
                // Set part count to
                this->m_nPartCount = 0;
                // Set last asked time to 0
                this->m_dwLastAskedTime = 0;
                // set irate to 0
                this->m_iRate = 0;
                // Set comment to ""
                this->m_strComment = "";
                // If pszClientFilename isn't null
                if (this->m_pszClientFilename)
                {
                    // Delete pszClientFilename
                    delete this->m_pszClientFilename;
                    // Set it to 0
                    this->m_pszClientFilename = 0;
                }
                // Set requested file to swapped file
                this->reqfile = SwapFile;
                // Add this source in the sources list of swapped file
                SwapFile->srclist.AddTail(this);
                // Add source of swapped file from this client to download list
                theApp.xmuledlg->transferwnd->downloadlistctrl->AddSource(SwapFile, this, false);
            }
        }
        // Else if swapped file isn't null...
        else if(SwapFile)
        {
            // Add it to other requests' list
            this->m_OtherRequests_list.AddHead(SwapFile);
            // Add this source to A4AF sources list of swapped file
            this->m_OtherRequests_list.GetHead()->A4AFSourcesList.AddHead(this);
        }
        // Else...
        else
        // Add this source to A4AF sources list of swapped file
        this->m_OtherRequests_list.GetHead()->A4AFSourcesList.AddHead(this);
    }
    // Else...
    else
    // Add this source to A4AF sources lisy of file
    file->A4AFSourcesList.AddHead(this);
}

 /* Razor 1a - Modif by MikaelB
    Made some changes in SwapToAnoterFile function */

bool CUpDownClient::SwapToAnotherFile()
{
    if (this->GetDownloadState() == DS_DOWNLOADING)
    return false;
    if (!this->m_OtherRequests_list.IsEmpty())
    {
        CPartFile *SwapTo = NULL;
        POSITION pos, pos1, pos2;
        for (pos1 = this->m_OtherRequests_list.GetHeadPosition(); (pos2 = pos1) != NULL;)
        {
            this->m_OtherRequests_list.GetNext(pos1);
            CPartFile *cur_file = this->m_OtherRequests_list.GetAt(pos2);
            if (cur_file != this->reqfile
            &&theApp.downloadqueue->IsPartFile(cur_file)
            && !cur_file->IsStopped()
            && (cur_file->GetStatus(false) == PS_READY || cur_file->GetStatus(false) == PS_EMPTY))
            {
                SwapTo = cur_file;
                this->m_OtherRequests_list.RemoveAt(pos2);
                break;
            }
        }
        if (SwapTo)
        {
            pos = SwapTo->A4AFSourcesList.Find(this);
            if (pos)
            {
                SwapTo->A4AFSourcesList.RemoveAt(pos);
                theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(this, SwapTo);
                pos = this->reqfile->srclist.Find(this);
                if (pos)
                {
                    this->reqfile->srclist.RemoveAt(pos);
                    this->reqfile->RemoveDownloadingSource(this);
                    theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(this, this->reqfile);
                    if (this->reqfile->GetStatus(false) == PS_READY || this->reqfile->GetStatus(false) == PS_EMPTY)
                    {
                        this->reqfile->A4AFSourcesList.AddTail(this);
                        this->m_OtherRequests_list.AddTail(this->reqfile);
                        theApp.xmuledlg->transferwnd->downloadlistctrl->AddSource(this->reqfile, this, true);
                    }
                    this->reqfile->NewSrcPartsInfo();
                    this->reqfile->UpdateAvailablePartsCount();
                    this->SetDownloadState(DS_NONE);
                    this->reqfile = 0;
                    this->m_nRemoteQueueRank = 0;
                    if (this->m_abyPartStatus)
                    {
                        delete[] this->m_abyPartStatus;
                        this->m_abyPartStatus = 0;
                    }
                    this->m_nPartCount = 0;
                    this->m_dwLastAskedTime = 0;
                    this->m_iRate = 0;
                    this->m_strComment = "";
                    if (this->m_pszClientFilename)
                    {
                        delete[] this->m_pszClientFilename;
                        this->m_pszClientFilename = 0;
                    }
                    this->reqfile = SwapTo;
                    SwapTo->srclist.AddTail(this);
                    theApp.xmuledlg->transferwnd->downloadlistctrl->AddSource(SwapTo, this, false);
                    return true;
                }
            }
        }
        else if(SwapTo)
        this->m_OtherRequests_list.AddHead(SwapTo);
    }
    return false;
}

    /* End modif */

void CUpDownClient::SetDownloadFile(CPartFile *part)
{
    delete[] m_abyPartStatus;
    m_abyPartStatus = NULL;
    m_nPartCount = 0;
    reqfile = part;
}

