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

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

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

#include <iostream>

QMap<QString,QString> SpiresSearcher::mirror_map;

namespace {
  static const char* SPIRES_SEARCH_CGI = "www";
}


SpiresSearcher::SpiresSearcher(QObject *parent, const char *name)
	: searcher(parent, name),m_step(Begin), m_started(false)
{
	m_mirror = QString::fromLatin1("SLAC (US)");
	mirror_map.insert(QString::fromLatin1("DESY (Germany)"), QString::fromLatin1("http://www-library.desy.de/cgi-bin/spiface/find/hep"));
	mirror_map.insert(QString::fromLatin1("Fermilab (US)"), QString::fromLatin1("http://www-spires.fnal.gov/spires/find/hep"));
	mirror_map.insert(QString::fromLatin1("IHEP (Russia)"), QString::fromLatin1("http://usparc.ihep.su/spires/find/hep"));
	mirror_map.insert(QString::fromLatin1("Durham U. (UK)"), QString::fromLatin1("http://www-spires.dur.ac.uk/cgi-bin/spiface/hep"));
	mirror_map.insert(QString::fromLatin1("SLAC (US)"), QString::fromLatin1("http://www.slac.stanford.edu/spires/find/hep"));
	mirror_map.insert(QString::fromLatin1("YITP (Japan)"), QString::fromLatin1("http://www.yukawa.kyoto-u.ac.jp/cgi-bin/spiface/find/hep"));
	mirror_map.insert(QString::fromLatin1("LIPI (Indonesia)"), QString::fromLatin1("http://www.spires.lipi.go.id/spires/find/hep"));
}


SpiresSearcher::~SpiresSearcher()
{
}
QString SpiresSearcher::defaultName() {
	return i18n("Spires");
}

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

void SpiresSearcher::readConfig(KConfig* config_, const QString& group_) {
    KConfigGroupSaver groupSaver(config_, group_);
    m_mirror = config_->readEntry("Mirror", QString::fromLatin1("SLAC (US)"));
}


void SpiresSearcher::saveConfig(KConfig* config){
	config->writeEntry("Mirror", m_mirror);
}

void SpiresSearcher::search(SearchKey key1, SearchKey key2, SearchKey key3 , const QString& value1, const QString& value2, const QString& value3, int operator1, int operator2) {
 
    QString queryString;
    queryString = QString::fromLatin1("find");
    if (!value1.isEmpty())
    {
        QString str = value1;
	switch(key1)
        {
        case Title:
            queryString += QString::fromLatin1(" t ");
            break;

        case Author:
            queryString += QString::fromLatin1(" a ");
            break;

	case Journal:
            queryString += QString::fromLatin1(" j ");
            break;

        case Keyword:
            queryString += QString::fromLatin1(" k ");
            break;

        default:
            stop();
            return;
        }
	queryString += str;
    }


    if (!value2.isEmpty() )
    {
        QString str = value2;
	if (!queryString.isEmpty())
        {
            switch(operator1)
            {
            case 0:
                queryString += QString::fromLatin1(" and ");
                break;
            case 1:
                queryString += QString::fromLatin1(" or ");
                break;
            case 2:
                queryString += QString::fromLatin1(" not ");
                break;
            default:
                stop();
                return;
            }

        }

        switch(key2)
        {
	case Title:
            queryString += QString::fromLatin1(" t ");
            break;

        case Author:
            queryString += QString::fromLatin1(" a ");
            break;

	case Journal:
            queryString += QString::fromLatin1(" j ");
            break;

        case Keyword:
            queryString += QString::fromLatin1(" k ");
            break;

        default:
            stop();
            return;
        }
	queryString += str;
    }

    if (!value3.isEmpty() )
    {
        QString str = value3;
	if (!queryString.isEmpty())
        {
            switch(operator2)
            {
            case 0:
                queryString += QString::fromLatin1(" and ");
                break;
            case 1:
                queryString += QString::fromLatin1(" or ");
                break;
            case 2:
                queryString += QString::fromLatin1(" not ");
                break;
            default:
                stop();
                return;
            }

        }
        switch(key3)
        {
        case Title:
            queryString += QString::fromLatin1("t ");
            break;

        case Author:
            queryString += QString::fromLatin1("a ");
            break;

	case Journal:
            queryString += QString::fromLatin1("j ");
            break;

        case Keyword:
            queryString += QString::fromLatin1("k ");
            break;

        default:
            stop();
            return;
        }
	queryString += str;

    }
	search(queryString);


}

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

    m_data.truncate(0);
    SPIRES_BASE_URL = mirror_map[m_mirror];
    m_url = KURL(SPIRES_BASE_URL);
    m_url.addPath(QString::fromLatin1(SPIRES_SEARCH_CGI));

    QString str;
    m_query = queryString;


    if (m_query.isEmpty())
    {
        stop();
        return;
    }
    m_url.addQueryItem(QString::fromLatin1("FORMAT"), QString::fromLatin1("WWWRESULT"));
    m_url.addQueryItem(QString::fromLatin1("rawcmd"), m_query);
 

    m_step = Search;

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

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

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

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

