// 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 HAVE_CONFIG_H
    #include <config.h>
#endif // HAVE_CONFIG_H

// Test if we have _GNU_SOURCE before the next step will mess up
// setting __USE_GNU
// (only needed for gcc-2.95 compatibility, gcc 3.2 always defines it)
#include "wx/setup.h"

// Mario Sergio Fujikawa Ferreira <lioux@FreeBSD.org>
// to detect if this is a* BSD system
#if defined(HAVE_SYS_PARAM_H)
#include <sys/param.h>
#endif

#ifdef PRECOMP
    #include "xmule-headers.h"
#endif

#include "PartFile.h"                       // Needed for Module's Protype(s)

#include "AddFileThread.h"                  // Needed for CAddFileThread - audited 4 Nov 2004
#include "DownloadListCtrl.h"               // Needed for CDownloadListCtrl - audited 5 Nov 2004
#include "DownloadQueue.h"
#include "ED2KLink.h"
#include "ini2.h"
#include "KnownFileList.h"                  // CKnownFileList
#include "ListenSocket.h"                   // CClientReqSocket
#include "otherfunctions.h"
#include "otherstructs.h"                   // Gap_Struct Requested_Block_Struct - audited 5 Nov 2004
#include "packets.h"                        // Packet
#include "Preferences.h"                    // Needed for CPreferences::GetAutoDropTimer()
#include "Preview.h"
#include "SearchList.h"
#include "server.h"                         // Needed for CServer - audited 5 Nov 2004
#include "SharedFileList.h"                 // CSharedFileList
#include "sockets.h"                        // Needed for CServerConnect - audited 5 Nov 2004
#include "SysTray.h"
#include "TransferWnd.h"
#include "updownclient.h"
#include "UploadQueue.h"
#include "xmule.h"
#include "xmuleDlg.h"

#include <DynPrefs/DynPrefs.h>              // Needed for DynamicPreferences

#include <arpa/inet.h>
#include <cmath>
#include <iostream>                         // std::cout std::endl
#include <map>                              // std::map
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>                           // std::vector
#include <wx/filename.h>

#define min(a, b) ((a)<(b)?(a):(b))
#define PROGRESS_HEIGHT 3

using std::cout;
using std::endl;

namespace
{
    int s_instance = 0;
    std::map<int, std::vector<CUpDownClient *> > downloading_sources;

    void SetPartFileStatus(CPartFile* in_part);
    void UpdateDisplayedInfo(CPartFile* in_part, unsigned long& last_time, bool force = false);
}

CBarShader CPartFile::s_LoadBar(PROGRESS_HEIGHT);

CBarShader CPartFile::s_ChunkBar(16);

CPartFile::CPartFile()
{
    Init();
}

CPartFile::CPartFile(CSearchFile* searchresult)
{
    Init();
    memcpy(m_abyFileHash, searchresult->GetFileHash(), 16);

    for (int i = 0; i != searchresult->taglist.GetCount(); i++)
    {
        switch (searchresult->taglist[i]->tag->specialtag)
        {
            case FT_FILENAME:
            {
                m_pszFileName = nstrdup(searchresult->taglist[i]->tag->stringvalue);
                break;
            }
            case FT_FILESIZE:
            {
                m_nFileSize = searchresult->taglist[i]->tag->intvalue;
                break;
            }
            default:
            {
                CTag* newtag = new CTag(searchresult->taglist[i]->tag);
                taglist.Add(newtag);
            }
        }
    }
    CreatePartFile();
}

CPartFile::CPartFile(wxString edonkeylink)
{
    std::auto_ptr<CED2KLink> pLink(CED2KLink::CreateLinkFromUrl(edonkeylink));

    try
    {        
        CED2KFileLink* pFileLink = pLink->GetFileLink();

        if (pFileLink == 0)
        {
            throw GetResString(IDS_ERR_NOTAFILELINK);
        }

        InitializeFromLink(pFileLink);
    }
    catch(const wxString error)
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_LINKERROR), wxString::Format(GetResString(IDS_ERR_INVALIDLINK), error.GetData()).c_str());
        status = PS_ERROR;
        SetPartFileStatus(this);
    }
}

void CPartFile::InitializeFromLink(CED2KFileLink* fileLink)
{
    Init();
    m_pszFileName = nstrdup(fileLink->GetName());
    m_nFileSize = fileLink->GetSize();
    memcpy(m_abyFileHash, fileLink->GetHashKey(), sizeof(m_abyFileHash));

    if (!theApp.downloadqueue->IsFileExisting(m_abyFileHash))
    {
        CreatePartFile();
    }
    else
    {
        status = PS_ERROR;
        SetPartFileStatus(this);
    }
}

CPartFile::CPartFile(CED2KFileLink* fileLink)
{
    InitializeFromLink(fileLink);
}

void CPartFile::Init()
{
    ++s_instance;
    my_id = s_instance;
//    cout << "CPartFile Instance " << my_id << " created..." << endl;

    valid_sources = 0;
    sources_iterations = 0;
    fullname = 0;
    newdate = true;
    lastsearchtime = 0;
    lastpurgetime = ::GetTickCount();
    paused = false;
    stopped = false;
    status = PS_EMPTY;
    SetPartFileStatus(this);
    transfered = 0;

    if (theApp.dynprefs->Get<bool>("auto-priority-share") == true)
    {
        m_iDownPriority = PR_HIGH;
        m_bAutoDownPriority = true;
    }
    else
    {
        m_iDownPriority = PR_NORMAL;
        m_bAutoDownPriority = false;
    }

    srcarevisible = false;
    transferingsrc = 0;
    datarate = 0;
    hashsetneeded = true;
    count = 0;
    percentcompleted = 0;
    partmetfilename = 0;
    completedsize = 0;
    m_bPreviewing = false;
    //NULL;:
    lastseencomplete = 0;
    availablePartsCount = 0;
    m_ClientSrcAnswered = 0;
    m_LastNoNeededCheck = 0;
    m_iRate = 0;
    m_strComment = "";
    m_nTotalBufferData = 0;
    m_nLastBufferFlushTime = 0;
    m_bPercentUpdated = false;
    m_bRecoveringArchive = false;
    m_iGainDueToCompression = 0;
    m_iLostDueToCorruption = 0;
    m_iTotalPacketsSavedDueToICH = 0;
    m_nSavedReduceDownload = 0;
    hasRating = false;
    hasComment = false;
    m_IsA4AFAuto = false;
    /* Razor 1a - Modif by MikaelB*/
    m_lastdatetimecheck = 0;
    m_category = 0;
    m_lastRefreshedDLDisplay = 0;
    m_LastSourceDropTime = 0;
//    MapData_Init(listof_downloadingSources);
}

CPartFile::~CPartFile()
{
    // Barry - Ensure all buffered data is written
    FlushBuffer();
    //m_hpartfile.Flush();
    //SavePartFile();
    //m_hpartfile.Close();
    if (fullname)
    {
        delete[] fullname;
    }

    if (partmetfilename)
    {
        delete[] partmetfilename;
    }

    m_SrcpartFrequency.RemoveAll();
    
    for (POSITION pos = gaplist.GetHeadPosition(); pos != 0; gaplist.GetNext(pos))
    {
        delete gaplist.GetAt(pos);
    }

//    MAP* pos=listof_downloadingSources->next;
//    while (pos->Key)
//    {
//	    pos = MapData_Remove(listof_downloadingSources, pos);
//    }

    std::map<int, std::vector<CUpDownClient *> >::iterator it;
    it = downloading_sources.find(my_id);

    if (it != downloading_sources.end())
    {
        std::vector<CUpDownClient *>::iterator it2;
        for (it2 = it->second.begin(); it2 != it->second.end(); ++it2)
        {
            delete *it2;
        }

        downloading_sources.erase(it++);
        cout << "Erased a source..." << endl;
    }

//    cout << "CPartFile Instance " << my_id << " destroyed..." << endl;
    --s_instance;
}

void CPartFile::CreatePartFile()
{
    // use lowest free partfilenumber for free file (InterCeptor)
    int i = 0;
    wxString filename;

    do
    {
        ++i;

        if (theApp.dynprefs->Get<bool>("use-new-parts"))
        {
            filename.Printf(theApp.dynprefs->Get<wxString>("temp-directory") + "/%03i.pnew", i);
        }
        else
        {
            filename.Printf(theApp.dynprefs->Get<wxString>("temp-directory") + "/%03i.part", i);
        }
    }
    while (wxFileName::FileExists(filename));

    partmetfilename = new char[15];

    if (theApp.dynprefs->Get<bool>("use-new-parts"))
    {
        sprintf(partmetfilename, "%03i.pnew.met", i);
    }
    else
    {
        sprintf(partmetfilename, "%03i.part.met", i);
    }

    fullname = new char[theApp.dynprefs->Get<wxString>("temp-directory").length() + strlen(partmetfilename) + 2048];
    sprintf(fullname, "%s/%s", static_cast<const char* >(theApp.dynprefs->Get<wxString>("temp-directory").c_str()), partmetfilename);
    char* buffer = nstrdup(partmetfilename);
    buffer[strlen(buffer) - 4] = 0;
    CTag* partnametag = new CTag(FT_PARTFILENAME, buffer);
    delete[] buffer;
    taglist.Add(partnametag);
    Gap_Struct* gap = new Gap_Struct;
    gap->start = 0;
    gap->end = m_nFileSize - 1;
    gaplist.AddTail(gap);
    char* partfull = nstrdup(fullname);
    partfull[strlen(partfull) - 4] = 0;
//    printf("partfull=%s\n", partfull);

    if (!m_hpartfile.Create(partfull, TRUE))
    {
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_CREATEPARTFILE));
        status = PS_ERROR;
        SetPartFileStatus(this);
    }

    // jesh.. luotu. nyt se vaan pitää avata uudestaan read-writeen..
    m_hpartfile.Close();
    
    if (!m_hpartfile.Open(partfull, CFile::read_write))
    {
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_CREATEPARTFILE));
        status = PS_ERROR;
        SetPartFileStatus(this);
    }

    delete[] partfull;
    m_SrcpartFrequency.SetSize(GetPartCount());

    for (uint32_t i = 0; i != GetPartCount(); i++)
    {
        m_SrcpartFrequency.Add((uint16_t) 0);
    }

    paused = false;
    SavePartFile(true);
}

bool CPartFile::LoadPartFile(char* in_directory, char* in_filename)
{
    // Slugfiller:
    transfered = 0;
    partmetfilename = nstrdup(in_filename);
    directory = nstrdup(in_directory);
    char* buffer = new char[strlen(directory) + strlen(partmetfilename) + 2];
    sprintf(buffer, "%s/%s", directory, partmetfilename);
    fullname = buffer;
    CSafeFile file;

    // readfile data form part.met file
    if (!file.Open(fullname, CFile::read))
    {
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_OPENMET), partmetfilename, m_pszFileName);
        return false;
    }
    uint8_t version = 0;
    file.Read(&version, 1);

    if (version != PARTFILE_VERSION)
    {
        file.Close();
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_BADMETVERSION), partmetfilename, m_pszFileName);
        return false;
    }

    LoadDateFromFile(&file);
    LoadHashsetFromFile(&file, false);
