// This file is part of the aMule Project
//
// Copyright (c) 2003-2004 aMule Project ( http://www.amule-project.net )
// 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 "sys.h"
#include "ClientList.h"		// Interface declarations.
#include "ListenSocket.h"	// Needed for CClientReqSocket
#include "DownloadQueue.h"	// Needed for CDownloadQueue
#include "UploadQueue.h"	// Needed for CUploadQueue
#include "CamuleAppBase.h"	// Needed for theApp
#include "updownclient.h"	// Needed for CUpDownClient

CClientList::CClientList(){
}

CClientList::~CClientList(){
}

// xrmb : statsclientstatus
void CClientList::GetStatistics(uint32 &totalclient, int stats[], CMap<uint8, uint8, uint32, uint32> *clientStatus, CMap<uint16, uint16, uint32, uint32> *clientVersionEDonkey, CMap<uint16, uint16, uint32, uint32> *clientVersionEDonkeyHybrid, CMap<uint8, uint8, uint32, uint32> *clientVersionEMule){
	//if(clientStatus)		clientStatus->RemoveAll();
	totalclient = list.GetCount();
	if(clientVersionEDonkey)	clientVersionEDonkey->RemoveAll();
	if(clientVersionEMule)		clientVersionEMule->RemoveAll();
	POSITION pos1, pos2;

	for (int i=0;i<9;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_UNKNOWN : stats[0]++;break;
			case SO_EDONKEY : 
				stats[1]++;
				if(clientVersionEDonkey)
					(*clientVersionEDonkey)[cur_client->GetVersion()]++;
				break;
			case SO_EDONKEYHYBRID : 
				stats[4]++;
				if(clientVersionEDonkeyHybrid)
					(*clientVersionEDonkeyHybrid)[cur_client->GetVersion()]++;
				break;
			case SO_EMULE   :
			case SO_OLDEMULE:
				stats[2]++;
				if(clientVersionEMule) {
					uint8 version = cur_client->GetMuleVersion();
					(*clientVersionEMule)[version]++;
				}
				break;
			case SO_CDONKEY : //Didn't get much time to test this "aMule Compatable" feature.
				stats[5]++;
				break;
			case SO_LXMULE:
				stats[6]++;
				break;
			case SO_AMULE:
				stats[8]++;
				break;
			case SO_MLDONKEY:
				stats[3]++;
				break;
			case SO_NEW_MLDONKEY:
				stats[7]++;
				break;
		}

		//if(clientStatus) (*clientStatus)[cur_client->GetDownloadState()]++;
	}
}


void CClientList::AddClient(CUpDownClient* toadd,bool bSkipDupTest){
	if ( !bSkipDupTest){
		if(list.Find(toadd))
			return;
	}
	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);
		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 == cur_client) {
			return true;
		}
		// Sanity check. but FIXME if we get reports about this message
		// It's due to a void client on list. Why?
		if (cur_client == 0) {
			printf("WARNING!!!! VOID CLIENT ON LIST.\n");
			printf("PLEASE REPORT ON forums.amule.org\n");
			return false;
		} else {
			if (tocheck->Compare(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;
				*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 (memcmp(cur_client->GetUserHash() ,clienthash,16)  )
				return cur_client;
	}
	return 0;
}


void CClientList::Debug_SocketDeleted(CClientReqSocket* deleted){
	POSITION pos1, pos2;
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
	}
}

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