//StoredSource for eMule (www.emule-project.net) by Mr.Fry
//Version 0.07a

#include "StdAfx.h"
#include "otherfunctions.h"
#include "StoredSources.h"
#include "emule.h"

#ifdef _DEBUG
#	include "emule.h"
#	define new DEBUG_NEW
#endif

IMPLEMENT_SERIAL(CStoredSources, CObject, VERSIONABLE_SCHEMA|4)
// HACK by Md5Chap 2002-11-27 (incremented version coz of failedconns serialized)
IMPLEMENT_SERIAL(CSourceEntry, CObject, VERSIONABLE_SCHEMA|5)
IMPLEMENT_SERIAL(CServerEntry, CObject, VERSIONABLE_SCHEMA|2)
IMPLEMENT_SERIAL(CStoredSourcesContainer, CObject, VERSIONABLE_SCHEMA|1)

////////////////////////////////////////////////////////////////////////////////////
//Class: CStoredSources

//////////////////////////////Constructors:
CStoredSources::CStoredSources(void)
: m_strPartFileName(_T(""))
, m_strFileName(_T(""))
, m_strFileID(_T(""))
, m_iSourcesAtPosition(0)
, m_iVersion(CSTOREDSOURES_VERSION)
, m_bLoadTheseSources(true)
{
	this->SetPreferences(MAX_SOURCES_PER_FILE
						, RESTORE_SOURCES_AT_ONCE
						, RESTORE_SOURCES_TIME
						, RESTORE_SOURCES_SLOWLY_TIME
						, STORE_SOURCES_TIME);
}

CStoredSources::CStoredSources(CString partFileName, CString fileName
							   , uchar* fileID, uint16 maxSourceEntries)
{
	this->Init(partFileName, fileName, fileID, maxSourceEntries);
	//call this->Init() with default values for:
	//		uint16 userPort			= 0
	//		uint32 userID			= 0
	//		uint32 serverIP			= 0
	//		uint16 serverPort		= 0
	//		CString userName		= NONAME
}

CStoredSources::CStoredSources(CString partFileName, CString fileName,
		uchar* fileID, CString userName, uint16 userPort, uint32 userID,
		uint32 serverIP, uint16 serverPort, uint16 maxSourceEntries)
{
	//call Init(...)
	this->Init(partFileName, fileName, fileID, maxSourceEntries, userPort, userID
				, serverIP, serverPort, userName);
}

CStoredSources::CStoredSources(CString partFileName, CString fileName
								, uchar* fileID, uint16 userPort, uint32 userID
								, uint32 serverIP, uint16 serverPort, uint16 maxSourceEntries)
{	
	//call Init(...) as above, but with the default value (NONAME) vor (CString) userName
	this->Init(partFileName, fileName, fileID, maxSourceEntries, userPort, userID
				, serverIP, serverPort);
}

void CStoredSources::Init(CString partFileName, CString fileName, uchar* fileID
								, uint16 maxSourceEntries			//from here on
								, uint16 userPort, uint32 userID	//all parameters have	
								, uint32 serverIP, uint16 serverPort //default values
								, CString userName)
{
	this->m_strPartFileName	= partFileName;
	this->m_strFileName		= fileName;
	this->m_iSourcesAtPosition = 0;



	if(maxSourceEntries <= USHRT_MAX)
		this->m_iMaxSourceEntries = maxSourceEntries;
	else this->m_iMaxSourceEntries = USHRT_MAX;

	this->SetPreferences(this->m_iMaxSourceEntries
						, RESTORE_SOURCES_AT_ONCE
						, RESTORE_SOURCES_TIME
						, RESTORE_SOURCES_SLOWLY_TIME
						, STORE_SOURCES_TIME);

	//convert (uchar*) fileID to a CString
	this->m_strFileID.Append(EncodeBase16(fileID, 16));

	this->m_iVersion = CSTOREDSOURES_VERSION;

	this->m_bLoadTheseSources = true;
	
	//////////////////////////////////////////////////////////////////////////////
	//check, if a CSourceEntry in m_obarraySourceList should be made
	if(userPort == 0 && userID == 0 && serverIP == 0 && serverPort == 0)
		return;	//if all these values are equal 0, it is suggested, no CSourceEntry should be created
	
	//make the CSourceEntry
	this->m_obarraySourceList.Add(new CSourceEntry(userName, userPort, userID, serverIP,
											serverPort));
}

void CStoredSources::SetPreferences(uint16 maxSourceEntries
									, uint16 loadSourcesAtOnceLimit
									, uint32 loadSourcesTimeInterval
									, uint32 loadSourcesPartialTimeInterval
									, uint32 saveSourcesTimeInterval)
{
	this->m_iMaxSourceEntries					= maxSourceEntries;
	this->m_iLoadSourcesAtOnceLimit				= loadSourcesAtOnceLimit;
	this->m_iLoadingSourcesTimeInterval			= loadSourcesTimeInterval;
	this->m_iLoadingSourcesPartialTimeInterval	= loadSourcesPartialTimeInterval;
	this->m_iSavingSourcesTimeInterval			= saveSourcesTimeInterval;
}

CStoredSources::~CStoredSources(void)
{
	int length = this->m_obarraySourceList.GetCount();
	for(int i=0; i < length ; i++)// HACK by Md5Chap 2002-11-24 delete if ref==0
		((CSourceEntry*)this->m_obarraySourceList[i])->Release(true); //We'll delete if noref
		//delete this->m_obarraySourceList[i];
	this->m_obarraySourceList.RemoveAll();

}

/////////////////////////Update-Functions:
bool CStoredSources::UpdateSource(CStoredSources* newSources
									, bool overWritePartFileName	//from here on the parameters
									, bool overWriteFileName		//have default values
									, bool checkForEquality)
{
	if(checkForEquality && this->GetFileID().Compare(newSources->GetFileID()) != 0)
		return false;

	if(overWritePartFileName) this->m_strPartFileName = newSources->m_strPartFileName;
	if(overWriteFileName) this->m_strFileName = newSources->m_strFileName;

	this->m_bLoadTheseSources		= newSources->m_bLoadTheseSources;

	if(newSources->GetMaxSourceEntries() <= USHRT_MAX)
		this->m_iMaxSourceEntries = newSources->GetMaxSourceEntries();
	else this->m_iMaxSourceEntries = USHRT_MAX;
	
	//update or make deep copies for all of the CSourceEntries of newSources
	int new_SourceCount = newSources->m_obarraySourceList.GetCount();
	int this_SourceCount = this->m_obarraySourceList.GetCount();
	bool wasInList;
	CSourceEntry* newEntry;
	for(int i=0 ; i < new_SourceCount ; i++){
		wasInList = false;
		newEntry = (CSourceEntry*)newSources->m_obarraySourceList[i];
		//HACK by Md5Chap 2002-11-27 (Handling smooth outdating, honoring rarefiles)
		if(! newEntry->IsOutdated(newSources->m_obarraySourceList.GetCount())){//is newEntry outdated? if not then:
			for(int j=0 ; j < this_SourceCount ; j++){
				wasInList = ((CSourceEntry*) this->m_obarraySourceList[j])->AddSourceInformation(newEntry);
			}
			if(!wasInList) this->m_obarraySourceList.Add(new CSourceEntry(newEntry));
		}
	}

	return true;
}

void CStoredSources::UpdateSource(uint16 userPort, uint32 userID, uint32 serverIP
									, uint16 serverPort, uint16 partsCount)
{
	//call StandardUpdate(...) as below, but with default value (NONAME) for userName
	this->StandardUpdate(userPort, userID, serverIP, serverPort, partsCount);
}

void CStoredSources::UpdateSource(CString userName, uint16 userPort, uint32 userID,
									uint32 serverIP, uint16 serverPort, uint16 partsCount)
{	
	//call StandardUpdate(...)
	this->StandardUpdate(userPort, userID, serverIP, serverPort, partsCount, userName);
}