/* We need to forge some values here for later error
* detection to work.
*/

    uint32_t tagcount = 0;
    uint32_t j = 0;

    std::map<uint16_t, Gap_Struct *> gap_map;

    try
    {
        if (4 != file.Read(&tagcount, 4))
        {
            tagcount = 0;
            throw CInvalidPacket("short file reading tagcount");
        }
    
        for (j=0; j<tagcount; ++j)
        {
            try
            {
                std::auto_ptr<CTag> newtag(new CTag(&file));

                switch (newtag->tag->specialtag)
                {
                    case FT_FILENAME:
                    {
                        if (newtag->tag->stringvalue == NULL)
                        {
                            theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_METCORRUPT), partmetfilename, m_pszFileName);
                            return false;
                        }

                        m_pszFileName = nstrdup(newtag->tag->stringvalue);
                        break;
                    }
                    case FT_LASTSEENCOMPLETE:
                    {
                        lastseencomplete = newtag->tag->intvalue;
                        break;
                    }
                    case FT_FILESIZE:
                    {
                        m_nFileSize = newtag->tag->intvalue;
                        break;
                    }
                    case FT_TRANSFERED:
                    {
                        transfered = newtag->tag->intvalue;
                        break;
                    }
                    case FT_CATEGORY:
                    {
                        m_category = newtag->tag->intvalue;
                        break;
                    }
                    case FT_OLDDLPRIORITY:
                    case FT_DLPRIORITY:
                    {
                        m_iDownPriority = newtag->tag->intvalue;

                        if (m_iDownPriority == PR_AUTO)
                        {
                            m_iDownPriority = PR_HIGH;
                            SetAutoDownPriority(true);
                        }
                        else
                        {
                            SetAutoDownPriority(false);
                        }

                        break;
                    }
                    case FT_STATUS:
                    {
                        paused = newtag->tag->intvalue;
                        stopped = paused;
                        break;
                    }
                    case FT_OLDULPRIORITY:
                    case FT_ULPRIORITY:
                    {
                        SetUpPriority(newtag->tag->intvalue, false);

                        if (GetUpPriority() == PR_AUTO)
                        {
                            SetUpPriority(PR_HIGH, false);
                            SetAutoUpPriority(true);
                        }
                        else
                        {
                            SetAutoUpPriority(false);
                        }

                        break;
                    }
                    default:
                    {
                        // Start Changes by Slugfiller for better exception handling
                        if ((!newtag->tag->specialtag) && 
/*->*/                      (newtag->tag->tagname[0] == FT_GAPSTART ||
/*->*//*->*/                    newtag->tag->tagname[0] == FT_GAPEND))
                        {
                            Gap_Struct* gap;
                            uint16_t gapkey = atoi(&newtag->tag->tagname[1]);

                            if (gap_map.find(gapkey) == gap_map.end())
                            {
                                gap = new Gap_Struct;
                                gap->start = -1;
                                gap->end = -1;
                                gap_map[gapkey] = gap;

                            }

                            if (newtag->tag->tagname[0] == FT_GAPSTART)
                            {
                                gap->start = newtag->tag->intvalue;
                            }

                            if (newtag->tag->tagname[0] == FT_GAPEND)
                            {
                                gap->end = newtag->tag->intvalue - 1;
                            }

                            // End Changes by Slugfiller for better exception handling
                        }
                        else
                        {
                            taglist.Add(newtag.release());
                        }
                    }
                }
            }
            catch(CStrangePacket)
            {
                cout << "Strange little packet, I see you\n[and will not crash because of you :-)]." << endl;
            }
        }
    }
    catch(CInvalidPacket e)
    {
/*
* When failing on the last tag, this might be
* due to a failure in xMule writing the file.
*/
        if (!tagcount || (j != tagcount - 1))
        {
            printf("failure reading part file - %s\n", e.what());
            file.Close();
            return false;
        }
    }
    file.Close();

    // Now to flush the map into the list (Slugfiller)

    std::map<uint16_t, Gap_Struct *>::iterator it;
    for (it = gap_map.begin(); it != gap_map.end(); ++it)
    {
        Gap_Struct* gap;
        uint16_t gapkey;

        gap = it->second;

        if (gap->start >= 0 && gap->end >= 0 && gap->start <= gap->end)
        {
            // All tags accounted for:
            gaplist.AddTail(gap);
        }
        else
        {
            // Some of the tags were missing:
            delete gap;
            gap_map.erase(it++);
        }
    }

    //check if this is a backup
    if (strcasecmp(strrchr(fullname, '.'), ".backup") == 0)
    {
        char* oldfullname = fullname;
        fullname = new char[strlen(fullname) + 1];
        strcpy(fullname, oldfullname);
        delete[] oldfullname;
    }

    // open permanent handle
    char* searchpath = nstrdup(fullname);
    searchpath[strlen(fullname) - 4] = 0;

    if (!m_hpartfile.Open(searchpath, CFile::read_write))
    {
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_FILEOPEN), fullname, m_pszFileName);
        delete[] searchpath;
        return false;
    }

    delete[] searchpath;
    searchpath = NULL;
    m_SrcpartFrequency.SetSize(GetPartCount());

    for (uint32_t i = 0; i != GetPartCount(); i++)
    {
        m_SrcpartFrequency.Add(0);
    }

    status = PS_EMPTY;
    SetPartFileStatus(this);

    if (gaplist.IsEmpty())
    {
        CompleteFile(false);
        return true;
    }

    if (hashlist.GetCount() < GetPartCount())
    {
        hashsetneeded = true;
        return true;
    }
    else
    {
        hashsetneeded = false;
        for (int i = 0; i != hashlist.GetSize(); i++)
        {
            if (IsComplete(i* PARTSIZE, ((i + 1) * PARTSIZE) - 1))
            {
                status = PS_READY;
                SetPartFileStatus(this);
            }
        }
    }

    struct stat statbuf;
    fstat(m_hpartfile.fd(), &statbuf);

    if ((time_t) date != (time_t) statbuf.st_mtime)
    {
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_REHASH), buffer, m_pszFileName);
        // rehash
        status = PS_WAITINGFORHASH;
        SetPartFileStatus(this);
        char* partfilename = nstrdup(partmetfilename);
        partfilename[strlen(partfilename) - 4] = 0;
        CAddFileThread::AddFile(directory, partfilename, this);
        delete[] partfilename;
    }

    UpdateCompletedInfos();

    if (completedsize > transfered)
    {
        m_iGainDueToCompression = completedsize - transfered;
    }
    else if (completedsize != transfered)
    {
        m_iLostDueToCorruption = transfered - completedsize;
    }

    return true;
}

bool CPartFile::SavePartFile(bool Initial)
{
    if (status == PS_WAITINGFORHASH || status == PS_HASHING)
    {
        return false;
    }

    /* Don't write anything to disk if less than 5000 bytes of free space is left.*/
    wxLongLong total, free;

    if (wxGetDiskSpace(theApp.dynprefs->Get<wxString>("temp-directory"), &total, &free) && free < 5000)
    {
        return false;
    }

    FILE* file = 0;
    char* searchpath = nstrdup(fullname);
    searchpath[strlen(fullname) - 4] = 0;
    wxString fName = ::wxFindFirstFile(searchpath, wxFILE);
    delete[] searchpath;
    searchpath = NULL;

    if (fName.IsEmpty())
    {
        if (file)
        fclose(file);
        theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_SAVEMET), GetResString(IDS_ERR_PART_FNF).GetData(), partmetfilename, m_pszFileName);
        return false;
    }

    struct stat sbf;
    stat(fName.GetData(), &sbf);
    date = sbf.st_mtime;
    uint32_t lsc = lastseencomplete;

    if (!Initial)
    {
        BackupFile(fullname, ".backup");
    }

    file = fopen(fullname, "wbS");

    if (!file)
    {
        throw GetResString(IDS_ERR_OPENMETFILE);
    }

    uint8_t version = PARTFILE_VERSION;
    fwrite(&version, 1, 1, file);
    fwrite(&date, 4, 1, file);
    fwrite(&m_abyFileHash, 16, 1, file);
    uint16_t parts = hashlist.GetCount();

    fwrite(&parts, 2, 1, file);

    for (int x = 0; x < parts; ++x)
    {
        fwrite(hashlist[x], 16, 1, file);
    }

    uint32_t tagcount = taglist.GetCount() + 10 + (gaplist.GetCount() * 2);
    fwrite(&tagcount, 4, 1, file);
    CTag(FT_FILENAME, m_pszFileName).WriteTagToFile(file);
    CTag(FT_FILESIZE, m_nFileSize).WriteTagToFile(file);
    CTag(FT_TRANSFERED, transfered).WriteTagToFile(file);
    CTag(FT_STATUS, (paused) ? 1: 0).WriteTagToFile(file);
    CTag* prioritytag;
    uint8_t autoprio = PR_AUTO;

    if (this->IsAutoDownPriority())
    {
        prioritytag = new CTag(FT_DLPRIORITY, autoprio);
    }
    else
    {
        prioritytag = new CTag(FT_DLPRIORITY, m_iDownPriority);
    }

    prioritytag->WriteTagToFile(file);
    delete prioritytag;

    if (this->IsAutoDownPriority())
    {
        prioritytag = new CTag(FT_OLDDLPRIORITY, autoprio);
    }
    else
    {
        prioritytag = new CTag(FT_OLDDLPRIORITY, m_iDownPriority);
    }

    prioritytag->WriteTagToFile(file);
    delete prioritytag;
    CTag* lsctag = new CTag(FT_LASTSEENCOMPLETE, lsc);
    lsctag->WriteTagToFile(file);
    delete lsctag;
    CTag* ulprioritytag;

    if (this->IsAutoUpPriority())
    {
        ulprioritytag = new CTag(FT_ULPRIORITY, autoprio);
    }
    else
    {
        ulprioritytag = new CTag(FT_ULPRIORITY, GetUpPriority());
    }

    ulprioritytag->WriteTagToFile(file);
    delete ulprioritytag;

    if (this->IsAutoUpPriority())
    {
        ulprioritytag = new CTag(FT_OLDULPRIORITY, autoprio);
    }
    else
    {
        ulprioritytag = new CTag(FT_OLDULPRIORITY, GetUpPriority());
    }

    ulprioritytag->WriteTagToFile(file);
    delete ulprioritytag;
    CTag* categorytab = new CTag(FT_CATEGORY, m_category);
    categorytab->WriteTagToFile(file);
    delete categorytab;

    for (uint32_t j = 0; j < (uint32_t)taglist.GetCount(); ++j)
    {
        taglist[j]->WriteTagToFile(file);
    }

    char* namebuffer = new char[10];
    char* number = &namebuffer[1];
    uint16_t i_pos = 0;

    for (POSITION pos = gaplist.GetHeadPosition(); pos != 0; gaplist.GetNext(pos))
    {
        sprintf(number, "%d", i_pos);
        namebuffer[0] = FT_GAPSTART;
        CTag* gapstarttag = new CTag(namebuffer, gaplist.GetAt(pos)->start);
        gapstarttag->WriteTagToFile(file);
        namebuffer[0] = FT_GAPEND;
        CTag* gapendtag = new CTag(namebuffer, (gaplist.GetAt(pos)->end) + 1);
        gapendtag->WriteTagToFile(file);
        delete gapstarttag;
        delete gapendtag;
        i_pos++;
    }

    delete[] namebuffer;

    if (ferror(file))
    {
        throw wxString("unexpected write error");
    }

    if (!Initial)
    {
        wxRemoveFile(wxString(fullname) + ".backup");
    }

    fclose(file);
    SafeCopyFile(fullname, wxString(fullname) + ".BAK");
    return true;
}

