/***************************************************************************
 *   Copyright (C) 2005 by xavier helluy   *
 *   xavier.helluy@web.de   *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/
#include "mythread2.h"
#include <dcopobject.h>
#include <dcopclient.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qapplication.h>
#include <kdebug.h>

//highlight
// #include "..\contributions\highlighter\QueryTermExtractor.h"
// #include "..\contributions\highlighter\QueryScorer.h"
// #include "..\contributions\highlighter\Highlighter.h"
// #include "..\contributions\highlighter\SimpleHTMLFormatter.h"



MyThread2::MyThread2(	QObject * o , const QString & query , QString  socketPath  ,
 			QDict <ClientList> * QueryList  ,
			QDict <lucene::search::IndexSearcher>  indexSingleSearchersList,
			QDict <QStringList   >  indexMultiSearchersList,
			QStringList indexesPathList,
			const bool & peer2peer) 
	:   
	    	    DCOPObject((QCString) query), 
		    CLuceneSearchThreadDCOPIface(),
		    QThread(),
// 	    m_queryList(QueryList),
	    m_indexesPathList(indexesPathList),
	    m_receiver(o),
	    m_query(query),
	    m_socketPath(socketPath),

	    m_indexSingleSearchersList(indexSingleSearchersList),
	    m_indexMultiSearchersList(indexMultiSearchersList),
	    m_byteResults(),
	    m_peer2peer(peer2peer)
			    
{
	kdDebug() << "MyThread2::MyThread2  - m_socketPath : " <<  m_socketPath << endl;
	
	m_queryList = QueryList;
}

MyThread2::~MyThread2()
{
    terminate();
    wait();

}

void MyThread2::run()
{
	
	//QString results_type could allow to return error messages
	//FIXME TODO tests that the list of indexes points to existing and valid indexes
	bool multiIndex = FALSE;
	if (m_indexesPathList.count()==1)
	{
		multiIndex = FALSE;
		runMultiIndexQuery(multiIndex);
	}
	else if (m_indexesPathList.count() >1)
	{
		kdDebug() << "MyThread2::run THREAD: MULTI-INDEX SEARCH STARTED" << endl;
		multiIndex = TRUE;
		runMultiIndexQuery(multiIndex);
	}
	else
	{
		kdDebug() << "MyThread2::run THREAD: EMPTY LIST OF INDEX" << endl;
		kdWarning() << "MyThread2::run NO INDEX PATH GIVEN" << endl;		
	}
	
	runTransfer( m_socketPath , m_byteResults );
	
	//deregister the client-socket associated to this query
	//QDict<ClientList>  QueryList[newQuery]->insert( SocketPath , new int(1)); // int value is egal
	mutex.lock();
	m_queryList->find(m_query)->remove(m_socketPath); 
	mutex.unlock();
	
	//process queued same queries	
	while (	!( m_queryList->find(m_query)->isEmpty() ) )
	{
		QDictIterator<int>  ClientIt( *(m_queryList->find(m_query)) ); // init at begin
		kdDebug() << "MyThread2::run Processing Queued Query" << ClientIt.currentKey() << endl;

		runTransfer( ClientIt.currentKey()  , m_byteResults );
		
		mutex.lock();
		m_queryList->find(m_query)->remove( ClientIt.currentKey() ); //this will increment the iterator
		mutex.unlock();
	}
	
	//sends finished customEvent telling that all queries m_query have been processed
	//the cleaning will be done later when the event will be received
	QCustomEvent *event = new QCustomEvent(12346); //number is random !!! > XXX
	event->setData(new QString(m_query));
	QApplication::postEvent(m_receiver, event);
// 	terminate();
// 	wait();
}

void MyThread2::runMultiIndexQuery(bool multiIndex)
{

	// QueryParser : NOT THREAD-SAFE !! so init here and not in main
	/*
	WARNING: the analyser used for searching indexes should be the same as the one 
	used to build the database !
	*/
	lucene::analysis::standard::StandardAnalyzer  analyzer;  //FIXME: is it THREAD-SAFE ???
	const TCHAR * input = (TCHAR*) m_query.ucs2();//FIXME:same UNICODE problem
	
	const TCHAR* buf;
	
	lucene::search::Query * q = lucene::queryParser::QueryParser::parse(
	                                                     input , _T("contents"), &analyzer);
	buf = q->toString(_T("contents"));
	
	kdDebug() << "MyThread2::runMultiIndexQuery" << buf << endl;
	delete[] buf;
	
	lucene::search::Hits * h;
	lucene::search::MultiSearcher * s = 0;
	if (multiIndex == FALSE)
	{
		//start a single index search
		h = m_indexSingleSearchersList[*(m_indexesPathList.begin())]->search(q);
	}
	else
	{
		//start a multiIndex search
		// ---- build the array of IndexSearchers
		// FIXME:this should be done in MAIN , not HERE
		//note: knapsack code creates a nice class Searchables with exceptions
		std::list <  lucene::search::Searchable* > myarray;
	        
		int count=0;
		for ( QStringList::Iterator it = m_indexesPathList.begin(); it != m_indexesPathList.end(); ++it )
		{
			kdDebug() << "MyThread2::runMultiIndexQuery ADDING INDEX" << *it <<  endl;
			lucene::search::Searchable * ss = 0;
			ss = new lucene::search::IndexSearcher(*it);
			if (ss)
         		{
            			myarray.push_back(ss);
        		}
// 			myarray[count] = m_indexSingleSearchersList.find(*it);
			count++;
		}
		   // -----------code update adapted from knapsack lucenesearcher.cpp from svn
		   // those code is under GPL licence (13 november 2005)
		   //construct a multisearcher
    int sz = myarray.size();
    if (sz > 0)
    {
        lucene::search::Searchable **searchers = new lucene::search::Searchable *[sz + 1];
        std::list < lucene::search::Searchable * >::const_iterator i;
        int p = 0;
        	for (i = myarray.begin(); i != myarray.end(); ++i)
        	{
     	            searchers[p++] = *i;
       		}
       		searchers[p] = 0;
       		s = new lucene::search::MultiSearcher(searchers);
		} 
		else
		{
      			s = 0;
		}
		   // code update from knapsack lucenesearcher.cpp
		   		//FIXME: search limit
		h =  s->search(q) ;
 		
 		// FIXME: LEAK  ??
//  		delete[] myarray;
	}
	//------------------end of knapsack code copy
	//encode results
	if ( h->length() > 0 );
	{
		encodeHits( h );
	}
	
	//cleaning
	kdDebug() << "MyThread2::runMultiIndexQuery NOT DELETING hits" <<  endl;
