//
// C++ Implementation: entrezsearcher
//
// Description:
//
//
// Author: Thach Nguyen <thach.nguyen@rmit.edu.au>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include "entrezsearcher.h"
#include "searchmanager.h"
#include "bibfile.h"
#include "filters/bibprogs.h"
#include "bibentrytable.h"

#include <klocale.h>
#include <kconfig.h>
#include <kstandarddirs.h>

#include <qdom.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qfile.h>

namespace
{
	static const int ENTREZ_MAX_RETURNS_TOTAL = 10;
	static const char* ENTREZ_BASE_URL = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/";
	static const char* ENTREZ_SEARCH_CGI = "esearch.fcgi";
	static const char* ENTREZ_SUMMARY_CGI = "esummary.fcgi";
	static const char* ENTREZ_FETCH_CGI = "efetch.fcgi";
	static const char* ENTREZ_LINK_CGI = "elink.fcgi";
	static const char* ENTREZ_DEFAULT_DATABASE = "pubmed";
}


EntrezSearcher::EntrezSearcher ( QObject *parent, const char *name )
		: searcher ( parent, name ),m_step ( Begin ), m_started ( false )
{
}


EntrezSearcher::~EntrezSearcher()
{}
QString EntrezSearcher::defaultName()
{
	return i18n ( "Entrez Database" );
}

QString EntrezSearcher::source() const
{
	return m_name.isEmpty() ? defaultName() : m_name;
}

void EntrezSearcher::readConfig ( KConfig* config_, const QString& group_ )
{
	KConfigGroupSaver groupSaver ( config_, group_ );
	QString s = config_->readEntry ( "Database", QString::fromLatin1 ( "pubmed" ) ); // default to pubmed
	if ( !s.isEmpty() )
	{
		m_dbname = s;
	}

	s = config_->readEntry ( "Name", defaultName() ); // default to pubmed
	if ( !s.isEmpty() )
	{
		m_name = s;
	}
}


void EntrezSearcher::saveConfig ( KConfig* config )
{
	config->writeEntry ( "Database", m_dbname );
}

void EntrezSearcher::search ( SearchKey key1, SearchKey key2, SearchKey key3 , const QString& value1, const QString& value2, const QString& value3, int operator1, int operator2 )
{
	QString str;
	QString query = QString();
	if ( !value1.isEmpty() )
	{

		str = QChar ( '"' ) + value1 + QChar ( '"' );
		switch ( key1 )
		{
			case Title:
				query = str + QString::fromLatin1 ( "[titl]" );
				break;

			case Author:
				query = str + QString::fromLatin1 ( "[auth]" );
				break;

			case Keyword:
				query = str + QString::fromLatin1 ( "[MH]" );
				break;

			case All:
				query = str + QString::fromLatin1 ( "[ALL]" );
				break;

			case Journal:
				query = str + QString::fromLatin1 ( "[TA]" );
				break;

			case Year:
				query = str + QString::fromLatin1 ( "[PDAT]" );
				break;
			default:
				stop();
				return;
		}
	}

	if ( !value2.isEmpty() )
	{
		if ( !query.isEmpty() )
		{
			switch ( operator1 )
			{
				case 0:
					query += QString::fromLatin1 ( " AND " );
					break;
				case 1:
					query += QString::fromLatin1 ( " OR " );
					break;
				case 2:
					query += QString::fromLatin1 ( " NOT " );
					break;
				default:
					stop();
					return;
			}

		}
		str = QChar ( '"' ) + value2 + QChar ( '"' );
		switch ( key2 )
		{
			case Title:
				query += str + QString::fromLatin1 ( "[titl]" );
				break;

			case Author:
				query += str + QString::fromLatin1 ( "[auth]" );
				break;

			case Keyword:
				query += str + QString::fromLatin1 ( "[MH]" );
				break;

			case All:
				query += str + QString::fromLatin1 ( "[ALL]" );
				break;

			case Journal:
				query += str + QString::fromLatin1 ( "[TA]" );
				break;

			case Year:
				query += str + QString::fromLatin1 ( "[PDAT]" );
				break;
			default:
				stop();
				return;
		}

	}

	if ( !value3.isEmpty() )
	{
		if ( !query.isEmpty() )
		{
			switch ( operator2 )
			{
				case 0:
					query += QString::fromLatin1 ( " AND " );
					break;
				case 1:
					query += QString::fromLatin1 ( " OR " );
					break;
				case 2:
					query += QString::fromLatin1 ( " NOT " );
					break;
				default:
					stop();
					return;
			}

		}
		str = QChar ( '"' ) + value3 + QChar ( '"' );
		switch ( key3 )
		{
			case Title:
				query += str + QString::fromLatin1 ( "[titl]" );
				break;

			case Author:
				query += str + QString::fromLatin1 ( "[auth]" );
				break;

			case Keyword:
				query += str + QString::fromLatin1 ( "[MH]" );
				break;

			case All:
				query += str + QString::fromLatin1 ( "[ALL]" );
				break;

			case Journal:
				query += str + QString::fromLatin1 ( "[TA]" );
				break;

			case Year:
				query += str + QString::fromLatin1 ( "[PDAT]" );
				break;
			default:
				stop();
				return;
		}

	}
	search(query);

}