void CPartFile::PartFileHashFinished(CKnownFile* result)
{
    newdate = true;
    bool errorfound = false;

    for (uint32_t i = 0; i != (uint32_t) hashlist.GetSize(); i++)
    {
        if (IsComplete(i* PARTSIZE, ((i + 1) * PARTSIZE) - 1))
        {
            if (!(result->GetPartHash(i) && !memcmp(result->GetPartHash(i), this->GetPartHash(i), 16)))
            {
                theApp.xmuledlg->AddLogLine(false, GetResString(IDS_ERR_FOUNDCORRUPTION), i + 1, m_pszFileName);
                AddGap(i* PARTSIZE, ((((i + 1) * PARTSIZE) - 1) >= m_nFileSize) ? m_nFileSize - 1: ((i + 1) * PARTSIZE) - 1);
                errorfound = true;
            }
        }
    }

    delete result;

    if (!errorfound)
    {
        if (status == PS_COMPLETING)
        {
            CompleteFile(true);
            return;
        }
        else
        {
            theApp.xmuledlg->AddLogLine(false, GetResString(IDS_HASHINGDONE), m_pszFileName);
        }
    }

    status = PS_READY;
    SetPartFileStatus(this);
    SavePartFile();
    theApp.sharedfiles->SafeAddKFile(this);
}

void CPartFile::AddGap(uint32_t start, uint32_t end)
{
    POSITION pos1, pos2;

    for (pos1 = gaplist.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        Gap_Struct* cur_gap = gaplist.GetNext(pos1);

        if (cur_gap->start >= start && cur_gap->end <= end)
        {
            // this gap is inside the new gap - delete
            gaplist.RemoveAt(pos2);
            delete cur_gap;
            continue;
        }
        else if (cur_gap->start >= start && cur_gap->start <= end)
        {
            // a part of this gap is in the new gap - extend limit and delete
            end = cur_gap->end;
            gaplist.RemoveAt(pos2);
            delete cur_gap;
            continue;
        }
        else if (cur_gap->end <= end && cur_gap->end >= start)
        {
            // a part of this gap is in the new gap - extend limit and delete
            start = cur_gap->start;
            gaplist.RemoveAt(pos2);
            delete cur_gap;
            continue;
        }
        else if (start >= cur_gap->start && end <= cur_gap->end)
        {
            // new gap is already inside this gap - return
            return;
        }
    }

    Gap_Struct* new_gap = new Gap_Struct;
    new_gap->start = start;
    new_gap->end = end;
    gaplist.AddTail(new_gap);
    //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay);
    newdate = true;
}

bool CPartFile::IsComplete(uint32_t start, uint32_t end)
{
    if (end >= m_nFileSize)
    {
        end = m_nFileSize - 1;
    }

    for (POSITION pos = gaplist.GetHeadPosition(); pos != 0; gaplist.GetNext(pos))
    {
        Gap_Struct* cur_gap = gaplist.GetAt(pos);
        if ((cur_gap->start >= start && cur_gap->end <= end)
/*->*/      || (cur_gap->start >= start && cur_gap->start <= end)
/*->*/      || (cur_gap->end <= end && cur_gap->end >= start)
/*->*/      || (start >= cur_gap->start && end <= cur_gap->end))
        {
            return false;
        }
    }

    return true;
}

bool CPartFile::IsPureGap(uint32_t start, uint32_t end)
{
    if (end >= m_nFileSize)
    {
        end = m_nFileSize - 1;
    }

    for (POSITION pos = gaplist.GetHeadPosition(); pos != 0; gaplist.GetNext(pos))
    {
        Gap_Struct* cur_gap = gaplist.GetAt(pos);

        if (start >= cur_gap->start && end <= cur_gap->end)
        {
            return true;
        }
    }

    return false;
}

bool CPartFile::IsAlreadyRequested(uint32_t start, uint32_t end)
{
    for (POSITION pos = requestedblocks_list.GetHeadPosition(); pos != 0; requestedblocks_list.GetNext(pos))
    {
        if ((start >= requestedblocks_list.GetAt(pos)->StartOffset)
            && (start <= requestedblocks_list.GetAt(pos)->EndOffset))
        {
            return true;
        }
    }

    return false;
}

bool CPartFile::GetNextEmptyBlockInPart(uint16_t partNumber, Requested_Block_Struct* result)
{
    Gap_Struct* firstGap;
    Gap_Struct* currentGap;
    uint32_t end;
    uint32_t blockLimit;
    // Find start of this part
    uint32_t partStart = (PARTSIZE* partNumber);
    uint32_t start = partStart;
    // What is the end limit of this block, i.e. can't go outside part (or filesize)
    uint32_t partEnd = (PARTSIZE* (partNumber + 1)) - 1;

    if (partEnd >= GetFileSize())
    {
        partEnd = GetFileSize() - 1;
    }

    // Loop until find a suitable gap and return true, or no more gaps and return false
    while (true)
    {
        firstGap = NULL;

        // Find the first gap from the start position
        for (POSITION pos = gaplist.GetHeadPosition(); pos != 0; gaplist.GetNext(pos))
        {
            currentGap = gaplist.GetAt(pos);
            // Want gaps that overlap start<->partEnd
            if ((currentGap->start <= partEnd) && (currentGap->end >= start))
            {
                // Is this the first gap?
                if ((firstGap == NULL) || (currentGap->start < firstGap->start))
                {
                    firstGap = currentGap;
                }
            }
        }

        // If no gaps after start, exit
        if (firstGap == NULL)
        {
            return false;
        }

        // Update start position if gap starts after current pos
        if (start < firstGap->start)
        {
            start = firstGap->start;
        }

        // If this is not within part, exit
        if (start > partEnd)
        {
            return false;
        }

        // Find end, keeping within the max block size and the part limit
        end = firstGap->end;
        blockLimit = partStart + (GAPBLOCKSIZE* (((start - partStart) / GAPBLOCKSIZE) + 1)) - 1;

        if (end > blockLimit)
        {
            end = blockLimit;
        }
        else if (end > partEnd)
        {
            end = partEnd;
        }

        // If this gap has not already been requested, we have found a valid entry
        if (!IsAlreadyRequested(start, end))
        {
            // Was this block to be returned
            if (result != NULL)
            {
                result->StartOffset = start;
                result->EndOffset = end;
                memcpy(result->FileID, GetFileHash(), 16);
                result->transferred = 0;
            }

            return true;
        }
        else
        {
            // Reposition to end of that gap
            start = end + 1;
        }

        // If tried all gaps then break out of the loop
        if (end == partEnd)
        {
            break;
        }
    }

    // No suitable gap found
    return false;
}

void CPartFile::FillGap(uint32_t start, uint32_t end)
{
    POSITION pos1, pos2;

    for (pos1 = gaplist.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        Gap_Struct* cur_gap = gaplist.GetNext(pos1);

        if (cur_gap->start >= start && cur_gap->end <= end)
        {
            // our part fills this gap completly
            gaplist.RemoveAt(pos2);
            delete cur_gap;
            continue;
        }
        else if (cur_gap->start >= start && cur_gap->start <= end)
        {
            // a part of this gap is in the part - set limit
            cur_gap->start = end + 1;
        }
        else if (cur_gap->end <= end && cur_gap->end >= start)
        {
            // a part of this gap is in the part - set limit
            cur_gap->end = start - 1;
        }
        else if (start >= cur_gap->start && end <= cur_gap->end)
        {
            uint32_t buffer = cur_gap->end;
            cur_gap->end = start - 1;
            cur_gap = new Gap_Struct;
            cur_gap->start = end + 1;
            cur_gap->end = buffer;
            gaplist.InsertAfter(pos1, cur_gap);
            // [Lord KiRon]:
            break;
        }
    }

    UpdateCompletedInfos();
    //theApp.xmuledlg->transferwnd->downloadlistctrl->UpdateItem(this);
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay);
    newdate = true;
    //SavePartFile();
}

void CPartFile::UpdateCompletedInfos()
{
    uint32_t allgaps = 0;

    for (POSITION pos = gaplist.GetHeadPosition(); pos != 0;)
    {
        POSITION prev = pos;
        Gap_Struct* cur_gap = gaplist.GetNext(pos);
        if ((cur_gap->end > m_nFileSize) || (cur_gap->start >= m_nFileSize))
        {
            gaplist.RemoveAt(prev);
        }
        else
        {
            allgaps += cur_gap->end - cur_gap->start;
        }
    }

    if (gaplist.GetCount() || requestedblocks_list.GetCount())
    {
        percentcompleted = (1.0f - (float) allgaps/m_nFileSize) * 100;
        completedsize = m_nFileSize - allgaps - 1;
    }
    else
    {
        percentcompleted = 100;
        completedsize = m_nFileSize;
    }
}

typedef unsigned long COLORREF;

void CPartFile::DrawStatusBar(wxMemoryDC* dc, wxRect rect, bool bFlat)
{
    COLORREF crProgress;
    COLORREF crHave;
    COLORREF crPending;
    COLORREF crMissing = RGB(255, 0, 0);

    if (bFlat)
    {
        crProgress = RGB(0, 150, 0);
        crHave = RGB(0, 0, 0);
        crPending = RGB(255, 255, 100);
    }
    else
    {
        crProgress = RGB(0, 224, 0);
        crHave = RGB(104, 104, 104);
        crPending = RGB(255, 208, 0);
    }

    s_ChunkBar.SetHeight(rect.height - rect.y);
    s_ChunkBar.SetWidth(rect.width - rect.x);
    s_ChunkBar.SetFileSize(m_nFileSize);
    s_ChunkBar.Fill(crHave);
    uint32_t allgaps = 0;

    if (status == PS_COMPLETE || status == PS_COMPLETING)
    {
        s_ChunkBar.FillRange(0, m_nFileSize, crProgress);
        s_ChunkBar.Draw(dc, rect.x, rect.y, bFlat);
        percentcompleted = 100;
        completedsize = m_nFileSize;
        return;
    }

    // red gaps
    for (POSITION pos = gaplist.GetHeadPosition(); pos != 0; gaplist.GetNext(pos))
    {
        Gap_Struct* cur_gap = gaplist.GetAt(pos);
        allgaps += cur_gap->end - cur_gap->start;
        bool gapdone = false;
        uint32_t gapstart = cur_gap->start;
        uint32_t gapend = cur_gap->end;

        for (uint32_t i = 0; i != GetPartCount(); i++)
        {
            if (gapstart >= i* PARTSIZE && gapstart <= (i + 1) * PARTSIZE)
            {
                // is in this part?
                if (gapend <= (i + 1) * PARTSIZE)
                {
                    gapdone = true;
                }
                else
                {
                    // and next part:
                    gapend = (i + 1) * PARTSIZE;
                }

                // paint
                COLORREF color;
                // frequency?:
                if (m_SrcpartFrequency.GetCount() >= (int) i && m_SrcpartFrequency[i])
                {
                    color = RGB(0, (210 - (22 * (m_SrcpartFrequency[i] - 1)) < 0) ? 0: 210 - (22 * (m_SrcpartFrequency[i] - 1)) , 255);
                }
                else
                {
                    color = crMissing;
                }

                s_ChunkBar.FillRange(gapstart, gapend + 1, color);

                // finished?:
                if (gapdone)
                {
                    break;
                }
                else
                {
                    gapstart = gapend;
                    gapend = cur_gap->end;
                }
            }
        }
    }

    // yellow pending parts
    for (POSITION pos = requestedblocks_list.GetHeadPosition(); pos != 0; requestedblocks_list.GetNext(pos))
    {
        Requested_Block_Struct* block = requestedblocks_list.GetAt(pos);
        s_ChunkBar.FillRange(block->StartOffset, block->EndOffset, crPending);
    }

    s_ChunkBar.Draw(dc, rect.x, rect.y, bFlat);
    // green progress
    float blockpixel = (float)(rect.width - rect.x) / ((float) m_nFileSize);
    RECT gaprect;
    gaprect.top = rect.y;
    gaprect.bottom = gaprect.top + 4;
    //->left;:
    gaprect.left = rect.x;

    if (!bFlat)
    {
        s_LoadBar.SetWidth((uint32_t)((float)((float)((m_nFileSize - ((allgaps == 0) ?1: allgaps)) - 1)) * blockpixel +.5f));
        s_LoadBar.Fill(crProgress);
        s_LoadBar.Draw(dc, gaprect.left, gaprect.top, false);
    }
    else
    {
        gaprect.right = rect.x + (uint32_t)((float)((float)((m_nFileSize - ((allgaps == 0) ?1: allgaps)) - 1)) * blockpixel +.5f);
        //wxBrush(crProgress));:
        dc->SetBrush(* (wxTheBrushList->FindOrCreateBrush(wxColour(crProgress), wxSOLID)));
        dc->DrawRectangle(gaprect.left, gaprect.top, gaprect.right, gaprect.bottom);
        //dc->FillRect(&gaprect, &CBrush(crProgress));
        //draw gray progress only if flat
        gaprect.left = gaprect.right;
        gaprect.right = rect.width;
        //wxBrush(crPending));:
        dc->SetBrush(* (wxTheBrushList->FindOrCreateBrush(wxColour(224, 224, 224), wxSOLID)));
        dc->DrawRectangle(gaprect.left, gaprect.top, gaprect.right, gaprect.bottom);
        //dc->FillRect(&gaprect, &CBrush(RGB(224, 224, 224)));
    }

    if ((gaplist.GetCount() || requestedblocks_list.GetCount()))
    {
        percentcompleted = ((1.0f - (float) allgaps/m_nFileSize)) * 100;
        completedsize = m_nFileSize - allgaps - 1;
    }
    else
    {
        percentcompleted = 100;
        completedsize = m_nFileSize;
    }
}