void CStoredSources::StandardUpdate(uint16 userPort, uint32 userID
									, uint32 serverIP, uint16 serverPort
									, uint16 partsCount, CString userName)
{
	int index;

	// Added by Tarod (no Low ID sources)
	if (userID < 16777216) return;
	// End by Tarod

	if((index=this->FindSource(NONAME, userPort)) > -1){
		//if(((CSourceEntry*)this->m_obarraySourceList[index])->UserIDExists(userID))
			((CSourceEntry*)this->m_obarraySourceList[index])->RemoveUserID(userID);
	}
	if((index=this->FindSource(userName, userPort)) > -1){
		((CSourceEntry*)this->m_obarraySourceList[index])->AddSourceInformation(userName,
															userID, serverIP, serverPort, partsCount);
	}
	else this->m_obarraySourceList.Add(new CSourceEntry(userName, userPort, userID, 
													serverIP, serverPort, partsCount));
}

bool CStoredSources::SetMaxSourceEntries(uint64 maxSourceEntries)
{
	if(maxSourceEntries <= USHRT_MAX){
		this->m_iMaxSourceEntries = maxSourceEntries;
		return true;
	}
	else this->m_iMaxSourceEntries = USHRT_MAX;
	return false;
}

////////////////////////////////Get-Functions:

CSourceEntry*	CStoredSources::GetFirstSourceEntry()
{
	this->m_iSourcesAtPosition=0;
	return ((CSourceEntry*)this->m_obarraySourceList[this->m_iSourcesAtPosition]);
}

CSourceEntry*	CStoredSources::GetNextSourceEntry()
{
	//HACK by Md5Chap 2002-11-27 (As we never use Getfirst, we need to fix this a bit! and move to last)
	//this->m_iSourcesAtPosition++;
	if(this->m_iSourcesAtPosition < this->m_obarraySourceList.GetCount()){

		//if(this->m_obarraySourceList[this->m_iSourcesAtPosition] == NULL){
		//	this->m_iSourcesAtPosition = 0;
		//	return NULL;
		//}
		//else
		//	
		//	//were all sources of this StoredSources delivered once?
		//if(this->m_iSourcesAtPosition +1 == this->m_obarraySourceList.GetCount())
		//	this->m_bLoadTheseSources = true;
 
		// HACK by Md5Chap 2002-11-27 (we never use Getfirst, so make this include [0] using postincrement)
		return ((CSourceEntry*) this->m_obarraySourceList[this->m_iSourcesAtPosition++]);
	}
	else{
		//this->m_iSourcesAtPosition = 0;
		return NULL;
	}
	
}

//see *.h for informations about CStoredSources::GetFileHash()
uchar* CStoredSources::GetFileHash()
{
	uchar* fileHash = new uchar[16] ;
	int j=0;
	//char* temp = new char[3];
	char conv[3];
	char* temp=conv;
	int converted;
	for(int i=0; i < 16; i++){
		temp[0]=this->GetFileID().GetAt(j);
		temp[1]=this->GetFileID().GetAt(j+1);
		converted = (uchar) strtol(temp,0,16);
		fileHash[i]=converted;
		j +=2;
	}
	//delete[] temp;

	return fileHash;
}

bool CStoredSources::SetSourceIndex(uint16 index)
{
	if((index >=0) && (index < this->GetCountSourceEntrys())){
		this->m_iSourcesAtPosition = index;
		return true;
	}
	return false;
}

uint32 CStoredSources::GetCountOfSources()
{
	uint32 iCount=0;
	uint16 sourceCount = (uint16) this->m_obarraySourceList.GetCount();
	for(int i=0; i < sourceCount; i++){
		if(((CSourceEntry*)this->m_obarraySourceList[i])->GetFirstUserID() > 0){
			iCount++;
			while( ((CSourceEntry*)this->m_obarraySourceList[i])->GetNextUserID() > 0)
				iCount++;
		}
	}
	return iCount;
}

///////////////////////////////////////////////////////////

bool CStoredSources::IsEqual(CStoredSources* toCompare)//does not work correct yet
{
	bool result=false;
	if((this->GetFileName().Compare(toCompare->GetFileName()) == 0)
			&& (this->GetPartFileName().Compare(toCompare->GetPartFileName()) == 0)
			&& (this->GetFileID().Compare(toCompare->GetFileID()) == 0)){
		TRACE("CStoredSources::IsEqual(): *toCompare is possibly equal\n");
		result = true;
	}
	
	int this_listSize	=this->m_obarraySourceList.GetCount();
	int toComp_listSize	=toCompare->m_obarraySourceList.GetCount();
	for(int i = 0 ; i < this_listSize; i++){
		for(int j=0 ; j < toComp_listSize ; j++){
			if(! ((CSourceEntry*)this->m_obarraySourceList[i])->IsEqual(((CSourceEntry*)toCompare->m_obarraySourceList[j]))){
				TRACE("CStoredSources::IsEqual()->m_obarraySourceList: *toCompare is not equal\n");
				result = false;
			}
		}
	}

	return false;
}

int CStoredSources::FindSource(CString userName, uint16 userPort)
{
	int length=this->m_obarraySourceList.GetCount();
	CSourceEntry* toFind;
	for(int i = 0 ; i < length ; i++){
		// Modified by Tarod [i_a] optimization
		// CPU load reduction: compare Port before string compare to reduce number of string compares
		toFind = (CSourceEntry*)this->m_obarraySourceList[i]; 
		if((toFind->GetUserPort() == userPort) 
			&& !toFind->GetUserName().Compare(userName))	//compare userName&userPort at (int) i, if 
															//they are the same as the function-
															//parameters, return i
		{
			return i;
		}
	}
	return -1;
}

// Added by Tarod (using Quick Sort algorithm) CPU use optimization

// Sort the "CSourceEntry"s by m_iPartsCount 
bool CStoredSources::SortSourcesByPartsCount(void) 
{ 
	if (m_obarraySourceList.GetCount() > 1) {
		//QuickSort(0, (m_obarraySourceList.GetCount() - 1), false) ;
		HeapSort(false);
	}

	return true; 
} 

// Sort the "CSourceEntry"s by m_iPartsCount and if two Entries 
// have equal m_iPartsCount, sort them by m_dtLastSeen 
bool CStoredSources::SortSourcesByPartsNDate(void) 
{
	if (m_obarraySourceList.GetCount() > 1) {
		//QuickSort(0, (m_obarraySourceList.GetCount() - 1), true) ;
		HeapSort(true);
	}

	return true; 
} 

// Compare to entries by number of parts
//
// [In] entry1: Source entry pointer to compare
//      entry2: Source entry pointer to compare
//      compare_date: Compare last seen dates too
//
// [Return]  1: entry2 > entry1
//           0: entry2 == entry1
//          -1: entry2 < entry1
int CStoredSources::CompareParts(CSourceEntry* entry1, CSourceEntry* entry2, bool compare_date)
{
	// Compare number of parts
	int result = entry2->GetPartsCount() - entry1->GetPartsCount() ;

	// Comapare last seen dates in case number of parts are the same number
	if (compare_date && result == 0) {
		if (entry1->GetLastSeenTime() == entry2->GetLastSeenTime()) {
			result = 0;
		} else if (entry1->GetLastSeenTime() < entry2->GetLastSeenTime()) {
			result = 1; 
		} else {
			result = -1;
		}
	}

	return result ;
}

// Sort array (or subarray) CStoredSources::m_obarraySourceList
// using Insertion Sort algorithm
//
// [In] lb: Low bound index of array to sort
//      ub: Upper bound index of array to sort
//      compare_date: Compare source entry's last seen dates too
void CStoredSources::InsertSort(int lb, int ub, bool compare_date)
{
	CSourceEntry* t;
	int i, j;

	// Sort array m_obarraySourceList[lb..ub]
	for (i = (lb + 1); i <= ub; i++) {
		t = (CSourceEntry*)m_obarraySourceList[i];

		// Shift elements down until insertion point found
		for (j = (i - 1); j >= lb && CompareParts((CSourceEntry*)m_obarraySourceList[j], t, compare_date) > 0; j--)
			m_obarraySourceList[j + 1] = m_obarraySourceList[j];

		// Insert
		m_obarraySourceList[j + 1] = t;
	}
}