void SpiresSearcher::searchResults(){
	
	QString str = QString::fromUtf8(m_data, m_data.size());
	

	QRegExp rx ( QString::fromLatin1 ( ".*Result: <b>(\\d+).*" ) );

	if ( !rx.exactMatch ( str ) )
	{
		signalMessage ( i18n ( "No matching records were found for your search." ), 1 );
		stop();
	}
	str = rx.cap ( 1 );
	int total = str.toInt();
	m_waitingRetrieveRange = true;
	m_step = Wait;
	if ( total > 0 )
	{
		emit signalQueryResult ( total );
	}
	else
	{
		signalMessage ( i18n ( "No matching records were found for your search." ), 1 );
		stop();
	}


}


void SpiresSearcher::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_total = max-min+1;

    m_url = KURL(SPIRES_BASE_URL);
    m_url.addPath(QString::fromLatin1(SPIRES_SEARCH_CGI));

    m_url.addQueryItem(QString::fromLatin1("FORMAT"), QString::fromLatin1("WWWBRIEFBIBTEX"));
    m_url.addQueryItem(QString::fromLatin1("rawcmd"), m_query);
    m_url.addQueryItem(QString::fromLatin1("SKIP"),  QString::number(min-1));
 
    m_data.truncate(0);
    m_step = Fetch;

    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 SpiresSearcher::fetchResults(){
	QString str = QString::fromUtf8(m_data, m_data.size());
	
	int start_index = str.find("<!-- START RESULTS -->");
	int stop_index = str.find("<!-- END RESULTS -->");
	QString refStr = str.mid(start_index, stop_index-start_index);

	KTempFile tmpfile;
	tmpfile.setAutoDelete(true);
    	QString fn = tmpfile.name();
	QFile f ( fn );
	if ( f.open ( IO_WriteOnly ) )
	{
		QTextStream t ( &f );
		t << refStr;
	}
	f.close();
	//Read bibfile
	BibentryTable *entryList = new BibentryTable();
	entryList->readBibfile ( const_cast<char*>(fn.ascii()), false );
	int total;
	if (m_total < entryList->size())
		total = m_total;
	else
		total = entryList->size();	

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

}


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


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


SearcherConfigWidget* SpiresSearcher::configWidget(QWidget* parent_)
{
    return new SpiresConfigWidget(parent_, this);
}


SpiresConfigWidget::SpiresConfigWidget(QWidget* parent_, SpiresSearcher* searcher_ /*=0*/)
        : SearcherConfigWidget(parent_)
{
	m_searcher = searcher_;
	QVBoxLayout* l = new QVBoxLayout ( optionsWidget() );

	QHBox* box = new QHBox ( optionsWidget(), "box" );
 	l->addWidget(box);
	box->setSpacing ( KDialog::spacingHint() );

	QLabel* label = new QLabel ( i18n ("Mirror:" ), box );
	m_mirrorCombo = new KComboBox ( box);
	m_mirrorCombo->insertItem(QString::fromLatin1("DESY (Germany)"), QString::fromLatin1("DESY (Germany)"));
	m_mirrorCombo->insertItem(QString::fromLatin1("Fermilab (US)"), QString::fromLatin1("Fermilab (US)"));
	m_mirrorCombo->insertItem(QString::fromLatin1("IHEP (Russia)"), QString::fromLatin1("IHEP (Russia)"));
	m_mirrorCombo->insertItem(QString::fromLatin1("Durham U. (UK)"), QString::fromLatin1("Durham U. (UK)"));
	m_mirrorCombo->insertItem(QString::fromLatin1("SLAC (US)"), QString::fromLatin1("SLAC (US)"));
	m_mirrorCombo->insertItem(QString::fromLatin1("YITP (Japan)"), QString::fromLatin1("YITP (Japan)"));
	m_mirrorCombo->insertItem(QString::fromLatin1("LIPI (Indonesia)"), QString::fromLatin1("YLIPI (Indonesia)"));

	QString w = i18n("The mirror of Spires-HEP database");
    	QWhatsThis::add(label, w);
    	QWhatsThis::add(m_mirrorCombo, w);
    	label->setBuddy(m_mirrorCombo);


	l->addStretch();
	if(searcher_)
    	{
        	m_mirrorCombo->setCurrentText(searcher_->m_mirror);
	}
	
	KURLLabel *urlLab = new KURLLabel( optionsWidget() );
	l->addWidget(urlLab);
	urlLab->setText( "More information about Spires-HEP");
        urlLab->setURL("http://www.slac.stanford.edu/spires/");
    
	connect(urlLab , SIGNAL( leftClickedURL( const QString& ) ), kapp, SLOT( invokeBrowser( const QString& ) ) );

	KAcceleratorManager::manage(optionsWidget());
}

void SpiresConfigWidget::updateSearcher()
{
    SpiresSearcher *s = static_cast<SpiresSearcher*>(m_searcher);
    s->m_mirror = m_mirrorCombo->currentText();
}



#include "spiressearcher.moc"