void CPartFile::WritePartStatus(CMemFile* file)
{
    uint16_t parts = hashlist.GetCount();
    file->Write(&parts, 2);
    uint16_t done = 0;

    while (done != parts)
    {
        uint8_t towrite = 0;

        for (uint32_t i = 0; i != 8; i++)
        {
            if (IsComplete(done* PARTSIZE, ((done + 1) * PARTSIZE) - 1))
            {
                towrite |= (1 << i);
            }

            ++done;

            if (done == parts)
            {
                break;
            }
        }

        file->Write(&towrite, 1);
    }
}

int CPartFile::GetValidSourcesCount()
{
    int counter = 0;
    POSITION pos1, pos2;

//    if (sources_iterations < 2)
//    {
        for (pos1 = srclist.GetHeadPosition(); (pos2 = pos1) != NULL;)
        {
            srclist.GetNext(pos1);
            CUpDownClient* cur_src = srclist.GetAt(pos2);

            if ((cur_src->GetDownloadState() != DS_ONQUEUE) && 
/*->*/          (cur_src->GetDownloadState() != DS_DOWNLOADING) && 
/*->*/          (cur_src->GetDownloadState() != DS_NONEEDEDPARTS))
            {
                ++counter;
            }
        }

//        valid_sources = counter;
//        ++sources_iterations;
//    }
//    else
//    {
//        sources_iterations = 0;
//    }

    return counter;
}

uint16_t CPartFile::GetNotCurrentSourcesCount()
{
    uint16_t counter = 0;
    POSITION pos1, pos2;
    for (pos1 = srclist.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        srclist.GetNext(pos1);
        CUpDownClient* cur_src = srclist.GetAt(pos2);

        if ((cur_src->GetDownloadState() != DS_ONQUEUE) && 
/*->*/      (cur_src->GetDownloadState() != DS_DOWNLOADING))
        {
            counter++;
        }
    }

    return counter;
}

uint8_t CPartFile::GetStatus(bool ignorepause)
{
    if (!paused || ignorepause || status == PS_ERROR)
    {
        return status;
    }

    return PS_PAUSED;
}