// Makes a partition to use Quick Sort algorithm on it
//
// [In] lb: Low bound index of array to sort
//      ub: Upper bound index of array to sort
//      compare_date: Compare source entry's last seen dates too
//
// [Return] Index for partition start in array (or subarray)
int CStoredSources::Partition(int lb, int ub, bool compare_date)
{
	CSourceEntry* t;
	CSourceEntry* pivot;
	int i, j, p;

	// Partition array m_obarraySourceList[lb..ub]

	// Select pivot and exchange with 1st element
	p = lb + ((ub - lb) >> 1);
	pivot = (CSourceEntry*)m_obarraySourceList[p];
	m_obarraySourceList[p] = m_obarraySourceList[lb];

	// Sort lb+1..ub based on pivot
	i = lb + 1;
	j = ub;
	for(;;) {
		while (i < j && CompareParts(pivot, (CSourceEntry*)m_obarraySourceList[i], compare_date) > 0) i++;
		while (j >= i && CompareParts((CSourceEntry*)m_obarraySourceList[j], pivot, compare_date) > 0) j--;
		if (i >= j) break;
		t = (CSourceEntry*)m_obarraySourceList[i];
		m_obarraySourceList[i] = m_obarraySourceList[j];
		m_obarraySourceList[j] = t;
		j--; 
		i++;
	}

	// pivot belongs in m_obarraySourceList[j]
	m_obarraySourceList[lb] = m_obarraySourceList[j];
	m_obarraySourceList[j] = pivot;

	return j;
}

// Sort array (or subarray) CStoredSources::m_obarraySourceList
// using "fast" Quick Sort algorithm
//
// [In] lb: Low bound index of array to sort
//      ub: Upper bound index of array to sort
//      compare_date: Compare source entry's last seen dates too
void CStoredSources::QuickSort(int lb, int ub, bool compare_date)
{
    int m;

	// Sort array m_obarraySourceList[lb..ub]
    while (lb < ub) {

        // Quickly sort short lists
        if ((ub - lb) <= 12) { // Cut off for Insertion Sort
            InsertSort(lb, ub, compare_date);
            return;
        }

        // Partition into two segments
        m = Partition(lb, ub, compare_date);

        // Sort the smallest partition to minimize stack requirements
        if ((m - lb) <= (ub - m)) {
            QuickSort(lb, (m - 1), compare_date);
            lb = m + 1;
        } else {
            QuickSort((m + 1), ub, compare_date);
            ub = m - 1;
        }
    }
}

// Sort array CStoredSources::m_obarraySourceList
// using in place Heap Sort algorithm
//
// [In]	compare_date: Compare source entry's last seen dates too
void CStoredSources::HeapSort(bool compare_date)
{
	int array_length = m_obarraySourceList.GetCount();

	for (int k = array_length / 2; k > 0; k--) {
		SiftDown(k, array_length, compare_date);
	}

	do {
		CSourceEntry* temp = (CSourceEntry*)m_obarraySourceList[0];

		m_obarraySourceList[0] = m_obarraySourceList[array_length - 1];
		m_obarraySourceList[array_length - 1] = temp;
		array_length = array_length - 1;

		SiftDown(1, array_length, compare_date);
	} while (array_length > 1);
}

// SiftsDown new sorted elements using free slot in bottom of the tree
//
// [In]	root: Root of the tree
//		bottom: Bottom node of the tree
//		compare_date: Compare source entry's last seen dates too
void CStoredSources::SiftDown(int root, int bottom, bool compare_date)
{
	CSourceEntry* temp = (CSourceEntry*)m_obarraySourceList[root - 1];

	while (root <= bottom / 2) {
		int j = root + root;

		//if ((j < bottom) && (m_obarraySourceList[j - 1] < m_obarraySourceList[j])) {
		if ((j < bottom) && (CompareParts((CSourceEntry*)m_obarraySourceList[j - 1], (CSourceEntry*)m_obarraySourceList[j], compare_date) < 0)) {
			j++;
		}
		//if (temp >= m_obarraySourceList[j - 1]) {
		if (CompareParts(temp, (CSourceEntry*)m_obarraySourceList[j - 1], compare_date) >= 0) {
			break;
		} else {
			m_obarraySourceList[root - 1] = m_obarraySourceList[j - 1];
			root = j;
		}
	}
	m_obarraySourceList[root - 1] = temp;
}


uint16 CStoredSources::DeleteOutdatedSources(void)
{
	int removed = 0;
	//int timeSpan;
	CSourceEntry* current_source;
	int i = 0 ; 
	while (i < this->m_obarraySourceList.GetCount()) {
		current_source = (CSourceEntry*)this->m_obarraySourceList.GetAt(i);
		// HACK by Md5Chap 2002-11-27 (using smooth outdating now, and honor rarefiles!)
		if(current_source->IsOutdated(this->m_obarraySourceList.GetCount())){
			if (current_source->GetRef()>1) {i++;continue;};// HACK by Md5Chap 2002-11-24 (delete only if unreferenced!)
			delete current_source;//this->m_obarraySourceList.GetAt(i);
			this->m_obarraySourceList.RemoveAt(i);
			removed++;
		}
		else {
			if(!current_source->DeleteOutdatedIDs()){//if this condition is true it means, that DeleteOutdatedIDs() had deleted ALL IDs of this user/sources, so it isn't needed anymore
				if (current_source->GetRef()>1) {i++;continue;};// HACK by Md5Chap 2002-11-24 (delete only if unreferenced!)
				delete current_source;
				this->m_obarraySourceList.RemoveAt(i);
				removed++;
			}
			else{//reduce ServerEntries to MAX_SERVERS_PER_SOURCE
				current_source->ReduceServersTo(MAX_SERVERS_PER_SOURCE);
				i++;
			}
		}
	}
	TRACE("CStoredSources::DeleteOutdatedSources: removed %u outdated CSourceEntries from m_obarraySourceList\n",removed);
	return removed;
}

uint16 CStoredSources::ReduceSourcesTo(sint32 sourceCount)
{
	//uint16 removed = 0;
	//int lastEntry;
	//while((lastEntry = this->m_obarraySourceList.GetCount()-1) >= sourceCount){
	//	delete this->m_obarraySourceList.GetAt(lastEntry);
	//	this->m_obarraySourceList.RemoveAt(lastEntry);
	//	removed++;
	//}
	//TRACE("CStoredSources::ReduceSourcesTo: removed %u CSourceEntries from m_obarraySourceList\n",removed);
	//return removed;
	// TODO by Md5Chap 2002-11-24 -- you may delete this commented code

	uint16 removed = 0;
	int lastEntry;
	int fakeDel=1;
	while((lastEntry = this->m_obarraySourceList.GetCount()-fakeDel) >= sourceCount){
		// HACK by Md5Chap 2002-11-24 (Never delete a referenced object!)
		if (((CSourceEntry*)this->m_obarraySourceList.GetAt(lastEntry))->GetRef()>1){
			fakeDel++;
			continue;
		};
		((CSourceEntry*)this->m_obarraySourceList.GetAt(lastEntry))->Release(true);
		this->m_obarraySourceList.RemoveAt(lastEntry);
		removed++;
	}
	TRACE("CStoredSources::ReduceSourcesTo: removed %u CSourceEntries from m_obarraySourceList\n",removed);
	return removed;
}

