/*
This Work is under GPL provided with this work.

This work is based on the java implementation of the Kademlia protocol.
Kademlia: Peer-to-peer routing based on the XOR metric
Copyright (C) 2002  Petar Maymounkov [petar@post.harvard.edu]
http://kademlia.scs.cs.nyu.edu
and
This work is bassed on translation from Barry Dunne to C++
Copyright (C)2003 Barry Dunne (http://www.emule-project.net)
*/
#include "cygwin.h"
#include "cSearchManager.h"
#include "sTag.h"
#include "opcodes.h"
#include "misc.h"

extern time_t currentTime;
SearchMap cSearchManager::m_searches;
size_t	  cSearchManager::m_nextID = 0;
void   cSearchManager::stopSearch(size_t searchID) {{{
	SearchMap::iterator it  = m_searches.begin(); 
	SearchMap::iterator end = m_searches.end();
	while (it != end) {
		SearchMap::iterator next = it;
		next++;
		if (it->second->m_searchID == searchID) {
			delete it->second;
			m_searches.erase(it);
			end = m_searches.end();
			}
		it = next;
		}
	}}}
void   cSearchManager::stopAllSearches(void) {{{
	SearchMap::iterator it;
	for (it = m_searches.begin(); it != m_searches.end(); it++)
		delete it->second;
	m_searches.clear();
	}}}
void   cSearchManager::jumpStart(void) {{{
	SearchMap::iterator it = m_searches.begin();
	SearchMap::iterator end = m_searches.end();
	while (it != end) {
		SearchMap::iterator next = it;
		next++;
		if (it->second->m_created + 120 < currentTime) {
			delete it->second;
			m_searches.erase(it);
			end = m_searches.end();
			}
		else	it->second->jumpStart();
		it = next;
		}
	}}}
void   cSearchManager::processResponse(const uint8_t *target, uint32_t fromIP, uint16_t fromPort, cPeerList *results) {{{
	class cSearch *s = NULL;
	SearchMap::const_iterator it = m_searches.find(target);
	if (it != m_searches.end()) s = it->second;
	if (s == NULL) {
		cPeerList::const_iterator it2;
		for (it2 = results->begin(); it2 != results->end(); it2++) delete (*it2);
//		delete results;
		return;
		}
	s->processResponse(target, fromIP, fromPort, results);
	}}}

bool   cSearchManager::alreadySearchingFor(const uint8_t *id) {{{
	 cHash ID(id);
	return (m_searches.count(ID) > 0);
	}}}
bool   cSearchManager::isKeywordSearch    (const uint8_t *id) {{{
  	cHash ID(id);
	SearchMap::const_iterator it = m_searches.find(ID);
	class cSearch *s = NULL;
	if (it != m_searches.end()) s = it->second;
	if (s != NULL) return (s->m_type == cSearch::KEYWORD);
	return false;
	}}}

size_t cSearchManager::findFile(const uint8_t *id) {{{
	if (alreadySearchingFor(id)) return 0;
	size_t searchID = 0;
	class cSearch *s = new cSearch;
	s->m_searchID	= searchID = ++m_nextID;
	s->m_type	= cSearch::FILE;
	memcpy(s->m_target, id, HASH_LEN);
	size_t len, LEN = len = 4096;
	unsigned char *buf = s->m_searchTerms = reinterpret_cast<unsigned char*>(MALLOC(LEN));
	ADD_U1   (&buf, &len, OP_EDONKEYHEADER);
	ADD_U1   (&buf, &len, OVERNET_SEARCH_REQUEST);
	ADD_HASH (&buf, &len, s->m_target);
	ADD_U1	 (&buf, &len, 0);
	ADD_U2	 (&buf, &len, 0);
	ADD_U2	 (&buf, &len, 100);
	s->m_lenSearchTerms = LEN - len;
	cHash ID(id);
	m_searches[ID] = s;
	s->go();
	return searchID;
	}}}
void   cSearchManager::getWords(const char *str, cWordList *words) {{{
	if (str   == NULL) return;
	if (words == NULL) return;
	char *tmp = STRDUP (str);
	char *s = tmp;
	while (strlen(s) > 0) {
		size_t len = strcspn(s, " ()[]{}<>,._-!?");
		if (len > 2) {
			char b = s[len];
			s[len] = 0;
			char *word = STRDUP (s);
			s[len] = b;
			for (size_t i=0; i<len; i++) word[i]=tolower(word[i]);
			words->push_back(word);
			} else 
		if ((len == 0) && (s[0] == '-')) {
			len = strcspn(s, " ");
			if 	(   (!strncasecmp(s, "-image", len))
				||  (!strncasecmp(s, "-audio", len))
				||  (!strncasecmp(s, "-video", len))
				||  (!strncasecmp(s, "-doc"  , len))
				||  (!strncasecmp(s, "-pro"  , len))) {
				char b = s[len];
				s[len] = 0;
				char *word = STRDUP (s);
				s[len] = b;
				for (size_t i=0; i<len; i++) word[i]=tolower(word[i]);
				words->push_back(word);
				}
			else len = 0;
			}
		if (len < strlen(s)) len++;
		s += len;
		}
	}}}