uint32_t CPartFile::Process(uint32_t reducedownload)
{
    //in percent
    uint16_t old_trans;
    unsigned long dwCurTick = ::GetTickCount();

    // If buffer size exceeds limit, or if not written within time limit, flush data
    if ((m_nTotalBufferData > theApp.dynprefs->Get<long>("file-buffer-size")))
    {
        // Avoid flushing while copying preview file
        if (!m_bPreviewing)
        {
            FlushBuffer();
        }
    }

    // check if we want new sources from server
    //uint16_t test = theApp.glob_prefs->GetMaxSourcePerFileSoft();
    double source_count(this->GetSourceCount());

    double max_sources(theApp.dynprefs->Get<long>("max-sources"));
    double max_sources_75 = max_sources * 0.75;
    double max_sources_85 = max_sources * 0.85;

    if (((!lastsearchtime)
/*->*/  || (dwCurTick - lastsearchtime) > SERVERREASKTIME)
/*->*/  && theApp.serverconnect->IsConnected()
/*->*/  && (source_count < max_sources_85)
/*->*/  && !stopped)
    {
        //local server
        lastsearchtime = dwCurTick;
        Packet* packet = new Packet(OP_GETSOURCES, 16);
        memcpy(packet->pBuffer, m_abyFileHash, 16);
        theApp.uploadqueue->AddUpDataOverheadServer(packet->size);
        theApp.serverconnect->SendPacket(packet, true);
    }

    old_trans = transferingsrc;
    transferingsrc = 0;
    datarate = 0;
    POSITION pos1, pos2;

    for (pos1 = srclist.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        srclist.GetNext(pos1);
        CUpDownClient* cur_src = srclist.GetAt(pos2);

        switch (cur_src->GetDownloadState())
        {
            case DS_DOWNLOADING:
            {
                transferingsrc++;
                uint32_t cur_datarate = cur_src->CalculateDownloadRate();
                datarate += cur_datarate;

                if (reducedownload && cur_src->GetDownloadState() == DS_DOWNLOADING)
                {
                    //(uint32_t)(((float)reducedownload/100) *cur_datarate)/10;:
                    uint32_t limit = reducedownload * cur_datarate / 1000;

                    if (limit < 1000 && reducedownload == 200)
                    {
                        limit += 1000;
                    }
                    else if (limit < 1)
                    {
                        limit = 1;
                    }

                    cur_src->socket->SetDownloadLimit(limit);
                }
                else
                {
                    cur_src->socket->DisableDownloadLimit();
                }

                cur_src->SetValidSource(true);
                break;
            }
            case DS_BANNED:
            case DS_ERROR:
                break;
            // if we now have a high ip we can ask:
            case DS_LOWTOLOWIP:
            {
                if (((dwCurTick - lastpurgetime) > 30000) && (source_count >= max_sources_75))
                {
                    theApp.downloadqueue->RemoveSource(cur_src);
                    lastpurgetime = dwCurTick;
                    break;
                }

                if (theApp.serverconnect->IsLowID())
                {
                    break;
                }
            }
            case DS_NONEEDEDPARTS:
            {
/* No needed sources are dropped if over 80% of available source slots
are full, once per 40 seconds.*/
                cur_src->SetValidSource(false);

                if (!cur_src->SwapToAnotherFile() 
/*->*/                      && ((dwCurTick - lastpurgetime) > 40000)
/*->*/                      && (source_count >= max_sources_75))
                {
                    theApp.downloadqueue->RemoveSource(cur_src);
                    lastpurgetime = dwCurTick;
                    //Johnny-B - nothing more to do here (good eye!):
                    break;
                }

                // doubled reasktime for no needed parts - save connections and traffic
                if (!((!cur_src->GetLastAskedTime()) || (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME* 2))
                {
                    break;
                }
            }
            case DS_ONQUEUE:
            {
/* Full queue sources are dropped if over 80% of available source slots
are filled, once per 60 seconds.*/
                cur_src->SetValidSource(true);

                if (cur_src->IsRemoteQueueFull())
                {
                    cur_src->SetValidSource(false);
                }

                if (((dwCurTick - lastpurgetime) > 60000) && (source_count >= max_sources_75))
                {
                    theApp.downloadqueue->RemoveSource(cur_src);
                    lastpurgetime = dwCurTick;
                    //Johnny-B - nothing more to do here (good eye!):
                    break;
                }

                if (theApp.serverconnect->IsConnected()
/*->*/                      && ((!cur_src->GetLastAskedTime())
/*->*/                      || (dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME - 20000))
                {
                    cur_src->UDPReaskForDownload();
                }
            }
            case DS_CONNECTED:
            case DS_CONNECTING:
            {
                if ((dwCurTick - cur_src->GetLastAskedTime()) > FILEREASKTIME)
                {
                    cur_src->AskForDownload();
                }

                break;
            }
            case DS_NONE:
            case DS_TOOMANYCONNS:
            case DS_WAITCALLBACK:
            {
                if ((dwCurTick - cur_src->GetLastAskedTime()) > CONNECTION_TIMEOUT)
                {
                    cur_src->AskForDownload();
                }

                break;
            }
        }
    }

    // swap No needed partfiles if possible
    if (((!m_LastNoNeededCheck) || (dwCurTick - m_LastNoNeededCheck) > 10000))
    {
        m_LastNoNeededCheck = dwCurTick;

        if (this->IsA4AFAuto())
        {
            POSITION pos1, pos2;

            for (pos1 = this->A4AFSourcesList.GetHeadPosition(); (pos2 = pos1) != NULL;)
            {
                this->A4AFSourcesList.GetNext(pos1);
                CUpDownClient* cur_source = this->A4AFSourcesList.GetAt(pos2);

                if (!(cur_source->GetDownloadState() == DS_DOWNLOADING))
                {
                    this->A4AFSourcesList.RemoveAt(pos2);
                    cur_source->SwapToThisFile(this);
                }
            }
        }
    }

    /* Sources droping engine. Auto drop allowed type of sources at interval.*/
    if ((dwCurTick - m_LastSourceDropTime) > theApp.glob_prefs->GetAutoDropTimer() * 1000)
    {
        m_LastSourceDropTime = dwCurTick;

    /* If all three are enabled, use CleanUpSources() function, will save us some CPU.*/
        if (theApp.glob_prefs->DropNoNeededSources() && theApp.glob_prefs->DropFullQueueSources() && theApp.glob_prefs->DropHighQueueRankingSources())
        {
            CleanUpSources();
        }
        else
        {
    /* Then check separately for each of them, and act accordingly.*/
            if (theApp.glob_prefs->DropNoNeededSources())
            {
                RemoveNoNeededSources();
            }

            if (theApp.glob_prefs->DropFullQueueSources())
            {
                RemoveFullQueueSources();
            }

            if (theApp.glob_prefs->DropHighQueueRankingSources())
            {
                RemoveHighQueueRatingSources();
            }
        }
    }

    if (((old_trans == 0) && (transferingsrc > 0)) || ((old_trans > 0) && (transferingsrc == 0)))
    {
        SetPartFileStatus(this);
    }

    // calculate datarate, set limit etc.
    count++;

    if (count == 30)
    {
        count = 0;
        //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
        UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay);

        if (m_bPercentUpdated == false)
        {
            UpdateCompletedInfos();
        }

        m_bPercentUpdated = false;
    }

    return datarate;
}

void CPartFile::AddSources(CMemFile* sources, uint32_t serverip, uint16_t serverport)
{
    if (stopped)
    {
        return;
    }

    uint8_t count;
    uint8_t debug_lowiddropped = 0;
    uint8_t debug_possiblesources = 0;
    sources->Read(&count, 1);

    uint16_t max_sources(theApp.dynprefs->Get<long>("max-sources"));
double source_count(this->GetSourceCount());
    for (int i = 0; i != count; i++)
    {
        uint32_t userid;
        sources->Read(&userid, 4);
        uint16_t port;
        sources->Read(&port, 2);

        // check first if we are this source
        // MOD Note: Do not change this part - Merkur
        if (theApp.serverconnect->GetClientID() < 16777216 && theApp.serverconnect->IsConnected())
        {
            if ((theApp.serverconnect->GetClientID() == userid) && inet_addr(theApp.serverconnect->GetCurrentServer()->GetFullIP()) == serverip)
            {
                continue;
            }
        }
        else if (theApp.serverconnect->GetClientID() == userid)
        {
            continue;
        }
        else if (userid < 16777216 && !theApp.serverconnect->IsLocalServer(serverip, serverport))
        {
            debug_lowiddropped++;
            continue;
        }

        // MOD Note - end
        if (max_sources > source_count)
        {
            debug_possiblesources++;
            CUpDownClient* newsource = new CUpDownClient(port, userid, serverip, serverport, this);
            theApp.downloadqueue->CheckAndAddSource(this, newsource);
        }
    }
}

void CPartFile::NewSrcPartsInfo()
{
    // Cache part count
    uint16_t partcount = GetPartCount();

    // Increase size if necessary
    if (m_SrcpartFrequency.GetSize() < partcount)
    {
        m_SrcpartFrequency.SetSize(partcount);
    }

    // Reset part counters
    for (int i = 0; i < partcount; i++)
    {
        m_SrcpartFrequency[i] = 0;
    }
    CUpDownClient* cur_src;

    for (POSITION pos = srclist.GetHeadPosition(); pos != 0;)
    {
        cur_src = srclist.GetNext(pos);

        for (int i = 0; i != partcount; i++)
        {
            if (cur_src->IsPartAvailable(i))
            {
                m_SrcpartFrequency[i] += 1;
            }
        }
    }

    //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay);
}

bool CPartFile::GetNextRequestedBlock(CUpDownClient* sender, Requested_Block_Struct* *newblocks, uint16_t* count)
{
    uint16_t requestedCount = *count;
    uint16_t newblockcount = 0;
    uint8_t* partsav = sender->GetPartStatus();
    *count = 0;
    uint16_t randomness;
    CList < int, int > liGoodParts;
    CList < int, int > liPossibleParts;
    bool finished = false;

    while (!finished)
    {
        // Need to empty lists to avoid infinite loop when file is smaller than 180k
        // Otherwise it would keep looping to find 3 blocks, there is only one and it is requested
        liGoodParts.RemoveAll();
        liPossibleParts.RemoveAll();
        // Barry - Top priority should be to continue downloading from current blocks (if anything left to download)
        bool foundPriorityPart = false;
        wxString gettingParts;
        sender->ShowDownloadingParts(gettingParts);

        for (int i = (gettingParts.Len() - 1); i >= 0; i--)
        {
            if ((gettingParts.GetChar(i) == 'Y') && (GetNextEmptyBlockInPart(i, 0)))
            {
                liGoodParts.AddHead(i);
                foundPriorityPart = true;
            }
        }

        // Barry - Give priorty to end parts of archives and movies
        if ((theApp.dynprefs->Get<bool>("last-chunk-first") == true) && (!foundPriorityPart) && (IsArchive() || IsMovie()))
        {
            uint32_t partCount = GetPartCount();

            // First part
            if (sender->IsPartAvailable(0) && GetNextEmptyBlockInPart(0, 0))
            {
                liGoodParts.AddHead(0);
                foundPriorityPart = true;
            }
            else if ((partCount > 1))
            {
                // Last part
                if (sender->IsPartAvailable(partCount - 1) && GetNextEmptyBlockInPart(partCount - 1, 0))
                {
                    liGoodParts.AddHead(partCount - 1);
                    foundPriorityPart = true;
                }
                // Barry - Better to get rarest than these, add to list, but do not exclude all others.
                // These get priority over other parts with same availability.
                else if (partCount > 2)
                {
                    // Second part
                    if (sender->IsPartAvailable(1) && GetNextEmptyBlockInPart(1, 0))
                    {
                        liGoodParts.AddHead(1);
                    }
                    // Penultimate part
                    else if (sender->IsPartAvailable(partCount - 2) && GetNextEmptyBlockInPart(partCount - 2, 0))
                    {
                        liGoodParts.AddHead(partCount - 2);
                    }
                }
            }
        }

        if (!foundPriorityPart)
        {
            randomness = (uint16_t) ROUND(((float) rand() /RAND_MAX) * (GetPartCount() - 1));
            for (uint16_t i = 0; i != GetPartCount(); i++)
            {
                if (sender->IsPartAvailable(randomness))
                {
                    if (partsav[randomness] && !IsComplete(randomness* PARTSIZE, ((randomness + 1) * PARTSIZE) - 1))
                    {
 /*if (IsCorruptedPart(randomness)){
 if (GetNextEmptyBlockInPart(randomness, 0)){
 goodpart = randomness;
 break;
 }
 }
    else*/
                        if (IsPureGap(randomness* PARTSIZE, ((randomness + 1) * PARTSIZE) - 1))
                        {
                            if (GetNextEmptyBlockInPart(randomness, 0))
                            {
                                liPossibleParts.AddHead(randomness);
                            }
                        }
                        else if (GetNextEmptyBlockInPart(randomness, 0))
                        {
                            // Barry - Add after archive/movie entries:
                            liGoodParts.AddTail(randomness);
                        }
                    }
                }

                randomness++;

                if (randomness == GetPartCount())
                {
                    randomness = 0;
                }
            }
        }

        CList < int, int >* usedlist;

        if (!liGoodParts.IsEmpty())
        {
            usedlist = &liGoodParts;
        }
        else if (!liPossibleParts.IsEmpty())
        {
            usedlist = &liPossibleParts;
        }
        else
        {
            if (!newblockcount)
            {
                return false;
            }

            else
            break;
        }

        uint16_t nRarest = 0xFFFF;
        uint16_t usedpart = usedlist->GetHead();

        for (POSITION pos = usedlist->GetHeadPosition(); pos != 0; usedlist->GetNext(pos))
        {
            if (m_SrcpartFrequency.GetCount() >= usedlist->GetAt(pos) && m_SrcpartFrequency[usedlist->GetAt(pos) ] < nRarest)
            {
                nRarest = m_SrcpartFrequency[usedlist->GetAt(pos) ];
                usedpart = usedlist->GetAt(pos);
            }
        }

        while (true)
        {
            Requested_Block_Struct* block = new Requested_Block_Struct;

            if (GetNextEmptyBlockInPart(usedpart, block))
            {
                requestedblocks_list.AddTail(block);
                newblocks[newblockcount] = block;
                newblockcount++;

                if (newblockcount == requestedCount)
                {
                    finished = true;
                    break;
                }
            }
            else
            {
                delete block;
                break;
            }
        }
        //wend:
    }

    *count = newblockcount;
    return true;
}

void CPartFile::RemoveBlockFromList(uint32_t start, uint32_t end)
{
    POSITION pos1, pos2;

    for (pos1 = requestedblocks_list.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        requestedblocks_list.GetNext(pos1);

        if (requestedblocks_list.GetAt(pos2)->StartOffset <= start && requestedblocks_list.GetAt(pos2)->EndOffset >= end)
        {
            requestedblocks_list.RemoveAt(pos2);
        }
    }
}

void CPartFile::RemoveAllRequestedBlocks(void)
{
    requestedblocks_list.RemoveAll();
}

#include <pthread.h>
pthread_attr_t pattr;

void CPartFile::CompleteFile(bool bIsHashingDone)
{
    if (this->srcarevisible)
    {
        theApp.xmuledlg->transferwnd->downloadlistctrl->HideSources(this);
    }

    status = PS_COMPLETING;
    SetPartFileStatus(this);

    if (!bIsHashingDone)
    {
        datarate = 0;
        char* partfileb = nstrdup(partmetfilename);
        partfileb[strlen(partmetfilename) - 4] = 0;
        CAddFileThread::AddFile(theApp.dynprefs->Get<wxString>("temp-directory"), partfileb, this);
        delete[] partfileb;
        return;
    }
    else
    {
        StopFile();
        // guess I was wrong about not need to spaw a thread.. It is if the temp and incoming dirs are on different partitions/drives and the file is large...[oz]
        // use pthreads
        pthread_t tid;
        pthread_attr_init(&pattr);
        pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);
        pthread_create(&tid, &pattr, (void* ( *)(void *)) CompleteThreadProc, this);
    }

    theApp.xmuledlg->transferwnd->downloadlistctrl->ShowFilesCount();
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay, true);
}

UINT CPartFile::CompleteThreadProc(CPartFile* pFile)
{
    if (!pFile)
    {
        return 1;
    }

    pFile->PerformFileComplete();
    return 0;
}

// Lord KiRon - using threads for file completion
bool CPartFile::PerformFileComplete()
{
    //CSingleLock(&m_FileCompleteMutex, TRUE); // will be unlocked on exit
    wxMutexLocker sLock(m_FileCompleteMutex);
    char* partfilename = nstrdup(fullname);
    // assumes ".met" at the end:
    partfilename[strlen(fullname) - 4] = 0;
    char* newfilename = nstrdup(GetFileName());
    strcpy(newfilename, theApp.StripInvalidFilenameChars(newfilename));
    // ???:
    wxString newname;
    wxString indir;

    if (wxFileName::DirExists(theApp.glob_prefs->GetCategory(GetCategory())->incomingpath))
    {
        indir = theApp.glob_prefs->GetCategory(GetCategory())->incomingpath;
        newname = indir << wxT("/") << newfilename;
    }
    else
    {
        indir = theApp.dynprefs->Get<wxString>("incoming-directory");
        newname = indir << wxT("/") << newfilename;
    }

    // add this file to the suspended uploads list
    CFileHash tmp_filehash(GetFileHash());
    theApp.uploadqueue->SuspendUpload(tmp_filehash);
    FlushBuffer();
    // close permanent handle
    m_hpartfile.Close();

    bool renamed = false;

    if (wxFileName::FileExists(newname))
    {
        renamed = true;
        int namecount = 0;
        size_t length = strlen(newfilename);
        //the file extension
        char* ext = strrchr(newfilename, '.');

        if (ext == NULL)
        {
            ext = newfilename + length;
        }

        //new end is the file name before extension:
        char* last = ext;
        //truncate file name:
        last[0] = 0;
        //serch for matching ()s and check if it contains a number

        if ((ext != newfilename) && (strrchr(newfilename, ')') + 1 == last))
        {
            char* first = strrchr(newfilename, '(');

            if (first != NULL)
            {
                first++;
                bool found = true;
                for (char* step = first; step < last - 1; step++)

                if (* step < '0' ||* step > '9')
                {
                    found = false;
                    break;
                }

                if (found)
                {
                    namecount = atoi(first);
                    last = first - 1;
                    //truncate again:
                    last[0] = 0;
                }
            }
        }

        wxString strTestName;

        do
        {
            namecount++;
            strTestName.Printf(
/*->*/          theApp.dynprefs->Get<wxString>("incoming-directory") + "/%s(%d).%s", 
/*->*/          newfilename, 
/*->*/          namecount, 
/*->*/          min(ext + 1, newfilename + length));
        }
        while (wxFileName::FileExists(strTestName));

        newname = strTestName;
    }

    delete[] newfilename;

    if (!wxRenameFile(partfilename, newname))
    {
        if (!SafeCopyFile(partfilename, newname))
        {
            delete[] partfilename;
            wxMutexGuiEnter();
            theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_COMPLETIONFAILED), GetFileName());
            wxMutexGuiLeave();
            paused = true;
            status = PS_ERROR;
            SetPartFileStatus(this);
            wxMutexGuiEnter();
            theApp.downloadqueue->StartNextFile();
            wxMutexGuiLeave();
            return FALSE;
        }

        if (!wxRemoveFile(partfilename))
        {
            printf("info: could not remove original '%s' after creating backup\n", partfilename);
        }
    }

    if (!wxRemoveFile(fullname))
    {
        wxMutexGuiEnter();
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_DELETEFAILED), fullname);
        wxMutexGuiLeave();
    }
    wxString BAKName(fullname);
    BAKName.Append(".BAK");

    if (!wxRemoveFile(BAKName))
    {
        wxMutexGuiEnter();
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_DELETE), BAKName.GetData());
        wxMutexGuiLeave();
    }

    delete[] partfilename;
    delete [] fullname;
    fullname = strdup(newname.c_str());
    delete[] directory;
    directory = nstrdup(theApp.glob_prefs->GetCategory(m_category)->incomingpath);
    status = PS_COMPLETE;
    SetPartFileStatus(this);
    paused = false;
    wxMutexGuiEnter();
    theApp.xmuledlg->AddLogLine(true, GetResString(IDS_DOWNLOADDONE), GetFileName());
    theApp.xmuledlg->ShowNotifier(GetResString(IDS_TBN_DOWNLOADDONE) + "\n" + GetFileName(), TBN_DLOAD);
    wxMutexGuiLeave();

    if (renamed)
    {
        wxMutexGuiEnter();
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_DOWNLOADRENAMED), newname.c_str());
        wxMutexGuiLeave();
    }

    // TODO: What the f*** if it is already known?
    theApp.knownfiles->SafeAddKFile(this);
    // remove the file from the suspended uploads list
    theApp.uploadqueue->ResumeUpload(tmp_filehash);
    SetAutoUpPriority(false);
    //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    wxMutexGuiEnter();
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay);
    theApp.xmuledlg->transferwnd->downloadlistctrl->ShowFilesCount();
    theApp.downloadqueue->RemoveFile(this);
    theApp.downloadqueue->StartNextFile();
    wxMutexGuiLeave();
    return TRUE;
}