void EntrezSearcher::search ( QString query )
{
	std::cerr << "Query string = " << query << "\n";
	if ( m_dbname.isEmpty() )
	{
		m_dbname = QString::fromLatin1 ( ENTREZ_DEFAULT_DATABASE );
	}

	m_started = true;
	m_data.truncate ( 0 );
	m_url = KURL ( QString::fromLatin1 ( ENTREZ_BASE_URL ) );
	m_url.addPath ( QString::fromLatin1 ( ENTREZ_SEARCH_CGI ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "tool" ),       QString::fromLatin1 ( "KBib" ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "retmode" ),    QString::fromLatin1 ( "xml" ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "usehistory" ), QString::fromLatin1 ( "y" ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "retmax" ),     QString::fromLatin1 ( "1" ) ); // we're just getting the count
	m_url.addQueryItem ( QString::fromLatin1 ( "db" ),         m_dbname );


	if ( query.isEmpty() )
	{
		stop();
		return;
	}


	m_url.addQueryItem ( QString::fromLatin1 ( "term" ), query );


	m_step = Search;
	std::cerr << m_url.prettyURL() << "\n";
	m_job = KIO::get ( m_url, false, false );
	connect ( m_job, SIGNAL ( data ( KIO::Job*, const QByteArray& ) ),
	          SLOT ( slotData ( KIO::Job*, const QByteArray& ) ) );
	connect ( m_job, SIGNAL ( result ( KIO::Job* ) ),
	          SLOT ( slotComplete ( KIO::Job* ) ) );

}

void EntrezSearcher::stop()
{
	if ( !m_started )
	{
		return;
	}
	if ( m_job )
	{
		m_job->kill();
		m_job = 0;
	}
	m_started = false;
	m_data.truncate ( 0 );
	m_step = Begin;
	emit signalDone ( this );
}

void EntrezSearcher::slotData ( KIO::Job*, const QByteArray& data_ )
{
	QDataStream stream ( m_data, IO_WriteOnly | IO_Append );
	stream.writeRawBytes ( data_.data(), data_.size() );
}

void EntrezSearcher::slotComplete ( KIO::Job* job_ )
{
	// since the fetch is done, don't worry about holding the job pointer
	m_job = 0;

	if ( job_->error() )
	{
		emit signalMessage ( job_->errorString(), 0 );
		stop();
		return;
	}

	if ( m_data.isEmpty() )
	{
		std::cerr << "EntrezSearcher::slotComplete() - no data\n";
		stop();
		return;
	}

	switch ( m_step )
	{
		case Search:
			searchResults();
			break;
		case Fetch:
			fetchResults();
			break;
		default:
			std::cerr << "EntrezSearcher::slotComplete() - wrong step = " << m_step << "\n";
			break;
	}
}

void EntrezSearcher::searchResults()
{
	QDomDocument dom;
	if ( !dom.setContent ( m_data, false ) )
	{
		std::cerr << "EntrezFetcher::searchResults() - server did not return valid XML.\n";
		stop();
		return;
	}
	// find Count, QueryKey, and WebEnv elements
	int count = 0;
	for ( QDomNode n = dom.documentElement().firstChild(); !n.isNull(); n = n.nextSibling() )
	{
		QDomElement e = n.toElement();
		if ( e.isNull() )
		{
			continue;
		}
		if ( e.tagName() == QString::fromLatin1 ( "Count" ) )
		{
			m_total = e.text().toInt();
			++count;
		}
		else if ( e.tagName() == QString::fromLatin1 ( "QueryKey" ) )
		{
			m_queryKey = e.text();
			++count;
		}
		else if ( e.tagName() == QString::fromLatin1 ( "WebEnv" ) )
		{
			m_webEnv = e.text();
			++count;
		}
		if ( count >= 3 )
		{
			break; // found them all
		}
	}
	m_waitingRetrieveRange = true;
	m_step = Wait;
	if ( m_total > 0 )
		emit signalQueryResult ( m_total );
	else
	{
		signalMessage ( i18n ( "No reference was found" ), 1 );
		stop();
	}
}


