//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 "searchlist.h"

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


CSearchFile::CSearchFile(CFile* in_data,uint32 nSearchID,uint32 nServerIP,uint16 nServerPort, LPCTSTR pszDirectory){
	
	m_nSearchID = nSearchID;
	in_data->Read(&m_abyFileHash,16);
	in_data->Read(&clientip,4);
	in_data->Read(&clientport,2);
	uint32 tagcount;
	in_data->Read(&tagcount,4);
	//TRACE("Search Result: %s  Client=%u.%u.%u.%u:%u  Tags=%u\n", md4str(m_abyFileHash), (uint8)clientip[0], (uint8)clientip[1], (uint8)clientip[2], (uint8)clientip[3], clientport, tagcount);
	for (int i = 0;i != tagcount; i++){
		CTag* toadd = new CTag(in_data);
		//TRACE("  %s\n", toadd->GetFullInfo());
		taglist.Add(toadd);
	}

	int iSize = 2;
	char* tempName = GetStrTagValue(FT_FILENAME);
	if( tempName != NULL && (strlen(tempName)>0) )
		iSize = (int)strlen(tempName)+1;
	m_pszFileName = new char[iSize];
	if (tempName == NULL)
		strcpy(m_pszFileName, "?");
	else
		strcpy(m_pszFileName, tempName);
	m_nFileSize = GetIntTagValue(FT_FILESIZE);
	m_nClientServerIP = nServerIP;
	m_nClientServerPort = nServerPort;
	m_pszDirectory = pszDirectory ? nstrdup(pszDirectory) : NULL;
}

CSearchFile::~CSearchFile(){
	for (int i = 0; i < taglist.GetSize();i++)
		safe_delete(taglist[i]);
	taglist.RemoveAll();
	taglist.SetSize(0);
	delete[] m_pszDirectory;
}

uint32 CSearchFile::GetIntTagValue(uint8 tagname){
	for (int i = 0; i < taglist.GetSize(); i++){
		if (taglist[i]->tag.specialtag == tagname)
			return taglist[i]->tag.intvalue;
	}
	return 0;
}
char* CSearchFile::GetStrTagValue(uint8 tagname){
	for (int i = 0; i < taglist.GetSize(); i++){
		if (taglist[i]->tag.specialtag == tagname)
			return taglist[i]->tag.stringvalue;			
	}
	return 0;
}
uint32 CSearchFile::AddSources(uint32 count){
	for (int i = 0; i < taglist.GetSize(); i++){
		if (taglist[i]->tag.specialtag == FT_SOURCES){
			taglist[i]->tag.intvalue += count;
			return taglist[i]->tag.intvalue;
		}
	}
	return 0;
}


uint32 CSearchFile::GetSourceCount(){
	return GetIntTagValue(FT_SOURCES);
}

CSearchList::CSearchList(){
	outputwnd = 0;
}

CSearchList::~CSearchList(){
	Clear();
}

void CSearchList::Clear(){
	for(POSITION pos = list.GetHeadPosition(); pos != NULL; pos = list.GetHeadPosition()) {
		delete list.GetAt(pos);
		list.RemoveAt(pos);
	}
}

void CSearchList::RemoveResults( uint32 nSearchID){
	// this will not delete the item from the window, make sure your code does it if you call this
	ASSERT( outputwnd );
	POSITION pos1, pos2;
	for (pos1 = list.GetHeadPosition();( pos2 = pos1 ) != NULL;){
		list.GetNext(pos1);
		CSearchFile* cur_file =	list.GetAt(pos2);
		if( cur_file->GetSearchID() == nSearchID ){
			list.RemoveAt(pos2);
			delete cur_file;
		}
	}
}

void CSearchList::ShowResults( uint32 nSearchID){
	ASSERT( outputwnd );
	outputwnd->SetRedraw(false);
	for (POSITION pos = list.GetHeadPosition(); pos !=0;list.GetNext(pos)){
		if( ((CSearchFile*)list.GetAt(pos))->GetSearchID() == nSearchID ){
			outputwnd->AddResult(list.GetAt(pos));
		}
	}
	outputwnd->SetRedraw(true);
}