void CPartFile::RemoveAllSources(bool bTryToSwap)
{
    //TODO transfer sources to other downloading files if possible
    POSITION pos1, pos2;

    for (pos1 = srclist.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        srclist.GetNext(pos1);

        if (bTryToSwap)
        {
            if (!srclist.GetAt(pos2)->SwapToAnotherFile())
            {
                theApp.downloadqueue->RemoveSource(srclist.GetAt(pos2));
            }
        }
        else
        {
            theApp.downloadqueue->RemoveSource(srclist.GetAt(pos2));
        }
    }

    /* Razor 1a - Modif by MikaelB*/
    if (!this->A4AFSourcesList.IsEmpty())
    {
        POSITION pos1, pos2;

        for (pos1 = this->A4AFSourcesList.GetHeadPosition(); (pos2 = pos1) != NULL;)
        {
            this->A4AFSourcesList.GetNext(pos1);
            POSITION pos3 = this->A4AFSourcesList.GetAt(pos2)->m_OtherRequests_list.Find(this);

            if (pos3)
            {
                this->A4AFSourcesList.GetAt(pos2)->m_OtherRequests_list.RemoveAt(pos3);
                theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveSource(this->A4AFSourcesList.GetAt(pos2), this);
                this->A4AFSourcesList.RemoveAt(pos2);
            }
        }
    }

    /* End modif*/
    UpdateFileRatingCommentAvail();
}

void CPartFile::DeleteFile()
{
    // Barry - Need to tell any connected clients to stop sending the file
    StopFile();
    theApp.downloadqueue->RemoveFile(this);
    theApp.xmuledlg->transferwnd->downloadlistctrl->RemoveFile(this);
    m_hpartfile.Close();

    cout << "remove file -" << fullname << "-\n";
    if (!wxRemoveFile(fullname))
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_DELETE), fullname);
    }

    char* partfilename = nstrdup(fullname);
    partfilename[strlen(fullname) - 4] = 0;

    if (!wxRemoveFile(partfilename))
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_DELETE), partfilename);
    }

    wxString BAKName(fullname);
    BAKName.Append(".BAK");

    if (!wxRemoveFile(BAKName))
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_DELETE), BAKName.GetData());
    }

    delete[] partfilename;
    theApp.sharedfiles->RemoveFile(this);
}

bool CPartFile::HashSinglePart(uint16_t partnumber)
{
    if ((GetHashCount() <= partnumber) && (GetPartCount() > 1))
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_HASHERRORWARNING), GetFileName());
        this->hashsetneeded = true;
        return true;
    }
    else if (!GetPartHash(partnumber) && GetPartCount() != 1)
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_INCOMPLETEHASH), GetFileName());
        this->hashsetneeded = true;
        return true;
    }
    else
    {
        unsigned char hashresult[16];
        m_hpartfile.Seek(PARTSIZE* partnumber);
        uint32_t length = PARTSIZE;

        if (PARTSIZE* (partnumber + 1) > m_hpartfile.Length())
        {
            length = (m_hpartfile.Length() - (PARTSIZE* partnumber));
        }

        CreateHashFromFile(&m_hpartfile, length, hashresult);

        if (GetPartCount() > 1)
        {
            if (memcmp(hashresult, GetPartHash(partnumber), 16))
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        else
        {
            if (memcmp(hashresult, m_abyFileHash, 16))
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
}

bool CPartFile::IsCorruptedPart(uint16_t partnumber)
{
    return corrupted_list.Find(partnumber);
}

bool CPartFile::IsMovie()
{
    bool it_is;
    wxString extension = wxString(GetFileName()).Right(5);
    it_is = ((extension.CmpNoCase(".divx") == 0) || (extension.CmpNoCase(".mpeg") == 0) ||
    (extension.CmpNoCase(".vivo") == 0));
    extension = wxString(GetFileName()).Right(4);
    it_is = (it_is || (extension.CmpNoCase(".avi") == 0) || (extension.CmpNoCase(".mpg") == 0) ||
    (extension.CmpNoCase(".m2v") == 0) || (extension.CmpNoCase(".wmv") == 0) ||
    (extension.CmpNoCase(".asf") == 0) || (extension.CmpNoCase(".mov") == 0) ||
    (extension.CmpNoCase(".bin") == 0) || (extension.CmpNoCase(".swf") == 0) ||
    (extension.CmpNoCase(".ogm") == 0));
    extension = wxString(GetFileName()).Right(3);
    it_is = (it_is || (extension.CmpNoCase(".rm") == 0) || (extension.CmpNoCase(".qt") == 0));
    return(it_is);
}

bool CPartFile::IsArchive()
{
    bool it_is;
    wxString extension = wxString(GetFileName()).Right(4);
    it_is = ((extension.CmpNoCase(".zip") == 0) || (extension.CmpNoCase(".rar") == 0) ||
    (extension.CmpNoCase(".ace") == 0) || (extension.CmpNoCase(".arj") == 0) ||
    (extension.CmpNoCase(".lhz") == 0) || (extension.CmpNoCase(".tar") == 0) ||
    (extension.CmpNoCase(".bz2") == 0));
    extension = wxString(GetFileName()).Right(3);
    it_is = (it_is || (extension.CmpNoCase(".gz") == 0) || (extension.CmpNoCase(".bz") == 0));
    return(it_is);
}

bool CPartFile::IsSound()
{
    bool it_is;
    wxString extension = wxString(GetFileName()).Right(4);
    it_is = ((extension.CmpNoCase(".mp3") == 0) || (extension.CmpNoCase(".mp2") == 0) ||
    (extension.CmpNoCase(".wma") == 0) || (extension.CmpNoCase(".ogg") == 0) ||
    (extension.CmpNoCase(".rma") == 0) || (extension.CmpNoCase(".wav") == 0) ||
    (extension.CmpNoCase(".mid") == 0));
    return(it_is);
}

bool CPartFile::IsCDImage()
{
    bool it_is;
    wxString extension = wxString(GetFileName()).Right(4);
    it_is = ((extension.CmpNoCase(".bin") == 0) || (extension.CmpNoCase(".cue") == 0) ||
    (extension.CmpNoCase(".nrg") == 0) || (extension.CmpNoCase(".ccd") == 0) ||
    (extension.CmpNoCase(".img") == 0) || (extension.CmpNoCase(".iso") == 0));
    return(it_is);
}

bool CPartFile::IsImage()
{
    bool it_is;
    wxString extension = wxString(GetFileName()).Right(5);
    it_is = (extension.CmpNoCase(".jpeg") == 0) || (extension.CmpNoCase(".tiff") == 0);
    extension = wxString(GetFileName()).Right(4);
    it_is = (it_is || (extension.CmpNoCase(".jpg") == 0) || (extension.CmpNoCase(".gif") == 0) ||
    (extension.CmpNoCase(".bmp") == 0) || (extension.CmpNoCase(".rle") == 0) ||
    (extension.CmpNoCase(".psp") == 0) || (extension.CmpNoCase(".tga") == 0) ||
    (extension.CmpNoCase(".wmf") == 0) || (extension.CmpNoCase(".xpm") == 0) ||
    (extension.CmpNoCase(".png") == 0) || (extension.CmpNoCase(".pcx") == 0));
    return(it_is);
}

bool CPartFile::IsText()
{
    bool it_is;
    wxString extension = wxString(GetFileName()).Right(5);
    it_is = (extension.CmpNoCase(".html") == 0);
    extension = wxString(GetFileName()).Right(4);
    it_is = (it_is || (extension.CmpNoCase(".doc") == 0) || (extension.CmpNoCase(".txt") == 0) ||
    (extension.CmpNoCase(".pdf") == 0) || (extension.CmpNoCase(".ps") == 0) ||
    (extension.CmpNoCase(".htm") == 0) || (extension.CmpNoCase(".sxw") == 0) ||
    (extension.CmpNoCase(".log") == 0));
    return(it_is);
}

void CPartFile::SetDownPriority(uint8_t np)
{
    m_iDownPriority = np;
    theApp.downloadqueue->SortByPriority();
    //theApp.xmuledlg->transferwnd->downloadlistctrl->UpdateItem(this);
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay, true);
    SavePartFile();
}

void CPartFile::StopFile()
{
    // Barry - Need to tell any connected clients to stop sending the file
    // Kry - Need to set it hereto get into SetPartFileStatus(this, status) correctly:
    stopped = true;
    PauseFile();
    RemoveAllSources(true);
    datarate = 0;
    transferingsrc = 0;
    FlushBuffer();
    //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay, true);
}

void CPartFile::PauseFile()
{
    if (status == PS_COMPLETE || status == PS_COMPLETING)
    {
        return;
    }

    Packet* packet = new Packet(OP_CANCELTRANSFER, 0);
    POSITION pos1, pos2;

    for (pos1 = srclist.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        srclist.GetNext(pos1);
        CUpDownClient* cur_src = srclist.GetAt(pos2);

        if (cur_src->GetDownloadState() == DS_DOWNLOADING)
        {
            theApp.uploadqueue->AddUpDataOverheadOther(packet->size);
#if defined(__DEBUG__)
            // printf("CPartFile::PauseFile OP_CANCELTRANSFER size\n", packet->size);
#endif
            cur_src->socket->SendPacket(packet, false, true);
            cur_src->SetDownloadState(DS_ONQUEUE);
        }
    }

    delete packet;
    paused = true;

    SetPartFileStatus(this);
    datarate = 0;
    transferingsrc = 0;
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay, true);
    SavePartFile();
}