///////////////////////Serialize:
void CStoredSources::Serialize(CArchive& ar)
{
//#ifdef _DEBUG
//		COleDateTime startTime = COleDateTime::GetCurrentTime();
//		COleDateTime endTime = COleDateTime::GetCurrentTime();
//#endif
	if(ar.IsStoring()){
		this->m_iVersion = CSTOREDSOURES_VERSION;
//#ifdef _DEBUG
//		startTime = COleDateTime::GetCurrentTime();
//#endif
		this->SortSourcesByPartsNDate();
//#ifdef _DEBUG
//		endTime = COleDateTime::GetCurrentTime();
//		TRACE("sort sources by parts took %s min\n",(endTime-startTime).Format("%M:%S"));
//		startTime = COleDateTime::GetCurrentTime();
//#endif
		//HACK by 2002-11-27 (Superseeded by IsOutdatedSmooth)
		/*if(!this->IsRareFile()){*/
		{
			this->DeleteOutdatedSources();
//#ifdef _DEBUG
//		endTime = COleDateTime::GetCurrentTime();
//		TRACE("delete outdated sources took %s min\n",(endTime-startTime).Format("%M:%S"));
//		startTime = COleDateTime::GetCurrentTime();
//#endif
			this->ReduceSourcesTo(this->m_iMaxSourceEntries);
//#ifdef _DEBUG
//		endTime = COleDateTime::GetCurrentTime();
//		TRACE("reduce sources to %u took %s min\n",this->m_iMaxSourceEntries,(endTime-startTime).Format("%M:%S"));
//#endif
		}
	}
//#ifdef _DEBUG
//	startTime = COleDateTime::GetCurrentTime();
//#endif
	CObject::Serialize(ar);
//#ifdef _DEBUG
//		endTime = COleDateTime::GetCurrentTime();
//		TRACE("%sraw CObject took %s min\n"
//			,(ar.IsStoring()?"saving ":"loading "),(endTime-startTime).Format("%M:%S"));
//		uint32 startT= ::GetTickCount();
//		startTime = COleDateTime::GetCurrentTime();
//#endif
	m_obarraySourceList.Serialize(ar);
//#ifdef _DEBUG
//		uint32 endT= ::GetTickCount();
//		endTime = COleDateTime::GetCurrentTime();
//		TRACE("%s%i SourceEntries took %s min (%.3f sec)\n"
//			,(ar.IsStoring()?"saving ":"loading ")
//			,this->m_obarraySourceList.GetCount()
//			,(endTime-startTime).Format("%M:%S")
//			,(float(endT-startT)/1000));
//#endif
	
	if(ar.IsStoring()){
		ar << m_iVersion << m_strPartFileName << m_strFileName << m_strFileID
			<<  m_iMaxSourceEntries << m_iSourcesAtPosition << m_bLoadTheseSources
			<< m_iLoadingSourcesTimeInterval << m_iLoadingSourcesPartialTimeInterval
			<< 	m_iSavingSourcesTimeInterval << m_iLoadSourcesAtOnceLimit;
	}
	else {
		//for different versions of CStoredSources
		//int iVersion = ar.GetObjectSchema();
		try{
			//for later use, make a 'copy' of the CArchive ar
			CFile* file = ar.GetFile()->Duplicate();
			
			int iVersion = ar.GetObjectSchema();
			if(iVersion != -1)
				switch(iVersion){
					case 0:
					case 1:
						break;
					case 2:
					case 3:
					case 4:
						ar >> m_iVersion;
						break;
					default:
						break;
				}
			else {
				ar >> m_iVersion;
				iVersion = this->m_iVersion;
			}

			switch(iVersion){
				case -1: 
					TRACE("CStoredSources (Deserialization): Unknown Version, trying to read anyway ...\n");
				case 0:
				case 1:
					ar >> m_strPartFileName >> m_strFileName >> m_strFileID;
					break;
				case 2:
					ar >> m_strPartFileName >> m_strFileName >> m_strFileID >> m_iMaxSourceEntries;
					break;
				case 3:
					ar >> m_strPartFileName >> m_strFileName >> m_strFileID 
						>> m_iMaxSourceEntries >> m_iSourcesAtPosition >> m_bLoadTheseSources;
					break;
				case 4:
					ar >> m_strPartFileName >> m_strFileName >> m_strFileID 
						>> m_iMaxSourceEntries >> m_iSourcesAtPosition 
						>> m_bLoadTheseSources >> m_iLoadingSourcesTimeInterval
						>> m_iLoadingSourcesPartialTimeInterval	>> 	m_iSavingSourcesTimeInterval
						>> m_iLoadSourcesAtOnceLimit;
					break;
				default:
					//this is for the old version (that is Version No. 1) of the 
					//Serialize-function.
					//It will slow down the process, but since old source-files
					//are only read on time and then saved in the new format
					//this part of code will not be used very often
					TRACE("The CStoredSources on disk has a %s Version-Number (%i)\nTry to read as Version 1 ...\n"
						,(iVersion == -1?"unknown":"wrong"),iVersion);
					
					//delete was readed yet
					int length = this->m_obarraySourceList.GetCount();
					for(int i=0; i < length ; i++)
                        ((CSourceEntry*)this->m_obarraySourceList[i])->Release(true);// HACK by Md5Chap 2002-11-24 (we'll leave some obj alone, but that doesn't matter due to release()!)
						//delete this->m_obarraySourceList[i];	
					this->m_obarraySourceList.RemoveAll();
					
					//start reading agian ...
					//(it's necessary to read all again, because
					// theres no way to determine what position
					// the CArchive-Object has right now in the file
					// , or set the position in the file for the
					// CArchive-Object)
					file->SeekToBegin();
					CArchive archive(file, CArchive::load);
					
					//first the embedded objects ...
					CObject::Serialize(archive);
					m_obarraySourceList.Serialize(archive);
					
					//then the part we really wanted
					archive >> m_strPartFileName >> m_strFileName >> m_strFileID;
					this->m_iVersion = 1;
					
			}//end switch
			delete file;
		}
		catch(CArchiveException* err){
			#ifdef _DEBUG
				char errorMsg[254];
				err->GetErrorMessage(errorMsg,254);
				TRACE("CStoredSources::Serialize: failed to read sources\n\tErrorMessage: %s\n",errorMsg);
			#endif
			err->Delete();
		}
	}
}

#ifdef _DEBUG
	void CStoredSources::Dump( CDumpContext& dc ) const
	{
		// call base class function first
		CObject::Dump( dc );
		m_obarraySourceList.Dump(dc);

		// now do the stuff for our specific class
		dc << "Part-File Name: " << m_strPartFileName << "\n"
			<< "(Real) File Name: " << m_strFileName << "\n"
			<< "File-ID: " << m_strFileID << "\n"
			<< "Source-Position: " << m_iSourcesAtPosition << "\n";
	}
#endif


////////////////////////////////////////////////////////////////////////////////////
//Class: CSourceEntry

/////////////////////Constructors:
CSourceEntry::CSourceEntry(void)
: m_strUserName(_T(""))
, m_iTransfered(0)
, m_iServerAtPositon(0)
, m_iPartsCount(0)
, m_bIsReliable(false)
{
	this->m_posUserIDAtPositon = this->m_listUserIDs.GetHeadPosition();
	this->m_dtLastSeen = COleDateTime::GetCurrentTime();
	this->m_nFailedConnections = 0 ; // Added by Tarod (LS tweak)
	this->m_referenceCount = 1; // HACK by Md5Chap 2002-11-24 (ref in baseclient)
	//this->m_obarrayServerList.SetSize(16,32);
}