void CSearchList::RemoveResults( CSearchFile* todel ){
	for (POSITION pos = list.GetHeadPosition(); pos !=0;list.GetNext(pos)){
		if( (CSearchFile*)list.GetAt(pos) == todel ){
			theApp.emuledlg->searchwnd.searchlistctrl.RemoveResult( todel );
			list.RemoveAt(pos);
			delete todel;
			return;
		}
	}
}

void CSearchList::NewSearch(CSearchListCtrl* in_wnd, CString resTypes, uint16 nSearchID){
	if(in_wnd)
		outputwnd = in_wnd;

	resultType=resTypes;
	m_nCurrentSearch = nSearchID;
	myHashList="";
	
	foundFilesCount.SetAt(nSearchID,0);
}

uint16 CSearchList::ProcessSearchanswer(char* in_packet, uint32 size, CUpDownClient* Sender, LPCTSTR pszDirectory){
	ASSERT( Sender != NULL );
	// Elandal: Assumes sizeof(void*) == sizeof(uint32)
	uint32 nSearchID = (uint32)Sender;
	if (theApp.emuledlg->searchwnd.CreateNewTab(Sender->GetUserName(), nSearchID))
		foundFilesCount.SetAt(nSearchID,0);

	CSafeMemFile packet((BYTE*)in_packet,size);
	uint32 results;
	packet.Read(&results,4);

	for (int i = 0; i != results; i++){
		CSearchFile* toadd = new CSearchFile(&packet, nSearchID, 0, 0, pszDirectory);
		if (Sender){
			toadd->SetClientID(Sender->GetUserID());
			toadd->SetClientPort(Sender->GetUserPort());
			toadd->SetClientServerIP(Sender->GetServerIP());
			toadd->SetClientServerPort(Sender->GetServerPort());
		}
		AddToList(toadd, true);
	}
	
	packet.Close();
	return GetResultCount();
}

uint16 CSearchList::ProcessSearchanswer(char* in_packet, uint32 size, uint32 nServerIP, uint16 nServerPort){
	CSafeMemFile packet((BYTE*)in_packet,size);
	uint32 results;
	packet.Read(&results,4);

	for (int i = 0; i != results; i++){
		CSearchFile* toadd = new CSearchFile(&packet, m_nCurrentSearch);
		toadd->SetClientServerIP(nServerIP);
		toadd->SetClientServerPort(nServerPort);
		AddToList(toadd, false);
	}
	
	packet.Close();
	return GetResultCount();
}

uint16 CSearchList::ProcessUDPSearchanswer(char* in_packet, uint32 size, uint32 nServerIP, uint16 nServerPort){
	CSafeMemFile packet((BYTE*)in_packet,size);
	CSearchFile* toadd = new CSearchFile(&packet, m_nCurrentSearch, nServerIP, nServerPort);

	UINT uAddData = packet.GetLength() - packet.GetPosition();
	if (uAddData > 0){
		if (uAddData == 1){
			BYTE uByte;
			packet.Read(&uByte, 1);
			if (uByte != 0x00 && uByte != 0x01)
				TRACE("*** NOTE: ProcessUDPSearchanswer from %s:%u: add. data: %s\n", inet_ntoa(*(in_addr*)&nServerIP), nServerPort, GetHexDump(&uByte, 1));
		}
		else{
			// Some of the big servers started to send some strange UDP search answer packets which mostly
			// contain 8 additional bytes (sometimes even 40). Those packets seem to have the ClientID+Port
			// fields filled with some data which is neither a ClientID nor a Port.
			TRACE("*** NOTE: ProcessUDPSearchanswer from %s:%u; add. data: %s\n", inet_ntoa(*(in_addr*)&nServerIP), nServerPort, GetHexDump((uint8*)in_packet + packet.GetPosition(), uAddData));
			toadd->SetClientID(0);
			toadd->SetClientPort(0);
		}
	}

	AddToList(toadd);

	packet.Close();
	return GetResultCount();
}