void CPartFile::ResumeFile()
{
    if (status == PS_COMPLETE || status == PS_COMPLETING)
    {
        return;
    }

    paused = false;
    stopped = false;
    lastsearchtime = 0;
    SetPartFileStatus(this);
    //theApp.xmuledlg->transferwnd->downloadlistctrl->UpdateItem(this);
    UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay, true);
}

wxString CPartFile::getPartfileStatus()
{
    wxString mybuffer = "";

    if (GetTransferingSrcCount() > 0)
    {
        mybuffer = GetResString(IDS_DOWNLOADING);
    }
    else
    {
        mybuffer = GetResString(IDS_WAITING);
    }

    switch (GetStatus())
    {
        case PS_HASHING:
        case PS_WAITINGFORHASH:
            mybuffer = GetResString(IDS_HASHING);
            break;
        case PS_COMPLETING:
            mybuffer = GetResString(IDS_COMPLETING);
            break;
        case PS_COMPLETE:
            mybuffer = GetResString(IDS_COMPLETE);
            break;
        case PS_PAUSED:
            mybuffer = GetResString(IDS_PAUSED);
            break;
        case PS_ERROR:
            mybuffer = GetResString(IDS_ERRORLIKE);
            break;
    }

    if (stopped && (GetStatus() != PS_COMPLETE))
    {
        mybuffer = GetResString(IDS_STOPPED);
    }

    return mybuffer;
}

int CPartFile::getPartfileStatusRang()
{
    int tempstatus = 0;

    if (GetTransferingSrcCount() == 0)
    {
        tempstatus = 1;
    }

    switch (GetStatus())
    {
        case PS_HASHING:
        case PS_WAITINGFORHASH:
            tempstatus = 3;
            break;
        case PS_COMPLETING:
            tempstatus = 4;
            break;
        case PS_COMPLETE:
            tempstatus = 5;
            break;
        case PS_PAUSED:
            tempstatus = 2;
            break;
        case PS_ERROR:
            tempstatus = 6;
            break;
    }

    return tempstatus;
}

int32_t CPartFile::getTimeRemaining()
{
    if (GetDatarate() == 0)
    {
        return -1;
    }

    return ((GetFileSize() - GetCompletedSize()) / GetDatarate());
}

void CPartFile::PreviewFile()
{
    wxString command;
    // If no player set in preferences, use mplayer.
    if (theApp.glob_prefs->GetVideoPlayer() == "")
    {
        command.Append(wxT("mplayer"));
    }
    else
    {
        command.Append(theApp.glob_prefs->GetVideoPlayer());
    }

    // Need to use quotes in case filename contains spaces.
    command.Append(wxT(" \""));
    command.Append(GetFullName());

    // Remove the.met from filename.
    for (int i = 0; i < 4; i++)
    {
        command.RemoveLast();
    }

    command.Append(wxT("\""));
    wxShell(command.c_str());
}

bool CPartFile::PreviewAvailable()
{
    return(IsMovie() && IsComplete(0, PARTSIZE));
}

void CPartFile::UpdateAvailablePartsCount()
{
    uint8_t availablecounter = 0;
    bool breakflag = false;
    uint16_t iPartCount = GetPartCount();

    for (uint32_t ixPart = 0; ixPart < iPartCount; ixPart++)
    {
        breakflag = false;

        for (POSITION pos = srclist.GetHeadPosition(); pos && !breakflag;)
        {
            if (srclist.GetNext(pos)->IsPartAvailable(ixPart))
            {
                availablecounter++;
                breakflag = true;
            }
        }
    }

    if (iPartCount == availablecounter && availablePartsCount < iPartCount)
    {
        //CTime::GetCurrentTime();:
        lastseencomplete = time(NULL);
    }

    availablePartsCount = availablecounter;
}

Packet* CPartFile::CreateSrcInfoPacket(CUpDownClient* forClient)
{
#if defined(__DEBUG__)
    // printf("CPartFile::CreateSrcInfoPacket\n");
#endif
    if (srclist.IsEmpty())
    {
        return 0;
    }

    CMemFile data;
    uint16_t nCount = 0;
    data.Write(m_abyFileHash, 16);
    data.Write(&nCount, 2);
    bool bNeeded;

    for (POSITION pos = srclist.GetHeadPosition(); pos != 0; srclist.GetNext(pos))
    {
        bNeeded = false;
        CUpDownClient* cur_src = srclist.GetAt(pos);

        if (cur_src->HasLowID())
        {
            continue;
        }

        // only send source which have needed parts for this client if possible
        uint8_t* srcstatus = cur_src->GetPartStatus();
        if (srcstatus)
        {
            uint8_t* reqstatus = forClient->GetPartStatus();

            if (reqstatus)
            {
                // only send sources which have needed parts for this client
                for (int x = 0; x < GetPartCount(); x++)
                {
                    if (srcstatus[x] && !reqstatus[x])
                    {
                        bNeeded = true;
                        break;
                    }
                }
            }
            else
            {
                // if we don't know the need parts for this client, return any source
                // currently a client sends it's file status only after it has at least one complete part, 
                for (int x = 0; x < GetPartCount(); x++)
                {
                    if (srcstatus[x])
                    {
                        bNeeded = true;
                        break;
                    }
                }
            }
        }

        if (bNeeded)
        {
            nCount++;
            uint32_t dwID = cur_src->GetUserID();
            uint16_t nPort = cur_src->GetUserPort();
            uint32_t dwServerIP = cur_src->GetServerIP();
            uint16_t nServerPort = cur_src->GetServerPort();
            data.Write(&dwID, 4);
            data.Write(&nPort, 2);
            data.Write(&dwServerIP, 4);
            data.Write(&nServerPort, 2);
            if (forClient->GetSourceExchangeVersion() > 1)
            {
                data.Write(cur_src->GetUserHash(), 16);
            }

            if (nCount > 500)
            {
                break;
            }
        }
    }

    if (!nCount)
    {
        return 0;
    }

    data.Seek(16);
    data.Write(&nCount, 2);
    Packet* result = new Packet(&data, OP_EMULEPROT);
    result->opcode = OP_ANSWERSOURCES;

    if (nCount > 28)
    {
        result->PackPacket();
    }

    return result;
}

void CPartFile::AddClientSources(CMemFile* sources, uint8_t sourceexchangeversion)
{
    if (stopped)
    {
        return;
    }

    uint16_t nCount;
    sources->Read(&nCount, 2);

    for (int i = 0; i != nCount; i++)
    {
        uint32_t dwID;
        uint16_t nPort;
        uint32_t dwServerIP;
        uint16_t nServerPort;
        unsigned char achUserHash[16];
        sources->Read(&dwID, 4);
        sources->Read(&nPort, 2);
        sources->Read(&dwServerIP, 4);
        sources->Read(&nServerPort, 2);

        if (sourceexchangeversion > 1)
        {
            sources->Read(achUserHash, 16);
        }

        // check first if we are this source
        if (theApp.serverconnect->GetClientID() < 16777216 && theApp.serverconnect->IsConnected())
        {
            if ((theApp.serverconnect->GetClientID() == dwID) && theApp.serverconnect->GetCurrentServer()->GetIP() == dwServerIP)
            {
                continue;
            }
        }
        else if (theApp.serverconnect->GetClientID() == dwID)
        {
            continue;
        }
        else if (dwID < 16777216)
        {
            continue;
        }

        if (theApp.dynprefs->Get<long>("max-sources") > this->GetSourceCount())
        {
            CUpDownClient* newsource = new CUpDownClient(nPort, dwID, dwServerIP, nServerPort, this);

            if (sourceexchangeversion > 1)
            {
                newsource->SetUserHash(achUserHash);
            }

            theApp.downloadqueue->CheckAndAddSource(this, newsource);
        }
        else
        {
            break;
        }
    }
}

// making this function return a higher when more sources have the extended
// protocol will force you to ask a larger variety of people for sources
int CPartFile::GetCommonFilePenalty()
{
    //TODO: implement, but never return less than MINCOMMONPENALTY!
    return MINCOMMONPENALTY;
}

 /* Barry - Replaces BlockReceived()
 Originally this only wrote to disk when a full 180k block
 had been received from a client, and only asked for data in
 180k blocks.
 This meant that on average 90k was lost for every connection
 to a client data source. That is a lot of wasted data.
 To reduce the lost data, packets are now written to a buffer
 and flushed to disk regularly regardless of size downloaded.
 This includes compressed packets.
 Data is also requested only where gaps are, not in 180k blocks.
 The requests will still not exceed 180k, but may be smaller to
 fill a gap.
   */

uint32_t CPartFile::WriteToBuffer(uint32_t transize, BYTE* data, uint32_t start, uint32_t end, Requested_Block_Struct* block)
{
    // Increment transfered bytes counter for this file
    transfered += transize;
    // This is needed a few times
    uint32_t lenData = end - start + 1;

    if (lenData > transize)
    {
        m_iGainDueToCompression += lenData - transize;
    }

    // Occasionally packets are duplicated, no point writing it twice
    if (IsComplete(start, end))
    {
        return 0;
    }

    // Create copy of data as new buffer
    BYTE* buffer = new BYTE[lenData];
    memcpy(buffer, data, lenData);
    // Create a new buffered queue entry
    PartFileBufferedData* item = new PartFileBufferedData;
    item->data = buffer;
    item->start = start;
    item->end = end;
    item->block = block;
    // Add to the queue in the correct position (most likely the end)
    PartFileBufferedData* queueItem;
    bool added = false;
    POSITION pos = m_BufferedData_list.GetTailPosition();

    while (pos != NULL)
    {
        queueItem = m_BufferedData_list.GetPrev(pos);

        if (item->end > queueItem->end)
        {
            added = true;
            m_BufferedData_list.InsertAfter(pos, item);
            break;
        }
    }

    if (!added)
    {
        m_BufferedData_list.AddHead(item);
    }

    // Increment buffer size marker
    m_nTotalBufferData += lenData;
    // Mark this small section of the file as filled
    FillGap(item->start, item->end);
    // Update the flushed mark on the requested block
    // The loop here is unfortunate but necessary to detect deleted blocks.
    pos = requestedblocks_list.GetHeadPosition();

    while (pos != NULL)
    {
        if (requestedblocks_list.GetNext(pos) == item->block)
        {
            item->block->transferred += lenData;
        }
    }

    if (gaplist.IsEmpty()) FlushBuffer();
    {
        // Return the length of data written to the buffer
        return lenData;
    }
}