// 	delete h;
	
	delete &q;
	if ( s != 0 )
	{
		delete s;	
		s = 0;
	}
}

//transferes binary data, whatever they are to the server
//i.e the client waiting for a response to its query
void MyThread2::runTransfer(QString socketPath , QByteArray &byteData)
{
	kdDebug() << "MyThread2::runTransfer to socket: " << socketPath << endl;

	//for the transfer, the search-daemon is a client and the query-client is the server here
	m_hitsSocket = new Client ( (QCString) socketPath); //FIXME:
	int ok = m_hitsSocket->init();
	if (! (ok > 0) )
	{
		kdDebug() << "MyThread2::runTransfer init client finished: BAD CONNECTION"<< endl;
		delete m_hitsSocket;
		return;
	}
	
	kdDebug() << "MyThread2::runTransfer init client finished: "<< endl;
	
	//FIXME:is it usefull to compress data ?
	QByteArray qByteData = qCompress( byteData );
	byteData.resize(0);
	m_hitsSocket->BigRawDataServerToCLient( qByteData , 1000);

	//tell server, that sending is finished
	m_hitsSocket->sendSignal((QString) "The End");
	
	//FIXME: blocking wait until the server responds correctly
	m_hitsSocket->getSignal((QString) "The End");
	
	kdDebug() << "MyThread2::runTransfer finished: "<< endl;

	delete m_hitsSocket;
}