bool CSearchList::AddToList(CSearchFile* toadd, bool bClientResponse){
	if (!bClientResponse && !(resultType.CompareNoCase(GetResString(IDS_SEARCH_ANY))==0 || GetFiletypeByName(toadd->GetFileName())==resultType)){
		delete toadd;
		return false;
	}
	for (POSITION pos = list.GetHeadPosition(); pos != NULL; list.GetNext(pos)){
		CSearchFile* cur_file = list.GetAt(pos);
		if ( (!md4cmp(toadd->GetFileHash(),cur_file->GetFileHash())) && cur_file->GetSearchID() ==  toadd->GetSearchID()){
			cur_file->AddSources(toadd->GetIntTagValue(FT_SOURCES));
			if (toadd->GetClientID() && toadd->GetClientPort()){
				// pre-filter sources which would be dropped in CPartFile::AddSources
				if (CPartFile::CanAddSource(toadd->GetClientID(), toadd->GetClientPort(), toadd->GetClientServerIP(), toadd->GetClientServerPort())){
					CSearchFile::SClient client;
					client.nIP = toadd->GetClientID();
					client.nPort = toadd->GetClientPort();
					client.nServerIP = toadd->GetClientServerIP();
					client.nServerPort = toadd->GetClientServerPort();
					cur_file->AddClient(client);
				}
			}
			if (outputwnd)
				outputwnd->UpdateSources(cur_file);
			delete toadd;
			return true;
		}
	}
	
	if (list.AddTail(toadd)) {	
		uint16 tempValue;
		VERIFY( foundFilesCount.Lookup(toadd->GetSearchID(),tempValue) );
		foundFilesCount.SetAt(toadd->GetSearchID(),tempValue+1);
	}
	if (outputwnd)
		outputwnd->AddResult(toadd);
	return true;
}

uint16 CSearchList::GetResultCount(uint32 nSearchID) {
	uint16 hits = 0;
	for (POSITION pos = list.GetHeadPosition(); pos != NULL; list.GetNext(pos)){
		if( list.GetAt(pos)->GetSearchID() == nSearchID )
			hits += list.GetAt(pos)->GetIntTagValue(FT_SOURCES);
	}
	return hits;
}


uint16 CSearchList::GetResultCount(){
	return GetResultCount(m_nCurrentSearch);
}


CString CSearchList::GetWebList(CString linePattern) {
	CSearchFile* sf;
	CString buffer="";
	CString temp;

	for (POSITION pos = list.GetHeadPosition(); pos !=0;list.GetNext(pos)){
		sf= (CSearchFile*)list.GetAt(pos);

		// colorize
		CString coloraddon="";CString coloraddonE="";
		CKnownFile* sameFile = theApp.sharedfiles->GetFileByID(sf->GetFileHash());
		if (!sameFile)
			sameFile = theApp.downloadqueue->GetFileByID(sf->GetFileHash());

		if (sameFile) {
			if (sameFile->IsPartFile())
				coloraddon="<font color=\"#FF0000\">";
			else
				coloraddon="<font color=\"#00FF00\">";
		}
		if (coloraddon.GetLength()>0) coloraddonE="</font>";

		CString myHash=EncodeBase16(sf->GetFileHash(),16);
		temp.Format(linePattern,
			coloraddon+StringLimit(sf->GetFileName(),70)+coloraddonE,
			CastItoXBytes(sf->GetFileSize()),
			myHash,
			sf->GetSourceCount(),
			myHash
			);
		buffer.Append(temp);
	}
	return buffer;
}

void CSearchList::AddFileToDownloadByHash(uchar* hash,uint8 cat) {
	CSearchFile* sf;

	for (POSITION pos = list.GetHeadPosition(); pos !=0;list.GetNext(pos)){
		sf=(CSearchFile*)list.GetAt(pos);//->GetSearchID() == nSearchID ){
		if (!md4cmp(hash,sf->GetFileHash())) {
			theApp.downloadqueue->AddSearchToDownload(sf,cat);
			break;
		}
	}
}