void CPartFile::FlushBuffer(void)
{
    m_nLastBufferFlushTime = GetTickCount();

    if (m_BufferedData_list.IsEmpty())
    {
        return;
    }

 /* Madcat - Check if there is at least PARTSIZE amount of free disk space
 in temp dir before flushing. If not enough space, pause the file, 
    add log line and abort flushing.*/
    wxLongLong total, free;

    if (wxGetDiskSpace(theApp.dynprefs->Get<wxString>("temp-directory"), &total, &free) && (free < PARTSIZE))
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_OUT_OF_SPACE));
        PauseFile();
        return;
    }

    uint32_t partCount = GetPartCount();
    bool* changedPart = new bool[partCount];

    try
    {
        // Remember which parts need to be checked at the end of the flush
        for (int partNumber = 0; (uint32_t) partNumber < partCount; partNumber++)
        {
            changedPart[partNumber] = false;
        }

        // Ensure file is big enough to write data to (the last item will be the furthest from the start)
        PartFileBufferedData* item = m_BufferedData_list.GetTail();

        if (m_hpartfile.Length() <= item->end)
        {
            //m_hpartfile.SetLength(item->end + 1);
            ftruncate(m_hpartfile.fd(), item->end + 1);
        }

        // Loop through queue
        for (int i = m_BufferedData_list.GetCount(); i > 0; i--)
        {
            if (i < 1)
            {
                // Can this happen ?
                // Well, if it did... abort then.:
                return;
            }

            // Get top item
            item = m_BufferedData_list.GetHead();
            // This is needed a few times
            uint32_t lenData = item->end - item->start + 1;
            int curpart = item->start/PARTSIZE;
            changedPart[curpart] = true;
            // Go to the correct position in file and write block of data
            m_hpartfile.Seek(item->start);
            m_hpartfile.Write(item->data, lenData);
            // Remove item from queue
            m_BufferedData_list.RemoveHead();
            // Decrease buffer size
            m_nTotalBufferData -= lenData;
            // Release memory used by this item
            delete [] item->data;
            delete item;
        }

        // Flush to disk
        m_hpartfile.Flush();
        // Check each part of the file
        uint32_t partRange = (m_nFileSize % PARTSIZE) - 1;

        for (int partNumber = partCount - 1; partNumber >= 0; partNumber--)
        {
            if (changedPart[partNumber] == false)
            {
                // Any parts other than last must be full size
                partRange = PARTSIZE - 1;
                continue;
            }

            // Is this 9MB part complete
            if (IsComplete(PARTSIZE* partNumber, (PARTSIZE* (partNumber + 1)) - 1))
            {
                // Is part corrupt
                if (!HashSinglePart(partNumber))
                {
                    theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_PARTCORRUPT), partNumber, GetFileName());
                    AddGap(PARTSIZE* partNumber, (PARTSIZE* partNumber + partRange));
                    corrupted_list.AddTail(partNumber);
                    // Reduce transfered amount by corrupt amount
                    this->m_iLostDueToCorruption += (partRange + 1);
                }
                else
                {
                    // Successfully completed part, make it available for sharing
                    if (status == PS_EMPTY)
                    {
                        status = PS_READY;
                        theApp.sharedfiles->SafeAddKFile(this);
                    }
                }
            }
            else if (theApp.dynprefs->Get<bool>("enable-ich") && IsCorruptedPart(partNumber))
            {
                // Try to recover with minimal loss
                if (HashSinglePart(partNumber))
                {
                    m_iTotalPacketsSavedDueToICH++;
                    FillGap(PARTSIZE* partNumber, (PARTSIZE* partNumber + partRange));
                    RemoveBlockFromList(PARTSIZE* partNumber, (PARTSIZE* partNumber + partRange));
                    theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ICHWORKED), partNumber, GetFileName());
                }
            }

            // Any parts other than last must be full size
            partRange = PARTSIZE - 1;
        }
        // Update met file
        SavePartFile();

        // Is this file finished?
        if (gaplist.IsEmpty())
        {
            CompleteFile(false);
        }
    }
    catch(...)
    {
        theApp.xmuledlg->AddLogLine(true, GetResString(IDS_ERR_WRITEERROR), GetFileName(), GetResString(IDS_UNKNOWN).GetData());
        status = PS_ERROR;
        SetPartFileStatus(this);
        paused = true;
        datarate = 0;
        transferingsrc = 0;
        //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);
        UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay);
    }

    delete[] changedPart;
}

void CPartFile::UpdateFileRatingCommentAvail()
{
    if (!this)
    {
        return;
    }

    bool prev = (hasComment || hasRating);
    hasComment = false;
    hasRating = false;
    POSITION pos1, pos2;

    for (pos1 = srclist.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        srclist.GetNext(pos1);
        CUpDownClient* cur_src = srclist.GetAt(pos2);

        if (cur_src->GetFileComment().length() > 0)
        {
            hasComment = true;
        }

        if (cur_src->GetFileRate() > 0)
        {
            hasRating = true;
        }

        if (hasComment && hasRating)
        {
            break;
        }
    }

    //theApp.xmuledlg->transferwnd.downloadlistctrl.UpdateItem(this);:
    if (prev != (hasComment || hasRating))
    {
        UpdateDisplayedInfo(this, m_lastRefreshedDLDisplay);
    }
}

uint16_t CPartFile::GetSourceCount()
{
    return srclist.GetCount();
}

bool CPartFile::HasBadRating()
{
    if (!hasRating)
    {
        return false;
    }

    POSITION pos1, pos2;

    for (pos1 = srclist.GetHeadPosition(); (pos2 = pos1) != NULL;)
    {
        srclist.GetNext(pos1);
        CUpDownClient* cur_src = srclist.GetAt(pos2);

        if (cur_src->GetFileRate() == 1)
        {
            return true;
        }
    }

    return false;
}

time_t CPartFile::GetLastChangeDatetime(bool forcecheck)
{
    if ((::GetTickCount() - m_lastdatetimecheck) < 60000 && !forcecheck)
    {
        return m_lastdatecheckvalue;
    }

    m_lastdatetimecheck = ::GetTickCount();

    if (!::wxFileExists(m_hpartfile.GetFilePath()))
    {
        m_lastdatecheckvalue = -1;
    }
    else
    {
        //CFileStatus filestatus;
        struct stat filestatus;
        fstat(m_hpartfile.fd(), &filestatus);
        //m_hpartfile.GetStatus(filestatus); // this; "...returns m_attribute without high-order flags" indicates a known MFC bug, wonder how many unknown there are... :)
        m_lastdatecheckvalue = filestatus.st_mtime;
    }

    return m_lastdatecheckvalue;
}

uint8_t CPartFile::GetCategory()
{
    if (m_category > theApp.glob_prefs->GetCatCount() - 1)
    {
        m_category = 0;
    }

    return m_category;
}

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

void CPartFile::RemoveNoNeededSources()
{
    POSITION position, temp_position;
    position = srclist.GetHeadPosition();

    while (position != NULL)
    {
        temp_position = position;
        srclist.GetNext(position);
        CUpDownClient* client = srclist.GetAt(temp_position);

        if (client->GetDownloadState() == DS_NONEEDEDPARTS)
        {
/* If allowed, try to swap to other file. If swapping fails, remove from this one.*/
            if (theApp.glob_prefs->SwapNoNeededSources())
            {
                if (!client->SwapToAnotherFile())
                {
                    theApp.downloadqueue->RemoveSourceFromPartFile(this, client, temp_position);
                }
/* If not allowed to swap, simply remove from this one.*/
            }
            else
            {
                theApp.downloadqueue->RemoveSourceFromPartFile(this, client, temp_position);
            }
        }
    }
}

    /* End modif*/

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

void CPartFile::RemoveFullQueueSources()
{
    POSITION position, temp_position;

    position = srclist.GetHeadPosition();

    while (position != NULL)
    {
        temp_position = position;
        srclist.GetNext(position);
        CUpDownClient* client = srclist.GetAt(temp_position);

        if ((client->GetDownloadState() == DS_ONQUEUE) && (client->IsRemoteQueueFull()))
        {
            theApp.downloadqueue->RemoveSourceFromPartFile(this, client, temp_position);
        }
    }
}

    /* End modif*/

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

void CPartFile::RemoveHighQueueRatingSources()
{
    POSITION position, temp_position;
    position = srclist.GetHeadPosition();

    while (position != NULL)
    {
        temp_position = position;
        srclist.GetNext(position);
        CUpDownClient* client = srclist.GetAt(temp_position);

        if ((client->GetDownloadState() == DS_ONQUEUE) && (client->GetRemoteQueueRank() > theApp.glob_prefs->HighQueueRanking()))
        {
            theApp.downloadqueue->RemoveSourceFromPartFile(this, client, temp_position);
        }
    }
}

    /* End modif*/

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

void CPartFile::CleanUpSources()
{
    POSITION position, temp_position;

    position = srclist.GetHeadPosition();

    while (position != NULL)
    {
        temp_position = position;
        srclist.GetNext(position);
        CUpDownClient* client = srclist.GetAt(temp_position);

        if (client->GetDownloadState() == DS_NONEEDEDPARTS)
        {
            if ((theApp.glob_prefs->DropNoNeededSources()) && (!client->SwapToAnotherFile()))
            {
                theApp.downloadqueue->RemoveSourceFromPartFile(this, client, temp_position);
            }
        }

        if ((client->GetDownloadState() == DS_ONQUEUE) && (client->IsRemoteQueueFull()))
        {
            theApp.downloadqueue->RemoveSourceFromPartFile(this, client, temp_position);
        }
        else if ((client->GetDownloadState() == DS_ONQUEUE) && (client->GetRemoteQueueRank() > theApp.glob_prefs->HighQueueRanking()))
        {
            theApp.downloadqueue->RemoveSourceFromPartFile(this, client, temp_position);
        }
    }
}

void CPartFile::AddDownloadingSource(CUpDownClient* client)
{
//    MAP* pos = listof_downloadingSources->next;

//    while (pos->Key)
//    {
//        if (client == (CUpDownClient*)pos->Data)
//        {
//            return;
//        }
//        else
//        {
//            pos = pos->next;
//        }
//    }

//    MapData_Append(listof_downloadingSources, client, NULL, 0);

    std::vector<CUpDownClient *> temp = downloading_sources[my_id];

    if (find(temp.begin(), temp.end(), client) != temp.end())
    {
        cout << "DEBUG: Stopped the storage of duplicate CUpDownClient" << client << endl;
        return;
    }

    temp.push_back(client);
    downloading_sources[my_id] = temp;
}

void CPartFile::RemoveDownloadingSource(CUpDownClient* client)
{
//    MAP* pos = listof_downloadingSources->next;

//    while (pos->Key)
//    {
//        if (client == (CUpDownClient*)pos->Data)
//        {
//            pos = MapData_Remove(listof_downloadingSources, pos);
//            return;
//        }
//        else
//        {
//            pos = pos->next;
//        }
//    }
    std::vector<CUpDownClient *> temp = downloading_sources[my_id];
    std::vector<CUpDownClient *>::iterator it = find(temp.begin(), temp.end(), client);

    if (it != temp.end())
    {
        temp.erase(it++);
        downloading_sources[my_id] = temp;
    }
}

namespace 
{
    void SetPartFileStatus(CPartFile* in_part)
    {
        if (theApp.glob_prefs->GetAllcatType() > 1)
        {
            theApp.xmuledlg->transferwnd->downloadlistctrl->Freeze();

            if (!CheckShowItemInGivenCat(in_part, theApp.xmuledlg->transferwnd->downloadlistctrl->curTab))
            {
                theApp.xmuledlg->transferwnd->downloadlistctrl->HideFile(in_part);
            }
            else
            {
                theApp.xmuledlg->transferwnd->downloadlistctrl->ShowFile(in_part);

            }

            theApp.xmuledlg->transferwnd->downloadlistctrl->Thaw();
            theApp.xmuledlg->transferwnd->downloadlistctrl->ShowFilesCount();
        }

        theApp.xmuledlg->transferwnd->downloadlistctrl->InitSort();
    }

    void UpdateDisplayedInfo(CPartFile* in_part, unsigned long& last_time, bool force)
    {
        unsigned long curTick = ::GetTickCount();

        if (force || curTick - last_time > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE + (uint32_t)(rand() / (RAND_MAX / 1000)))
        {
            theApp.xmuledlg->transferwnd->downloadlistctrl->UpdateItem(in_part);
            last_time = curTick;
        }
    }
}
