//this file is part of eMule
//Copyright (C)2002 Merkur ( merkur-@users.sourceforge.net / http://www.emule-project.net )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either
//version 2 of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

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

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


CClientList::CClientList(){
}

CClientList::~CClientList(){
}

// xrmb : statsclientstatus
// -khaos--+++> Rewritten to accomodate some new statistics, and just for cleanup's sake.
//				I've added three new stats: Number of cDonkey clients, # errored clients, # banned clients.
//				We also now support LMule
void CClientList::GetStatistics(uint32 &totalclient, int stats[], CMap<uint16, uint16, uint32, uint32> *clientVersionEDonkey, CMap<uint16, uint16, uint32, uint32> *clientVersionEDonkeyHybrid, CMap<uint8, uint8, uint32, uint32> *clientVersionEMule, CMap<uint8, uint8, uint32, uint32> *clientVersionLMule){
	totalclient = list.GetCount();
	if(clientVersionEDonkeyHybrid)	clientVersionEDonkeyHybrid->RemoveAll();
	if(clientVersionEDonkey)		clientVersionEDonkey->RemoveAll();
	if(clientVersionEMule)			clientVersionEMule->RemoveAll();
	if(clientVersionLMule)			clientVersionLMule->RemoveAll();
	POSITION pos1, pos2;

	for (int i=0;i<12;i++) stats[i]=0;

	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
		CUpDownClient* cur_client =	list.GetAt(pos2);
		
		switch (cur_client->GetClientSoft()) {

			case SO_EDONKEY :				
				if(clientVersionEDonkey)
				{
					stats[1]++;
					(*clientVersionEDonkey)[cur_client->GetVersion()]++;
				}
				break;

			case SO_EDONKEYHYBRID : 
				if(clientVersionEDonkeyHybrid)
				{
					stats[4]++;
					(*clientVersionEDonkeyHybrid)[cur_client->GetVersion()]++;
				}
				break;
			case SO_EMULE   :
			case SO_OLDEMULE:
				if(clientVersionEMule)
				{
					stats[2]++;
					uint8 version = cur_client->GetMuleVersion();
					if (version != 0xFF && version != 0x66)
					{
						if ((*clientVersionEMule)[version] < 1) (*clientVersionEMule)[version] = 0;
						(*clientVersionEMule)[version]++;
					}
				}
				break;

			case SO_LMULE:
				if(clientVersionLMule)
				{
					stats[10]++;
					uint8 version = cur_client->GetMuleVersion();
					if ((*clientVersionLMule)[version] < 1) (*clientVersionLMule)[version] = 0;
					(*clientVersionLMule)[version]++;
				}

				break;

			case SO_UNKNOWN :	stats[0]++;		break;
			case SO_CDONKEY :	stats[5]++;		break;
			case SO_MLDONKEY:	stats[3]++;		break;
			case SO_SHAREAZA:   stats[11]++;    break;
		}

		switch (cur_client->GetDownloadState()) {
			case 9:
				stats[6]++; // Error
				break;
			case 10:
				stats[7]++; // Banned
				break;
			default:
				switch (cur_client->GetUploadState()) {
					case 6:
						stats[7]++; // Banned
						break;
					case 7:
						stats[6]++; // Error
				}
		}

		switch (cur_client->GetUserPort()) {
			case 4662:
				stats[8]++; // Default Port
				break;
			default:
				stats[9]++; // Other Port
		}
	}
}
// <-----khaos-


void CClientList::AddClient(CUpDownClient* toadd,bool bSkipDupTest){
	if ( !bSkipDupTest){
		if(list.Find(toadd))
			return;
	}
	theApp.emuledlg->transferwnd.clientlistctrl.AddClient(toadd);
	list.AddTail(toadd);
}

void CClientList::RemoveClient(CUpDownClient* toremove){
	POSITION pos = list.Find(toremove);
	if (pos){
		//just to be sure...
		theApp.uploadqueue->RemoveFromUploadQueue(toremove);
		theApp.uploadqueue->RemoveFromWaitingQueue(toremove);
		theApp.downloadqueue->RemoveSource(toremove);
		theApp.emuledlg->transferwnd.clientlistctrl.RemoveClient(toremove);
		list.RemoveAt(pos);
	}
}

void CClientList::DeleteAll(){
	theApp.uploadqueue->DeleteAll();
	theApp.downloadqueue->DeleteAll();
	POSITION pos1, pos2;
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
		CUpDownClient* cur_client =	list.GetAt(pos2);
		list.RemoveAt(pos2);
		delete cur_client; // recursiv: this will call RemoveClient
	}
}

bool CClientList::AttachToAlreadyKnown(CUpDownClient** client, CClientReqSocket* sender){
	POSITION pos1, pos2;
	CUpDownClient* tocheck = (*client);
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
		CUpDownClient* cur_client =	list.GetAt(pos2);
		if (tocheck->Compare(cur_client)){
			ASSERT ( (tocheck != cur_client) );
			if (sender){
				if (cur_client->socket){
					cur_client->socket->client = 0;
					cur_client->socket->Safe_Delete();
				}
				cur_client->socket = sender;
				tocheck->socket = 0;
			}
			*client = 0;
			delete tocheck;
			// TODO: I think we should reset some client properties here (like m_byEmuleVersion, 
			// m_byDataCompVer, m_bySourceExchangeVer,...). If we attach to a client instance 
			// which has already set some eMule specific properties but is no longer responding
			// to an OP_EMULEINFO message we may deal with that client in a wrong way.
			*client = cur_client;
			return true;
		}
	}
	return false;
}

CUpDownClient* CClientList::FindClientByIP(uint32 clientip,uint16 port){
	POSITION pos1, pos2;
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
		CUpDownClient* cur_client =	list.GetAt(pos2);
		if (cur_client->GetIP() == clientip && cur_client->GetUserPort() == port)
			return cur_client;
	}
	return 0;
}

CUpDownClient* CClientList::FindClientByUserHash(uchar* clienthash){
	POSITION pos1, pos2;
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
		CUpDownClient* cur_client =	list.GetAt(pos2);
		if (!md4cmp(cur_client->GetUserHash() ,clienthash))
			return cur_client;
	}
	return 0;
}


void CClientList::Debug_SocketDeleted(CClientReqSocket* deleted){
	POSITION pos1, pos2;
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
		CUpDownClient* cur_client =	list.GetAt(pos2);
		if (!AfxIsValidAddress(cur_client, sizeof(CUpDownClient))) {
			AfxDebugBreak();
		}
		if (cur_client->socket == deleted){
			AfxDebugBreak();
		}
	}
}

bool CClientList::Debug_IsValidClient(CUpDownClient* tocheck){
	return list.Find(tocheck);
}