CSourceEntry::CSourceEntry(CSourceEntry* toCopy)	//copy constructor
{
	this->m_strUserName = toCopy->GetUserName();
	this->m_iTransfered = toCopy->GetTransfered();
	this->m_iUserPort	= toCopy->GetUserPort();
	this->m_dtLastSeen	= toCopy->m_dtLastSeen;
	this->m_nFailedConnections = toCopy->m_nFailedConnections; // Added by Tarod (LS tweak)
	this->m_referenceCount = 1; // HACK by Md5Chap 2002-11-24 (ref in baseclient)
	this->m_iPartsCount	= toCopy->m_iPartsCount;
	this->m_bIsReliable	= toCopy->m_bIsReliable;
	//this->m_obarrayServerList.SetSize((toCopy->m_obarrayServerList.GetCount() + 8),32);
	
	for(POSITION pos = toCopy->m_listUserIDs.GetHeadPosition(); pos != NULL;)
		this->m_listUserIDs.AddTail(toCopy->m_listUserIDs.GetNext(pos));
	
	int serverCount = toCopy->m_obarrayServerList.GetCount();
	for(int i=0; i < serverCount; i++)
		this->m_obarrayServerList.Add(new CServerEntry(
							((CServerEntry*)toCopy->m_obarrayServerList[i])->GetServerIP()
							, ((CServerEntry*)toCopy->m_obarrayServerList[i])->GetServerPort()
							, ((CServerEntry*)toCopy->m_obarrayServerList[i])->GetCounter()));

	this->m_iServerAtPositon = 0;
	this->m_posUserIDAtPositon = this->m_listUserIDs.GetHeadPosition();
}

CSourceEntry::CSourceEntry(uint16 userPort, uint32 userID, uint32 serverIP,
							   uint16 serverPort, uint16 partsCount, bool isReliable)
{
	//use default values for userName (NONAME) and transfered (0)
	this->Init(userPort, userID, serverIP, serverPort, partsCount);
	this->SetReliable(isReliable);
	this->m_referenceCount = 1; // HACK by Md5Chap 2002-11-24 (ref in baseclient)
}

CSourceEntry::CSourceEntry(uint16 userPort, uint32 userID, uint32 transfered,
							   uint32 serverIP, uint16 serverPort
							   , uint16 partsCount, bool isReliable)
{
	
	//use default value for userName (NONAME)
	this->Init(userPort, userID, serverIP, serverPort, partsCount, NONAME, transfered);
	this->SetReliable(isReliable);
	this->m_referenceCount = 1; // HACK by Md5Chap 2002-11-24 (ref in baseclient)
}

CSourceEntry::CSourceEntry(CString userName, uint16 userPort, uint32 userID,
							   uint32 serverIP, uint16 serverPort
							   , uint16 partsCount, bool isReliable)
{
	//use default value (0) for uint32 transfered
	this->Init(userPort, userID, serverIP, serverPort, partsCount, userName);
	this->SetReliable(isReliable);
	this->m_referenceCount = 1; // HACK by Md5Chap 2002-11-24 (ref in baseclient)
}

CSourceEntry::CSourceEntry(CString userName, uint16 userPort, uint32 userID,uint32 transfered,
							   uint32 serverIP, uint16 serverPort
							   , uint16 partsCount, bool isReliable)
{
	this->Init(userPort, userID, serverIP, serverPort, partsCount, userName, transfered);
	this->SetReliable(isReliable);
	this->m_referenceCount = 1; // HACK by Md5Chap 2002-11-24 (ref in baseclient)
}

void CSourceEntry::Init( uint16 userPort, uint32 userID, uint32 serverIP
			, uint16 serverPort, uint16 partsCount
			, CString userName, uint32 transfered, bool isReliable)	//<- these parameters have default values
{
	this->m_strUserName	=userName;
	this->m_iUserPort	=userPort;
	this->m_listUserIDs.AddTail(userID);
	this->m_iTransfered= transfered;
	this->m_iPartsCount= partsCount;
	this->m_bIsReliable= isReliable;

	this->m_dtLastSeen = COleDateTime::GetCurrentTime();
	this->m_nFailedConnections = 0 ; // Added by Tarod (LS tweak)

//#ifdef _DEBUG
//	//only for testing
//	//todo: delete this
//	this->m_dtLastSeen.SetDateTime(this->m_dtLastSeen.GetYear()
//		,this->m_dtLastSeen.GetMonth()
//		,((this->m_dtLastSeen.GetDay() + ((uint16)rand()))%26)
//		,this->m_dtLastSeen.GetHour()
//		,this->m_dtLastSeen.GetMinute()
//		,((this->m_dtLastSeen.GetSecond() + ((uint16)rand()))%58));
//#endif

	//this->m_obarrayServerList.SetSize(16,32);
	this->m_obarrayServerList.Add(new CServerEntry(serverIP, serverPort));

	//indizes:
	this->m_iServerAtPositon = 0;
	this->m_posUserIDAtPositon = this->m_listUserIDs.GetHeadPosition();
}

CSourceEntry::~CSourceEntry(void)
{
    m_referenceCount--; // HACK by Md5Chap 2002-11-24
	ASSERT(m_referenceCount==0); // HACK by Md5Chap 2002-11-24 (never delete a referenced object!)

	this->m_listUserIDs.RemoveAll();
	
	int length = this->m_obarrayServerList.GetCount();
	for(int i=0; i < length ; i++)
		delete this->m_obarrayServerList[i];	
	this->m_obarrayServerList.RemoveAll();
	
}

bool CSourceEntry::IsEqual(CSourceEntry* toCompare)//does not work correct yet
{
	bool result=false;
	if((this->GetUserName().Compare(toCompare->GetUserName()) == 0)
			&& (this->GetUserPort() == toCompare->GetUserPort())
			&& (this->GetTransfered() == toCompare->GetTransfered())){
		TRACE("CSourceEntry::IsEqual: *toCompare is possibly equal\n");
		result = true;
	}
	
	
	//User-IDs
	uint32 this_userID;
	for(POSITION pos_this= this->m_listUserIDs.GetHeadPosition(); pos_this != NULL;){
		this_userID=this->m_listUserIDs.GetNext(pos_this);
		for(POSITION pos_toComp = toCompare->m_listUserIDs.GetHeadPosition(); pos_toComp!=NULL;){
			if(this_userID != toCompare->m_listUserIDs.GetNext(pos_toComp)){
				TRACE("CStoredSources::IsEqual()-> *toCompare is not equal\n");
				result = false;
			}
		}
	}
	
	int this_listSize	=this->m_obarrayServerList.GetCount();
	int toComp_listSize	=toCompare->m_obarrayServerList.GetCount();
	for(int i=0; i < this_listSize ; i++){
		for(int j=0; j < toComp_listSize ; j++){
			if(!((CServerEntry*) this->m_obarrayServerList[i])->IsEqual(((CServerEntry*)toCompare->m_obarrayServerList[j]))){
				TRACE("CStoredSources::IsEqual() *toCompare is not equal\n");
				result = false;
			}
		}
	}


	return result;
}

bool CSourceEntry::RemoveUserID(uint32 userID)
{
	POSITION pos;
	if((pos=this->m_listUserIDs.Find(userID)) != NULL && this->m_listUserIDs.GetCount() > 0){
		this->m_listUserIDs.RemoveAt(pos);
		return true;
	}
	return false;
}

int CSourceEntry::GetTimeSpanSinceLastSeen()
{
	COleDateTime currentTime = COleDateTime::GetCurrentTime();

	if((currentTime.GetStatus() == COleDateTime::valid)
		&& (this->GetLastSeenTime().GetStatus() == COleDateTime::valid)){
	//get the absolute difference between currentTime and this->m_dtLastSeen
		return abs((this->GetLastSeenTime() - currentTime).GetDays());	//<- the result of this difference is a COleDateTimeSpan
	}
	return -1;
}