void EntrezSearcher::retrieveRange ( unsigned int min, unsigned int max )
{
	if ( m_step != Wait )
		return;
	m_waitingRetrieveRange = false;
	if ( min < 1 && max < 1 )
	{
		stop();
		return;
	}
	m_url = KURL ( QString::fromLatin1 ( ENTREZ_BASE_URL ) );
	m_url.addPath ( QString::fromLatin1 ( ENTREZ_FETCH_CGI ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "tool" ),       QString::fromLatin1 ( "KBib" ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "retmode" ),    QString::fromLatin1 ( "xml" ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "retstart" ),   QString::number ( min-1 ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "retmax" ),     QString::number ( max-min+1 ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "usehistory" ), QString::fromLatin1 ( "y" ) );
	m_url.addQueryItem ( QString::fromLatin1 ( "db" ),         m_dbname );
	m_url.addQueryItem ( QString::fromLatin1 ( "query_key" ),  m_queryKey );
	m_url.addQueryItem ( QString::fromLatin1 ( "WebEnv" ),     m_webEnv );

	m_data.truncate ( 0 );
	m_step = Fetch;
//  myDebug() << m_url.prettyURL() << endl;
	m_job = KIO::get ( m_url, false, false );
	connect ( m_job, SIGNAL ( data ( KIO::Job*, const QByteArray& ) ),
	          SLOT ( slotData ( KIO::Job*, const QByteArray& ) ) );
	connect ( m_job, SIGNAL ( result ( KIO::Job* ) ),
	          SLOT ( slotComplete ( KIO::Job* ) ) );

}


void EntrezSearcher::fetchResults()
{
	QFile f ( QString::fromLatin1 ( "/tmp/kbib-entrez-pubmed.xml" ) );
	if ( f.open ( IO_WriteOnly ) )
	{
		QTextStream t ( &f );
//		t.setEncoding(QTextStream::UnicodeUTF8);
//		t << QString(m_data);
		t << QString::fromUtf8 ( m_data, m_data.size() );
	}
	f.close();

	//Convert to bibtex
	med2xml ( "/tmp/kbib-entrez-pubmed.xml", "/tmp/kbib-entrez-mods.xml" );
	xml2bib ( "/tmp/kbib-entrez-mods.xml", "/tmp/kbib-entrez-mods.bib" );
	//Read bibfile
	BibentryTable *entryList = new BibentryTable();
	entryList->readBibfile ( "/tmp/kbib-entrez-mods.bib", false );

	for ( int i = 0; i < entryList->size(); i++ )
	{
		BibEntry *bib = entryList->get_entry ( i );
		if ( bib )
			emit signalResultFound ( new BibEntry ( *bib ) );
	}
	delete entryList;
	stop();
}


void EntrezSearcher::setSource ( const QString s )
{
	m_name = s ;
}


QStringList EntrezSearcher::searchKey()
{
	QStringList keyList;
	keyList << searchManager::self()->searchKeyString ( All ) << searchManager::self()->searchKeyString ( Author )
	<< searchManager::self()->searchKeyString ( Title ) << searchManager::self()->searchKeyString ( Journal )
	<< searchManager::self()->searchKeyString ( Keyword ) << searchManager::self()->searchKeyString ( Year );
	return keyList;
}

SearcherConfigWidget* EntrezSearcher::configWidget ( QWidget* parent_ )
{
	return new EntrezConfigWidget ( parent_, this );
}

EntrezConfigWidget::EntrezConfigWidget ( QWidget* parent_, EntrezSearcher* searcher_/*=0*/ )
		: SearcherConfigWidget ( parent_ )
{
	m_searcher = searcher_;
	QVBoxLayout* l = new QVBoxLayout ( optionsWidget() );
	l->addWidget ( new QLabel ( i18n ( "This source has no options." ), optionsWidget() ) );
	KURLLabel *urlLab = new KURLLabel( optionsWidget() );
	l->addWidget(urlLab);
	urlLab->setText( "More information about Pubmed");
        urlLab->setURL("http://www.ncbi.nlm.nih.gov/sites/entrez?db=PubMed");
    
	connect(urlLab , SIGNAL( leftClickedURL( const QString& ) ), kapp, SLOT( invokeBrowser( const QString& ) ) );
	l->addStretch();
}




#include "entrezsearcher.moc"