size_t cSearchManager::findKeywords(const char *text) {{{
	size_t searchID = 0;
	cSearch *s = new cSearch;
	s->m_type = cSearch::KEYWORD;
	getWords(text, &s->m_words);
	size_t word_count = s->m_words.size();
	if (word_count == 0) {{{
		delete s; 
		return 0; 
		}}}
	char *k = s->m_words.front();
	memcpy(s->m_target, md4(k, strlen(k)), HASH_LEN);
	printf("Keyword search %s\n", hash_bin2hex(s->m_target));
	if (alreadySearchingFor(s->m_target)) {{{
		delete s; 
		return 0; 
		}}}
	if (word_count == 1) {{{
		size_t len3, LEN3 = len3 = s->m_lenSearchTerms = 23;
		unsigned char *buf3 =  s->m_searchTerms = reinterpret_cast<unsigned char*>(MALLOC (LEN3));
		ADD_U1   (&buf3, &len3, OP_EDONKEYHEADER);		// 1
		ADD_U1   (&buf3, &len3, OVERNET_SEARCH_REQUEST);	// 1
		ADD_HASH (&buf3, &len3, s->m_target);			// HASH_LEN
		ADD_U1 (&buf3, &len3,0);				// 1
		ADD_U2 (&buf3, &len3 ,  0);				// 2
		ADD_U2 (&buf3, &len3 ,100);				// 2
		ASSERT (len3 == 0);
		m_searches[s->m_target] = s;
		searchID      = ++m_nextID;
		s->m_searchID = searchID;
		s->go();
		return searchID;
		}}}
	// Build keyword message (skip first keyword)
	size_t count = s->m_words.size() - 1;
	size_t len2, LEN2 = len2 = 4096;
	unsigned char *buf2 =  s->m_searchTerms = reinterpret_cast<unsigned char*>(MALLOC (4096));
	ADD_U1   (&buf2, &len2, OP_EDONKEYHEADER);		// 1
	ADD_U1   (&buf2, &len2, OVERNET_SEARCH_REQUEST);	// 1
	ADD_HASH (&buf2, &len2, s->m_target);			// HASH_LEN
	ADD_U1 (&buf2, &len2 ,1);				// 1
	if (count > 0) {{{ // count * 2
		for (size_t i=0; i<(count - 1); i++) {
			ADD_U1 (&buf2, &len2 ,0); // BRACKET
			ADD_U1 (&buf2, &len2 ,0); // AND
			}
		}}}
	cWordList::const_iterator it = s->m_words.begin();
	for (it++; it != s->m_words.end(); it++) {{{
		printf("WORD %s\n", *it);
		if (0 == strcmp(*it, "-image")) { add_strTag(&buf2, &len2, SEEK_IMAGE, "\x03"); count++; } else  
		if (0 == strcmp(*it, "-audio")) { add_strTag(&buf2, &len2, SEEK_AUDIO, "\x03"); count++; } else  
		if (0 == strcmp(*it, "-video")) { add_strTag(&buf2, &len2, SEEK_VIDEO, "\x03"); count++; } else  
		if (0 == strcmp(*it, "-doc"  )) { add_strTag(&buf2, &len2, SEEK_DOC  , "\x03"); count++; } else  
		if (0 == strcmp(*it, "-prc"  )) { add_strTag(&buf2, &len2, SEEK_PRO  , "\x03"); count++; } else  
						{ add_strCmp(&buf2, &len2, *it		     ); count++; }
		}}}
	ADD_U2 (&buf2, &len2 ,  0);				// 2
	ADD_U2 (&buf2, &len2 ,100);				// 2
	s->m_lenSearchTerms = LEN2 - len2;
	m_searches[s->m_target] = s;
	searchID      = ++m_nextID;
	s->m_searchID = searchID;
	s->go();
	return searchID;
	}}}
void   cSearchManager::findNode(const uint8_t *id) {{{
	if (alreadySearchingFor(id)) return;
	class cSearch *s = new cSearch;
	s->m_type = cSearch::NODE;
	memcpy(s->m_target, id, HASH_LEN);
	s->m_searchTerms = NULL;
	s->m_lenSearchTerms = 0; 
	s->m_searchID = 0;
	m_searches[s->m_target] = s;
	s->go();
	}}}