// HACK by Md5Chap 2002-11-27
// BEGIN new Source
//
// This Function performs dynamic smooth outdating dependant of its given parameters
// minFail	: This specifies the minimun number of connection retries granted;
// addFail	: This specifies the additional number of connection retries granted
//			  if it is rare(ex: sources<<rareBelow);
// minDays	: This specifies the minimun number of days to be valid;
// addDays	: This specifies the additional number of days to be valid
//			  if it is rare(ex: sources<<rareBelow);;
// rareBelow: This magic number specifies a border. sourcenumbers below are handled
//            smoothly to be rare, that means the less sources you have, the more
//			  additional days and failedconns you get;
//
//  Each value corresponds to the others. That means if your entry has aged
//   for some days, you will have less connectionretries to go, and vice versa;
//  The small asymetry for being harder to connectionretries is wanted, being polite
//   not flooding the net.
//
// if you want to have a look at this function, open your M@ple/Scilab... and enter the following line.
// Rest a while this takes a minute...
// params used(minfail=5,addfail=3,mindays=10,adddays=16,rarebelow=40)
// g:=(x,y)->-5.*(2.*y-13.*RootOf(625*_Z^5+(128*x^2-1250)*_Z^4+300*_Z^3*y-600*_Z^2*y+36*_Z*y^2-72*y^2)^2)/
// RootOf(625*_Z^5+(128*x^2-1250)*_Z^4+300*_Z^3*y-600*_Z^2*y+36*_Z*y^2-72*y^2)^2;
// plot3d({trunc(g(trunc(x),y)),0},x=0..8,y=0..26,view=0..40,axes=boxed,
// labels=["fail","age","sources"],shading=none,lightmodel=light3,grid=[50,50]);
bool CSourceEntry::IsOutdatedSmooth(double fail,double age,double sources)
{
	const double minFail	= 5e+0;
	const double addFail	= 3e+0;
	const double minDays	=10e+0;
	const double addDays	=16e+0;
	const double rareBelow	=40e+0;
	double A,B;
	//double fail,age,sources;

	//bound sources to range & default(-1)
	if (sources<0)				sources=rareBelow;
	else if (sources>rareBelow)		sources=rareBelow;
	// bound failcounter to range
	if (fail<0)					fail=0;
	else if (fail>(minFail+addFail)) fail=(minFail+addFail);
	//bound age(in days) to range
	if (age<0)					age=0;
	else if (age>(minDays+addDays))	age=(minDays+addDays);
    
	A=fail/((((rareBelow-sources)*addFail)/rareBelow)+minFail);
	A=1-A*A;
	B=age/((((rareBelow-sources)*addDays)/rareBelow)+minDays);
	B=1-sqrt(B);
	A=(A+B)-1;
#ifdef _DEBUG
	TRACE("SmoothOutdating: Source of %10s,fail(%.0f),age(%.2f),sources(%.0f) returned %f\n",this->GetUserName(),fail,age,sources,A);
#endif
	return (A<=0);    	
}
// END new Source

// TODO remove this if not needed naymore
//bool CSourceEntry::IsOutdated()
//{
//	if(this->GetReliable())
//		return false;
//
//	if (this->m_nFailedConnections >= OUTDATED_AFTER_TRIES) { // Added by Tarod (LS tweak) two failed conns.
//		return true ;
//	} else if (this->GetLastSeenTime().GetStatus() == COleDateTime::valid) {
//		int timeSpan = this->GetTimeSpanSinceLastSeen();
//		
//		//if time was not valid, set this entry to 'outdated' (-> overwrite or delete)
//		if (timeSpan == -1) return true;
//				
//		//if the timespan is greater or equal than OUTDATED return true (this CSourceEntry is outdated):
//		return (timeSpan >= OUTDATED);
//	}
//	return true;
//}

// HACK by Md5Chap 2002-11-26 (using advanced aging)
bool CSourceEntry::IsOutdated(int numSources)
{
	double dAge,dFailed,dNumSources;

	if(this->GetReliable())
		return false;

	dFailed=this->m_nFailedConnections;
	dNumSources=numSources; // default(-1) will be handled in IsOutdatedSmooth;
	if (m_dtLastSeen!=COleDateTime::invalid){
		double dSDate=this->m_dtLastSeen.m_dt;
		dAge=COleDateTime::GetCurrentTime().m_dt;
		dAge-=dSDate;//If negative or too big, will be fixed in IsOutdatedSmooth
	}else{
		dAge=0;
	};

    return IsOutdatedSmooth(dFailed,dAge,dNumSources);
}

bool CSourceEntry::DeleteOutdatedIDs()
{
	int timeSpan = this->GetTimeSpanSinceLastSeen();
	//low IDs are not valid any more after 2 days (at least I think so)
	//so drop them
	if(timeSpan >= 3){
		//TODO: test if this works right
		for(POSITION pos = this->m_listUserIDs.GetHeadPosition() ; pos != NULL ; ){
			if(this->IsLowID(this->m_listUserIDs.GetAt(pos))){
				this->m_listUserIDs.RemoveAt(pos);
				pos = this->m_listUserIDs.GetHeadPosition();
			}
			else this->m_listUserIDs.GetNext(pos);
		}
	}
	if(this->m_listUserIDs.GetCount() == 0)
		return false;
	
	while(this->m_listUserIDs.GetCount() > MAX_IDS_PER_SOURCE 
			&& !this->m_listUserIDs.IsEmpty()){//just to be sure
		
		this->m_listUserIDs.RemoveHead();
	}
		

	return true;
}

uint16 CSourceEntry::ReduceServersTo(uint16 count)
{
	uint16 i=0;
	//if count is smaller than the array-size, nothing has to be done
	if(count <= this->m_obarrayServerList.GetCount()){
		int countToDeleted = this->m_obarrayServerList.GetCount() - count;
		for(i=0; i < countToDeleted; i++){
			//delete ServerEntry at begin of the CObArray
			delete this->m_obarrayServerList[0];
			//remove this position of the CObArray
			this->m_obarrayServerList.RemoveAt(0);
		}
		TRACE("Reduced ServerEntries for '%s' by %i ServerEntries to a count of %i\n",this->GetUserName(),i,this->m_obarrayServerList.GetCount());
		return i;
	}
	return i;
}

bool CSourceEntry::IsLowID(uint32 id)
{
	return (id < 16777216);
}


///////////////////////Update-Functions:
bool CSourceEntry::AddSourceInformation(CSourceEntry* newInformation)
{
	if((this->GetUserName().Compare(newInformation->GetUserName()) != 0)
		|| (this->GetUserPort() != newInformation->GetUserPort()))
		return false;

	//if(this->GetTransfered() < newInformation->GetTransfered())
	//	this->m_iTransfered = newInformation->m_iTransfered;
	
	if(newInformation->GetLastSeenTime() < this->GetLastSeenTime())//if the Last-Seen-Time in newInformation is newer, take it to this
		this->m_dtLastSeen = newInformation->m_dtLastSeen;

	//PartsCount
	this->m_iPartsCount = newInformation->GetPartsCount();

	//add new userIDs
	uint32 new_userID;
	for(POSITION newInf_pos = newInformation->m_listUserIDs.GetHeadPosition() ; newInf_pos != NULL ;){
		new_userID = newInformation->m_listUserIDs.GetNext(newInf_pos);
		if(!this->m_listUserIDs.Find(new_userID))
			this->m_listUserIDs.AddTail(new_userID);
	}

	//add new servers
	CServerEntry* newServerEntry;
	bool noNewServer;
	int this_serverCount = this->m_obarrayServerList.GetCount();
	int new_serverCount = newInformation->m_obarrayServerList.GetCount();
	for(int i=0; i < new_serverCount ; i++){
		noNewServer = true;
		newServerEntry = (CServerEntry*) newInformation->m_obarrayServerList[i];
		for(int j=0 ; j < this_serverCount ; j++){
			if(newServerEntry->IsEqual(((CServerEntry*)this->m_obarrayServerList[j]))){
				((CServerEntry*)this->m_obarrayServerList[j])->IncrementCounter();
				noNewServer = false;
			}
		}
		if(! noNewServer) 
			this->m_obarrayServerList.Add(new CServerEntry(newServerEntry->GetServerIP(),newServerEntry->GetServerPort()));
	}
	return true;

}


void CSourceEntry::AddSourceInformation(uint32 userID, uint32 serverIP, uint16 serverPort, uint16 partsCount)
{
	//call StandardAddInformation(...) with default value for userName (NONAME)
	this->StandardAddInformation(userID, serverIP, serverPort, partsCount);
}