void MyThread2::encodeHits( lucene::search::Hits * h )
{
	QString results;
	QStringList resultsList;
	QStringList resultsMimeType;
	QStringList resultsModTime;
	QStringList resultsMode;
	QStringList resultsSize;
	QStringList resultsStoredPreview;
// 	QStringList resultsItem;
	kdDebug() << "MyThread2::runMultiIndexQuery There are  " << h->length() << " results " << endl;

	for ( int32_t i=0;i < h->length();i++)
	{
		kdDebug() << "MyThread2::runMultiIndexQuery  i=" << i  << endl;

		lucene::document::Document& doc = (*h).doc(i);
// 		std::stringstream stream;
		resultsList.append    ( QString::fromUcs2( (const unsigned short *) doc.get(_T("URLKey"       ) ) ));
		resultsMimeType.append( QString::fromUcs2((const unsigned short *)doc.get(_T("mimetype"     ) ) ));	
		resultsModTime.append ( QString::fromUcs2( (const unsigned short *)doc.get(_T("ModTimeIntKey") ) ));
		resultsMode.append    ( QString::fromUcs2((const unsigned short *)doc.get(_T("Mode_T"       ) )) );
		resultsSize.append    ( QString::fromUcs2((const unsigned short *)doc.get(_T("FileSize"     ) ) ));
		//FIXME:someday get read of this StoredPreview field. Add indexLocation to URL ???
		resultsStoredPreview.append(QString::fromUcs2((const unsigned short *)doc.get(_T("StoredPreview" ) ) ));
// 		resultsItem.append    ( doc.get(_T("KFileItem"     ) ) );

		kdDebug() << "MyThread2::runMultiIndexQuery  Res[" << i << "] = " << doc.get(_T("URLKey")) << endl;
	}
	
	QDataStream in(m_byteResults, IO_WriteOnly);
	in << (QStringList) resultsList;
	kdDebug() << "MyThread2::runMultiIndexQuery resultsMimeType " << resultsMimeType.count()<< endl;
	in << (QStringList) resultsMimeType;
	kdDebug() << "MyThread2::runMultiIndexQuery  resultsModTime" << resultsModTime.count()<< endl;
	in << (QStringList) resultsModTime;
	kdDebug() << "MyThread2::runMultiIndexQuery  resultsMode" << resultsMode.count()<< endl;
	in << (QStringList) resultsMode;
	kdDebug() << "MyThread2::runMultiIndexQuery  resultsSize" << resultsSize.count()<< endl;
	in << (QStringList) resultsSize;
	kdDebug() << "MyThread2::runMultiIndexQuery  resultsStoredPreview" << resultsStoredPreview.count()<< endl;
	in << (QStringList) resultsStoredPreview;
	kdDebug() << "MyThread2::runMultiIndexQuery  resultsStoredPreview" << resultsStoredPreview<< endl;
	
// 	in << (QStringList) resultsItem;

}

//dcop interface. Not used so far
bool MyThread2::isRunning() const
{
	return this->running();
}

//dcop interface. Not used so far
bool MyThread2::isFinished() const
{
	return this->finished();
}


// MyThread2::doHighLigthing()
// {
// 	const int FRAGMENT_SIZE = 400;
// 	SimpleAnalyzer analyzer;
// 
// 	Highlighter highlighter( new SimpleHTMLFormatter(_T("<b>"), _T("</b>")),
// 				 new QueryScorer(query.get()), 
// 				 new SimpleFragmenter(FRAGMENT_SIZE));
// 	//need afieldname and a reader
// 	TokenStream& ts = analyzer.tokenStream(_T("contents"), &r);
// 	
// 	TCHAR * highlighted_text = highlighter.getBestFragments(&ts, test_texts[j], 2, _T("..."));
// 	
// 	
// }




