//
// C++ Implementation: mathscinetsearcher
//
// Description: 
//
//
// Author: Thach Nguyen <thach.nguyen@rmit.edu.au>, (C) 2008
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "mathscinetsearcher.h"
#include "searchmanager.h"
#include "bibentrytable.h"

#include <klocale.h>
#include <kio/job.h>
#include <kstandarddirs.h>
#include <kconfig.h>
#include <kcombobox.h>
#include <klineedit.h>
#include <kaccelmanager.h>
#include <knuminput.h>
#include <kstandarddirs.h>
#include <ktempfile.h>

#include <qdom.h>
#include <qregexp.h>
#include <qlabel.h>
#include <qfile.h>
#include <qlayout.h>
#include <qwhatsthis.h>
#include <qurl.h>

#include <iostream>

namespace {
  static const char* MATHSCINET_BASE_URL = "http://www.ams.org";
  static const char* MATHSCINET_SEARCH_CGI = "mathscinet/search/publications.html";
 
}


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


MathSciNetSearcher::~MathSciNetSearcher()
{
}
QString MathSciNetSearcher::defaultName() {
	return i18n("MathSciNet");
}

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

void MathSciNetSearcher::readConfig(KConfig* config_, const QString& group_) {

}


void MathSciNetSearcher::saveConfig(KConfig* config){
}


QString MathSciNetSearcher::searchField(int order, SearchKey key, QString data){
	QString field;
	QString str = data;
	str.simplifyWhiteSpace();
	field = QString::fromLatin1("pg%1=").arg(order+3);
	switch(key)
    {
		case All:
			field += QString::fromLatin1("ALLF&");
			break;
		case Author:
			field += QString::fromLatin1("AUCN&");
			break;
		case Title:
			field += QString::fromLatin1("TI&");
			break;
		case Journal:
			field += QString::fromLatin1("JOUR&");
			break;
		default:
            stop();
            return 0;
	}
	field+=QString::fromLatin1("s%1=").arg(order+3);
	field += str;
	return field;
}


QString MathSciNetSearcher::boolOperator(int order, int op){
	QString str;
	str = QString::fromLatin1("co%1=").arg(order+3);
	switch(op){
		case 0:
			str += QString::fromLatin1("AND");
			break;
		case 1:
			str += QString::fromLatin1("OR");
			break;
		case 2:
			str += QString::fromLatin1("NOT");
			break;
		default:
			stop();
			break;
	}
	return str;

}

void MathSciNetSearcher::search(SearchKey key1, SearchKey key2, SearchKey key3 , const QString& value1, const QString& value2, const QString& value3, int operator1, int operator2) {
 
    QString queryString;
	queryString = searchField(1, key1, value1) + QString::fromLatin1("&") + boolOperator(1, operator1) + QString::fromLatin1("&") + searchField(2, key2, value2) + QString::fromLatin1("&") + boolOperator(2, operator2) + QString::fromLatin1("&") + searchField(3, key3, value3);

	search(queryString);


}

void MathSciNetSearcher::search(QString queryString){
    m_started = true;

    m_data.truncate(0);

    m_url = KURL(MATHSCINET_BASE_URL);
    m_url.addPath(QString::fromLatin1(MATHSCINET_SEARCH_CGI));

    QString str;
    m_query = queryString;


    if (m_query.isEmpty())
    {
        stop();
        return;
    }
    
    m_step = Search;
	
    m_query = queryString;

    httpRequest(m_query);
		
}

void MathSciNetSearcher::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 MathSciNetSearcher::slotData(KIO::Job*, const QByteArray& data_) {
	QDataStream stream(m_data, IO_WriteOnly | IO_Append);
	stream.writeRawBytes(data_.data(), data_.size());
}

void MathSciNetSearcher::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 << "MathSciNetSearcher::slotComplete() - no data\n";
		stop();
		return;
	}

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

void MathSciNetSearcher::searchResults(){
	


	QString str = QString::fromUtf8(m_data, m_data.size());
	
	QRegExp rx ( QString::fromLatin1 ( ".*<strong>Matches:</strong> (\\d+).*" ) );

	if ( !rx.exactMatch ( str ) )
	{
		signalMessage ( i18n ( "No matching records were found for your search." ), 1 );
		stop();
	}
	str = rx.cap ( 1 );
	m_total = str.toInt();

	m_waitingRetrieveRange = true;
	m_step = Wait;
	if (m_total > 0)
		emit signalQueryResult(m_total);	
	else{
		signalMessage(i18n("No reference was found"), 1);
		stop();
	}

	
}


void MathSciNetSearcher::retrieveRange(unsigned int min, unsigned int max){
	if (m_step != Wait)
		return;
	m_waitingRetrieveRange = false;
    if ((min < 1 && max < 1) || max < min)
    {
        stop();
        return;
    }
    m_start = min;
    m_end = max;
    m_total = max-min+1;
 
    retrieved = 0;


    QString postData = m_query + QString("&r=%1").arg(min);
    
    httpRequest(postData);



    m_step = Fetch;
	

}

void MathSciNetSearcher::httpRequest(QString data){
    m_url = KURL(QString(MATHSCINET_BASE_URL)+"/"+QString(MATHSCINET_SEARCH_CGI)+"?"+data);

    m_job = KIO::get( m_url, false, false );

    m_data.truncate(0);

    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*)));
	
std::cerr << m_url.url() << "\n";
}

void MathSciNetSearcher::fetchResults(){
	QString str = QString::fromUtf8(m_data, m_data.size());

	int start = 0;
	int stop;

	QString id;
	QString postData;
	start = str.find("<input type=\"checkbox\" name=\"b\" value=\"", 0);

	while (retrieved < m_total && start >= 0){	
		str = str.mid(start);
		stop = str.find("\" /></div>", 0);
		id = str.mid(39, stop-39);
		retrieved++;	
		start = str.find("<input type=\"checkbox\" name=\"b\" value=\"", stop);
		postData += QString("b=") + id + QString("&");
	}
	postData += QString("fmt=bibtex");
	
	httpRequest(postData);
	m_step = FetchBibtex;
	
	

}



void MathSciNetSearcher::fetchBibtex(){
	QString str = QString::fromUtf8(m_data, m_data.size());
	KTempFile tmpfile;
	tmpfile.setAutoDelete(true);
    	QString fn = tmpfile.name();
	QFile f ( fn );
	if ( f.open ( IO_WriteOnly ) )
	{
		QTextStream t ( &f );
		t << str;
	}
	f.close();
	//Read bibfile
	BibentryTable *entryList = new BibentryTable();
	entryList->readBibfile ( const_cast<char*>(fn.ascii()), false );

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

	if (retrieved >= m_total)
		stop();
	else{
		QString postData = m_query + QString("&offset=%1").arg(m_start+retrieved);
    		httpRequest(postData);
		m_step = Fetch;

	}
}



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


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


SearcherConfigWidget* MathSciNetSearcher::configWidget(QWidget* parent_)
{
    return new MathSciNetConfigWidget(parent_, this);
}


MathSciNetConfigWidget::MathSciNetConfigWidget(QWidget* parent_, MathSciNetSearcher* 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 MathSciNet");
        urlLab->setURL("http://www.ams.org/mathscinet/help/about.html");
    
	connect(urlLab , SIGNAL( leftClickedURL( const QString& ) ), kapp, SLOT( invokeBrowser( const QString& ) ) );
	l->addStretch();
}



//#include "mathscinetsearcher.moc"