void CSourceEntry::AddSourceInformation(CString userName, uint32 userID, uint32 serverIP
										 , uint16 serverPort, uint16 partsCount)
{
	//call StandardAddInformation(...)
	this->StandardAddInformation(userID, serverIP, serverPort, partsCount, userName);
}

void CSourceEntry::StandardAddInformation(uint32 userID, uint32 serverIP
										, uint16 serverPort, uint16 partsCount
										, CString userName)//<- this parameter has a default value
{
	if(userName.Compare(NONAME) != 0)	//is the userName-parameter a real name
										//(means: different from NONAME)?, if so then...
		if(! this->GetUserName().Compare(NONAME))
			/*&& (userName != NULL) && (userName.Compare("") != 0))*/
			// Modified by Tardo (fixed a wrong assignment)
			this->SetUserName(userName)	;	//if this user has no name yet
											//, set the new name, given by
											//the userName-paramater

	//update PartsCount
	this->m_iPartsCount = partsCount;
	


	//update user-IDs (if still existing, move to end of the list)
	POSITION pos;
	if((pos = this->m_listUserIDs.Find(userID)) == NULL)
		this->m_listUserIDs.AddTail(userID);
	else{
		this->m_listUserIDs.RemoveAt(pos);
		this->m_listUserIDs.AddTail(userID);
	}
	
	//update servers on which the user were 
	bool isExisting = false;
	int length = this->m_obarrayServerList.GetCount();
	for(int i=0; i < length ; i++){
		if(serverIP == 0 || serverPort == 0){
			isExisting = true; //don't add this invalid server
			break;
		}
		if ((((CServerEntry*)this->m_obarrayServerList[i])->GetServerIP() == serverIP)
			   && (((CServerEntry*)this->m_obarrayServerList[i])->GetServerPort() == serverPort)){
			
		   //get server-entry, increment its occurence-counter
			CServerEntry* current_server = (CServerEntry*)this->m_obarrayServerList[i];
			current_server->IncrementCounter();

			//put at the end of the array (-> indicates, that this is the server, this user was on the last time):
			//remove from current position...
			this->m_obarrayServerList.RemoveAt(i);
			//... and add at the end of the array
			this->m_obarrayServerList.Add(current_server);


			isExisting = true;
		}
	}
	if(!isExisting) this->m_obarrayServerList.Add(new CServerEntry(serverIP, serverPort));

	//update last seen time for this user
	// HACK by Md5Chap 2002-11-26 (Moved to CUpDownClient::ConnectionEstablished,
	// because this method is called via SavetoStoredSources, and thus we would not
	// get older.
	//this->m_dtLastSeen = COleDateTime::GetCurrentTime();

//#ifdef _DEBUG
//	//only for testing
//	//todo: delete this
//	this->m_dtLastSeen.SetDateTime(this->m_dtLastSeen.GetYear()
//		,this->m_dtLastSeen.GetMonth()
//		,((this->m_dtLastSeen.GetDay() + ((uint16)rand()))%26)
//		,this->m_dtLastSeen.GetHour()
//		,this->m_dtLastSeen.GetMinute()
//		,((this->m_dtLastSeen.GetSecond() + ((uint16)rand()))%58));
//#endif

}

/////////////////////////Get-Functions:
uint32 CSourceEntry::GetFirstUserID()
{
	if ((this->m_posUserIDAtPositon = this->m_listUserIDs.GetHeadPosition()) != NULL)
		return this->m_listUserIDs.GetNext(this->m_posUserIDAtPositon);
	return 0;
}

uint32 CSourceEntry::GetNextUserID()
{
	if(this->m_posUserIDAtPositon != NULL){
#ifdef _DEBUG
		//TRACE("\n\nNow dumping a CSourceEntry (%s)...\n",this->m_posUserIDAtPositon);
		//this->Dump(afxDump);
#endif
		return (uint32)this->m_listUserIDs.GetNext(this->m_posUserIDAtPositon);
	}
	else 
		this->m_posUserIDAtPositon = this->m_listUserIDs.GetHeadPosition();
	return 0;
}

CServerEntry* CSourceEntry::GetFirstServerEntry()
{
	this->m_iServerAtPositon=0;
	if(this->m_iServerAtPositon < this->m_obarrayServerList.GetCount())
		return ((CServerEntry*)this->m_obarrayServerList[this->m_iServerAtPositon]);
	return NULL;
}

CServerEntry* CSourceEntry::GetNextServerEntry()
{
	this->m_iServerAtPositon++;
	if(this->m_iServerAtPositon < this->m_obarrayServerList.GetCount())
			return ((CServerEntry*)this->m_obarrayServerList[this->m_iServerAtPositon]);
	else this->m_iServerAtPositon = 0;
	return NULL;
}

CServerEntry* CSourceEntry::GetPreviousServerEntry()
{
	this->m_iServerAtPositon--;
	if(/*this->m_iServerAtPositon >= 0 &&*/ this->m_iServerAtPositon < this->m_obarrayServerList.GetCount())
			return ((CServerEntry*)this->m_obarrayServerList[this->m_iServerAtPositon]);
	
	else{
		if(this->m_obarrayServerList.GetCount() > 0)
			this->m_iServerAtPositon = this->m_obarrayServerList.GetCount()-1;
		else
			this->m_iServerAtPositon=0;
	}
	return NULL;
}

CServerEntry* CSourceEntry::GetLastServerEntry()
{
	int index;
	if(this->m_obarrayServerList.GetCount() > 0)
		index = this->m_obarrayServerList.GetCount()-1;
	else{
		this->m_iServerAtPositon=0;
		//TODO: return standard-server (one that is always valid) if no CServerEntry exists
		return NULL;
	}

	this->m_iServerAtPositon = index;
	return ((CServerEntry*)this->m_obarrayServerList[index]);
}

/////////////////////Serialize:
void CSourceEntry::Serialize(CArchive& ar)
{
//#ifdef _DEBUG
//		COleDateTime startTime = COleDateTime::GetCurrentTime();
//		COleDateTime endTime = COleDateTime::GetCurrentTime();
//#endif
	CObject::Serialize(ar);
//#ifdef _DEBUG
//	if(ar.IsStoring()){
//		endTime = COleDateTime::GetCurrentTime();
//		TRACE("saving raw CObject for '%s'-SourceEntry took %s min\n"
//				,this->GetUserName(),(endTime-startTime).Format("%M:%S"));
//		startTime = COleDateTime::GetCurrentTime();
//	}
//#endif
	m_listUserIDs.Serialize(ar);
//#ifdef _DEBUG
//	if(ar.IsStoring()){
//		endTime = COleDateTime::GetCurrentTime();
//		TRACE("saving all UserIDs for '%s'-SourceEntry took %s min\n"
//				,this->GetUserName(),(endTime-startTime).Format("%M:%S"));
//		startTime = COleDateTime::GetCurrentTime();
//	}
//#endif
	m_obarrayServerList.Serialize(ar);
//#ifdef _DEBUG
//	//if(ar.IsStoring()){
//		endTime = COleDateTime::GetCurrentTime();
//		TRACE("saving all ServerEntries for '%s'-SourceEntry took %s min\n"
//				,this->GetUserName(),(endTime-startTime).Format("%M:%S"));
//	//}
//#endif

	if(ar.IsStoring()){
			//HACK by Md5Chap 2002-11-27 (added failedconnections to store!)
		ar << m_strUserName << m_iUserPort << m_dtLastSeen << m_iPartsCount << m_bIsReliable << m_nFailedConnections;
	}
	else{
		//for the different versions of CSourceEntry:
		int iVersion = ar.GetObjectSchema();
		//TRACE("This CSourceEntry for %s has the Version '%i'\n",this->GetUserName(),iVersion);
		switch(iVersion){
			case 0:
			case 1:
				ar >> m_strUserName >> m_iUserPort;
				break;
			case 2:
				ar >> m_strUserName >> m_iUserPort >> m_dtLastSeen;
				this->m_iPartsCount = 0;
				break;
			case 3:
				ar >> m_strUserName >> m_iUserPort >> m_dtLastSeen >> m_iPartsCount;
				break;
			case 4:
				ar >> m_strUserName >> m_iUserPort >> m_dtLastSeen >> m_iPartsCount >> m_bIsReliable;
				break;
			case 5:
					//HACK by Md5Chap 2002-11-27 (added failedconnections to store!)
				ar >> m_strUserName >> m_iUserPort >> m_dtLastSeen >> m_iPartsCount >> m_bIsReliable >> m_nFailedConnections;
				break;
			default:
				TRACE("The CSourceEntry on disk has %s Version number (%i)\n",(iVersion == -1?"a unknown":"the wrong"),iVersion);
		}
	}
}

#ifdef _DEBUG
	void CSourceEntry::Dump( CDumpContext& dc ) const
	{
		// call base class function first
		CObject::Dump( dc );
		m_listUserIDs.Dump(dc);
		m_obarrayServerList.Dump(dc);

		// now do the stuff for our specific class
		dc << "User Name: " << m_strUserName << "\n"
			<< "User Port: " << m_iUserPort << "\n"
			<< "Transfered: " << m_iTransfered << "\n"			
			<< "Last-Seen-Time: " << m_dtLastSeen << "\n"
			<< "User-Positon " << m_posUserIDAtPositon << "\n"
			<< "Server-Position: " << m_iServerAtPositon << "\n";
	}
#endif


///////////////////////////////////////////////////////////////////////////////////////
//Class: CServerEntry

///////////////////////Constructors:
CServerEntry::CServerEntry(void)
: m_iServerIP(0)
, m_iServerPort(4661)
, m_iCounter(1)
{
}

CServerEntry::CServerEntry(uint32 serverIP, uint16 serverPort)
{
	this->m_iServerIP=serverIP;
	this->m_iServerPort=serverPort;
	this->m_iCounter=1;
}

CServerEntry::CServerEntry(uint32 serverIP, uint16 serverPort, uint16 counter)
{
	this->m_iServerIP=serverIP;
	this->m_iServerPort=serverPort;
	this->m_iCounter=counter;
}

CServerEntry::~CServerEntry(void)
{
}

//see *.h file for informations about CServerEntry::GetFullServerIP()
char* CServerEntry::GetFullServerIP(){
	char* fullIP = new char[16];
	in_addr host;
	host.S_un.S_addr = this->GetServerIP();
	strcpy(fullIP,inet_ntoa(host));
	return fullIP;
}

bool CServerEntry::IsEqual(CServerEntry* toCompare)
{
	if((this->GetServerIP() == toCompare->GetServerIP()) 
			&& (this->GetServerPort() == toCompare->GetServerPort()))
			//&& (this->GetCounter() == toCompare->GetCounter()))
		return true;
	else return false;
}


//////////////////////////Serialize:
void CServerEntry::Serialize(CArchive& ar)
{
	CObject::Serialize(ar);

	if(ar.IsStoring()){
		ar << m_iServerIP << m_iServerPort << m_iCounter;
	}
	else{
		//for the different versions of CServerEntry:
		int iVersion = ar.GetObjectSchema();
		//TRACE("This CServerEntry for %s has the Version '%i'\n",this->GetUserName(),iVersion);
		
		int counter;		
		switch(iVersion){
			case 0:
			case 1:
				ar >> m_iServerIP >> m_iServerPort >> counter;
				if(counter <= USHRT_MAX && counter >= 0)
					m_iCounter = (uint16) counter;
				else if(counter < 0)
					m_iCounter = 1;
					else m_iCounter = USHRT_MAX;
				break;
			case 2:
				ar >> m_iServerIP >> m_iServerPort >> m_iCounter;
				break;
			default:
				TRACE("The CServerEntry on disk has the %s Version number (%i)",(iVersion == -1?"unknown":"wrong"),iVersion);
		}
	}
}

#ifdef _DEBUG
	void CServerEntry::Dump( CDumpContext& dc ) const
	{
		// call base class function first
		CObject::Dump( dc );

		// now do the stuff for our specific class
		dc << "Server IP: " << m_iServerIP << "\n"
			<< "Server Port: " << m_iServerPort << "\n"
			<< "Counter: " << m_iCounter << "\n";
	}
#endif

////////////////////////////////////////////////////////////////////////////////////
//Class: CStoredSourcesContainer


CStoredSourcesContainer::CStoredSourcesContainer(void)
: m_iVersion(1)
{
	this->m_dtLastUpdated = COleDateTime::GetCurrentTime();
}

CStoredSourcesContainer::CStoredSourcesContainer(CStoredSources* toSave)
: m_iVersion(1)
{
	CStoredSources* copy= new CStoredSources(toSave->GetPartFileName(), toSave->GetFileName(), toSave->GetFileHash());
	copy->UpdateSource(toSave);
	this->m_obarrayOfStoredSources.Add(copy);
	this->m_dtLastUpdated = COleDateTime::GetCurrentTime();
}

CStoredSourcesContainer::~CStoredSourcesContainer(void)
{
	int index = this->m_obarrayOfStoredSources.GetCount();
	for(int i=0 ; i < index; i++)
		delete this->m_obarrayOfStoredSources[i];

	this->m_obarrayOfStoredSources.RemoveAll();
}


bool CStoredSourcesContainer::Add(CStoredSources* toSave)
{
	if(this->GetByFileID(toSave->GetFileID()) == NULL){
		CStoredSources* copy= new CStoredSources(toSave->GetPartFileName(), toSave->GetFileName(), toSave->GetFileHash());
		copy->UpdateSource(toSave);
		this->m_obarrayOfStoredSources.Add(copy);
		this->m_dtLastUpdated = COleDateTime::GetCurrentTime();
	}
	else return false;

	return true;
}

CStoredSources*	CStoredSourcesContainer::GetAt(int iIndex)
{
	if((iIndex >= 0) && iIndex < this->m_obarrayOfStoredSources.GetCount())
		return (CStoredSources*)this->m_obarrayOfStoredSources.GetAt(iIndex);

	return NULL;
}

CStoredSources*	CStoredSourcesContainer::GetByPartName(CString partName)
{
	int index = this->m_obarrayOfStoredSources.GetCount();
	for(int i=0 ; i < index; i++)
		if(((CStoredSources*)this->m_obarrayOfStoredSources[i])->GetPartFileName().Compare(partName) == 0)
			return ((CStoredSources*)this->m_obarrayOfStoredSources[i]);

	return NULL;
}

CStoredSources*	CStoredSourcesContainer::GetByFileID(CString fileID)
{
	int index = this->m_obarrayOfStoredSources.GetCount();
	for(int i=0 ; i < index; i++)
		if(((CStoredSources*)this->m_obarrayOfStoredSources[i])->GetFileID().Compare(fileID) == 0)
			return ((CStoredSources*)this->m_obarrayOfStoredSources[i]);

	return NULL;
}

CStoredSources*	CStoredSourcesContainer::GetByFileHash(uchar* fileHash)
{
	//convert uchar* to a CString of File-ID
	CString fileID;
	fileID.Append(EncodeBase16(fileHash, 16));


	int index = this->m_obarrayOfStoredSources.GetCount();
	for(int i=0 ; i < index; i++)
		if(((CStoredSources*)this->m_obarrayOfStoredSources[i])->GetFileID().Compare(fileID) == 0)
			return ((CStoredSources*)this->m_obarrayOfStoredSources[i]);

	return NULL;
}

void CStoredSourcesContainer::Serialize(CArchive& ar)
{
	CObject::Serialize(ar);

	this->m_obarrayOfStoredSources.Serialize(ar);

	if(ar.IsStoring()){
		ar << m_iVersion << m_dtLastUpdated;
	}
	else{
		ar >> m_iVersion >> m_dtLastUpdated;
	}

}

