//
// C++ Implementation: woksearcher
//
// Description: 
//
//
// Author: Thach Nguyen <thach.nguyen@rmit.edu.au>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "woksearcher.h"
#include "searchmanager.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 <qdom.h>
#include <qregexp.h>
#include <qlabel.h>
#include <qfile.h>
#include <qlayout.h>
#include <qwhatsthis.h>

#include <iostream>

namespace {
  static const char* WOK_BASE_URL = "http://estipub.isiknowledge.com";
  static const char* WOK_SEARCH_CGI = "esti/cgi";
}


WOKSearcher::WOKSearcher(QObject *parent, const char *name)
	: searcher(parent, name),m_step(Begin), m_started(false)
{
	m_host = QString::fromLatin1(WOK_BASE_URL);
	m_dbname = QString::fromLatin1("WOS");
}


WOKSearcher::~WOKSearcher()
{
}
QString WOKSearcher::defaultName() {
	return i18n("Web of Knowledge");
}

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

void WOKSearcher::readConfig(KConfig* config_, const QString& group_) {
	KConfigGroupSaver groupSaver(config_, group_);
	QString s = config_->readEntry("Database", QString::fromLatin1("WOS")); 
	if(!s.isEmpty()) {
		m_dbname = s;
	}
	
	s = config_->readEntry("Name", defaultName()); 
	if(!s.isEmpty()) {
		m_name = s;
	}
	
	m_host = config_->readEntry("Host", WOK_BASE_URL);
}


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

void WOKSearcher::search(SearchKey key1, SearchKey key2, SearchKey key3 , const QString& value1, const QString& value2, const QString& value3, int operator1, int operator2) {
 
    QString queryString;

    if (!value1.isEmpty())
    {
        switch(key1)
        {
        case Title:
            queryString = QString::fromLatin1("TI=(");
            break;

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

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

        case Year:
            queryString = QString::fromLatin1("PY=(");
            break;

        default:
            stop();
            return;
        }
		queryString += value1 + QString::fromLatin1(")");
    }


    if (!value2.isEmpty() )
    {
        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("TI=(");
            break;

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

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

        case Year:
            queryString += QString::fromLatin1("PY=(");
            break;

        default:
            stop();
            return;
        }
		queryString += value2 + QString::fromLatin1(")");

    }

    if (!value3.isEmpty() )
    {
        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("TI=(");
            break;

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

        case Keyword:
            m_query += QString::fromLatin1("TS=(");
            break;

        case Year:
            m_query += QString::fromLatin1("PY=(");
            break;

        default:
            stop();
            return;
        }
		queryString += value3 + QString::fromLatin1(")");

    }
	search(queryString);


}

void WOKSearcher::search(QString queryString){
    std::cerr << "Query string = " << queryString << "\n";
    m_started = true;

    m_data.truncate(0);

    m_url = KURL(m_host);
    m_url.addPath(QString::fromLatin1(WOK_SEARCH_CGI));

    QString str;
    m_query = queryString;


    if (m_query.isEmpty())
    {
        stop();
        return;
    }
    m_url.addQueryItem(QString::fromLatin1("databaseID"), m_dbname);
    m_url.addQueryItem(QString::fromLatin1("rspType"), "xml");
    m_url.addQueryItem(QString::fromLatin1("method"), "search");
	m_url.addQueryItem(QString::fromLatin1("firstRec"), "1");
	m_url.addQueryItem(QString::fromLatin1("numRecs"), "1");
	m_url.addQueryItem(QString::fromLatin1("query"), 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*)));
	
	
}

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

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

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

void WOKSearcher::searchResults(){
/*	QFile f(QString::fromLatin1("/home/s9510300/tmp/WOS1.xml"));
    QString str;
    if(f.open(IO_ReadOnly)) {
    	QTextStream stream( &f );
    	str = stream.read(); 
    	f.close();
    }
	
	QDomDocument dom;
	if(!dom.setContent(str, false)) {
		std::cerr << "WOKSearcher::fetchResults() - server did not return valid XML.\n";
		stop();
		return;
	}
*/

	
	
	QString str = QString::fromUtf8(m_data, m_data.size());
	
	QDomDocument dom;
	if(!dom.setContent(m_data, false)) {
		std::cerr << "WOKSearcher::searchResults() - server did not return valid XML.\n";
		stop();
		return;
	}

	int count = 0;
	for(QDomNode n = dom.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) {
		if ( n.nodeName() == QString::fromLatin1 ( "searchResults" ) )
		{
			for ( QDomNode f = n.firstChild(); !f.isNull(); f = f.nextSibling() )
			{

				QDomElement e = f.toElement();
				if ( e.isNull() )
				{
					continue;
				}
				if ( e.tagName() == QString::fromLatin1 ( "recordsFound" ) )
				{
					m_total = e.text().toInt();
					break;
				}
			}
			count++;
			
		}
		else if (n.nodeName() == QString::fromLatin1 ( "sessionID" ) ){
			QDomElement e = n.toElement();	
			m_sessionID = e.text();
			count++;
		}
		if (count == 2)
			break;
	}
	m_waitingRetrieveRange = true;
	m_step = Wait;
	if (m_total > 0)
		emit signalQueryResult(m_total);	
	else{
		signalMessage(i18n("No reference was found"), 1);
		stop();
	}

	
}


void WOKSearcher::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_url = KURL(m_host);
	m_url.addPath(QString::fromLatin1(WOK_SEARCH_CGI));
    m_url.addQueryItem(QString::fromLatin1("databaseID"), m_dbname);
	m_url.addQueryItem(QString::fromLatin1("SID"), m_sessionID);
    m_url.addQueryItem(QString::fromLatin1("rspType"), "xml");
    m_url.addQueryItem(QString::fromLatin1("method"), "searchRetrieve");
	m_url.addQueryItem(QString::fromLatin1("firstRec"), QString::number(min));
	m_url.addQueryItem(QString::fromLatin1("numRecs"), QString::number(max-min+1));
	m_url.addQueryItem(QString::fromLatin1("query"), m_query);


    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 WOKSearcher::fetchResults(){
/*	QFile f(QString::fromLatin1("/home/s9510300/tmp/CCC.xm"));
    QString str;
    if(f.open(IO_ReadOnly)) {
    	QTextStream stream( &f );
    	str = stream.read(); 
    	f.close();
    }
	
	QDomDocument dom;
	if(!dom.setContent(str, false)) {
		std::cerr << "WOKSearcher::fetchResults() - server did not return valid XML.\n";
		stop();
		return;
	}
*/	
	
	
	QDomDocument dom;
	if(!dom.setContent(m_data, false)) {
		std::cerr << "WOKSearcher::fetchResults() - server did not return valid XML.\n";
		stop();
		return;
	}

	BibEntry *entry;
	
	for(QDomNode m = dom.documentElement().firstChild(); !m.isNull(); m = m.nextSibling()) {
		if ( m.nodeName() == QString::fromLatin1 ( "records" ) )
		{
			for ( QDomNode n = m.firstChild(); !n.isNull(); n = n.nextSibling() )
			{

				if ( n.nodeName() == QString::fromLatin1 ( "REC" ) ){
					for (QDomNode o = n.firstChild(); !o.isNull(); o = o.nextSibling() ){

						if ( o.nodeName() == QString::fromLatin1 ( "item" ) )
						{
							entry = new BibEntry(QString::fromLatin1("article"), QString());
							QDomElement e;
							for ( QDomNode p = o.firstChild(); !p.isNull(); p = p.nextSibling() )
							{

								if (p.nodeName() == QString::fromLatin1("source_title") ){
									e = p.toElement();	
									if (!e.isNull()){
										if (!e.text().isEmpty())
											entry->setField(QString::fromLatin1("journal"), e.text());	
									}
								}
								else if (p.nodeName() == QString::fromLatin1("item_title") ){
									e = p.toElement();	
									if (!e.isNull()){
										if (!e.text().isEmpty())
											entry->setField(QString::fromLatin1("title"), e.text());	
									}
								}
								else if (p.nodeName() == QString::fromLatin1("bib_pages") ){
									e = p.toElement();	
									if (!e.isNull()){
										if (!e.text().isEmpty())
											entry->setField(QString::fromLatin1("pages"), e.text());	
									}
								}
								else if (p.nodeName() == QString::fromLatin1("ut") ){
									e = p.toElement();	
									if (!e.isNull()){
										if (!e.text().isEmpty())
											entry->setField(QString::fromLatin1("url"), QString::fromLatin1("http://links.isiglobalnet2.com/gateway/Gateway.cgi?GWVersion=1&SrcAuth=KBib&SrcApp=KBib&KeyUT=") + e.text());	
									}
								}
								else if (p.nodeName() == QString::fromLatin1("article_nos") ){
									for ( QDomNode q = p.firstChild(); !q.isNull(); q = q.nextSibling() ){
										if ( q.nodeName() == QString::fromLatin1 ( "article_no" ) ){
											e = p.toElement();	
											if (!e.isNull()){
												if (!e.text().isEmpty())
													entry->setField(QString::fromLatin1("doi"), e.text().section(QString::fromLatin1("DOI "), 1));	
											}
										}
									}
								}
								else if (p.nodeName() == QString::fromLatin1("bib_issue") ){
									QDomNamedNodeMap nodeMap = p.attributes();
									for (int i = 0; i < nodeMap.count(); i++){
										if (nodeMap.item(i).nodeName() == QString::fromLatin1("year"))
											entry->setField(QString::fromLatin1("year"), nodeMap.item(i).nodeValue());
										if (nodeMap.item(i).nodeName() == QString::fromLatin1("vol"))
											entry->setField(QString::fromLatin1("volume"), nodeMap.item(i).nodeValue());
									}
								}
								
								else if (p.nodeName() == QString::fromLatin1("bib_id") ){
									e = p.toElement();	
									if (!e.isNull()){
										if (!e.text().isEmpty()){
											QRegExp entryRx1(QString::fromLatin1(".*\\((.+)\\):.+\\s+(.+)\\s+\\d\\d\\s+\\d\\d\\d\\d") );		
											QRegExp entryRx2(QString::fromLatin1(".*\\((.+)\\):.+\\s+(.+)\\s+\\d\\d\\d\\d") );
											QRegExp entryRx3(QString::fromLatin1(".*\\((.+)\\):.+\\s+\\d\\d\\d\\d") );
											QRegExp entryRx4(QString::fromLatin1(".*:.+\\s+(\\S+)\\s+\\d\\d\\d\\d") );
											
											
											if (entryRx1.exactMatch(e.text()))
        									{
												QString text = entryRx1.cap(1);
												entry->setField(QString::fromLatin1("number"),text);
												text = entryRx1.cap(2);
												entry->setField(QString::fromLatin1("month"),text);
										
											}
											else if (entryRx2.exactMatch(e.text()))
        									{
												QString text = entryRx2.cap(1);
												entry->setField(QString::fromLatin1("number"),text);
												text = entryRx2.cap(2);
												entry->setField(QString::fromLatin1("month"),text);
										
											}
											
											else if (entryRx3.exactMatch(e.text()))
        									{
												QString text = entryRx3.cap(1);
												entry->setField(QString::fromLatin1("number"),text);
											}
											else if (entryRx4.exactMatch(e.text()))
        									{
												QString text = entryRx4.cap(1);
												entry->setField(QString::fromLatin1("month"),text);
										
											}
										}	
									}
								}
								
								else if (p.nodeName() == QString::fromLatin1("authors") ){
									RefField *field = BibEntryDefTable::self()->getRefField(QString::fromLatin1("author"));
									QString linkSt;
									if (field)
										linkSt = field->connectingString;
									else
										linkSt = QString::fromLatin1(" and ");
									QString author;
									for ( QDomNode q = p.firstChild(); !q.isNull(); q = q.nextSibling() ){
										if ( q.nodeName() == QString::fromLatin1 ( "primaryauthor" ) || q.nodeName() == QString::fromLatin1 ( "author" )){
											e = q.toElement();	
											if (!e.isNull()){
												if (!e.text().isEmpty()){
													if (author.isEmpty() )
														author = e.text();
													else	
														author = author + linkSt + e.text();
												}
															
											}	
											
										}
									
										
									}
									if (!author.isEmpty())
										entry->setField(QString::fromLatin1("author"), author);
								}
									
								else if (p.nodeName() == QString::fromLatin1("keywords") || p.nodeName() == QString::fromLatin1("keywords_plus")  ){
									RefField *field = BibEntryDefTable::self()->getRefField(QString::fromLatin1("keywords"));
									QString linkSt;
									if (field)
										linkSt = field->connectingString;
									else
										linkSt = QString::fromLatin1(", ");
									QString keywords = entry->getField(QString::fromLatin1("keywords"));
									for ( QDomNode q = p.firstChild(); !q.isNull(); q = q.nextSibling() ){
										if ( q.nodeName() == QString::fromLatin1 ( "keyword" ) || q.nodeName() == QString::fromLatin1 ( "keyplus" ) ){
											e = q.toElement();	
											if (!e.isNull()){
												if (!e.text().isEmpty()){
													if (keywords.isEmpty() )
														keywords = e.text().lower();
													else	
														keywords = keywords + linkSt + e.text().lower();
														
															
												}
											}	
										}
									}
									if (!keywords.isEmpty())
										entry->setField(QString::fromLatin1("keywords"), keywords);
								}
								else if (p.nodeName() == QString::fromLatin1("abstract") ){
									QString abstract;
									for ( QDomNode q = p.firstChild(); !q.isNull(); q = q.nextSibling() ){
										if (q.nodeName() = QString::fromLatin1("p")){
											e = q.toElement();	
											if (!e.isNull()){
												if (!e.text().isEmpty()){
													if (abstract.isEmpty())
														abstract = e.text();
													else
														abstract = abstract + QString::fromLatin1("\n\n") + e.text();
												
												}
											}
										}	
									}
									entry->setField(QString::fromLatin1("abstract"), abstract);	
									
								}
							
								
								
							}
							emit signalResultFound(new BibEntry(*entry));
							delete entry;
							break;
						}
						
					}
				}
				
			}
			
			
			break;
		}
	}
	stop();

}


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


QStringList WOKSearcher::searchKey(){
	QStringList keyList;
	keyList << searchManager::self()->searchKeyString(Author)
			<< searchManager::self()->searchKeyString(Title)
			<< searchManager::self()->searchKeyString(Topic)
			<< searchManager::self()->searchKeyString(Year);
	return keyList;
}


SearcherConfigWidget* WOKSearcher::configWidget(QWidget* parent_)
{
    return new WOKConfigWidget(parent_, this);
}


WOKConfigWidget::WOKConfigWidget(QWidget* parent_, WOKSearcher* searcher_ /*=0*/)
        : SearcherConfigWidget(parent_)
{
    m_searcher = searcher_;
    QGridLayout* l = new QGridLayout(optionsWidget(), 4, 2);
    l->setSpacing(4);
    l->setColStretch(1, 10);

    int row = -1;
    QLabel* label = new QLabel(i18n("Server address: "), optionsWidget());
    l->addWidget(label, ++row, 0);
    m_hostEdit = new KLineEdit(optionsWidget());
    l->addWidget(m_hostEdit, row, 1);
    QString w = i18n("Enter the host name or IP address of the WOK server. By default it is http://estipub.isiknowledge.com. If your instution uses different address, enter it here.");
    QWhatsThis::add(label, w);
    QWhatsThis::add(m_hostEdit, w);
    label->setBuddy(m_hostEdit);

    label = new QLabel(i18n("Database: "), optionsWidget());
    l->addWidget(label, ++row, 0);
    m_dataBaseCombo = new KComboBox(optionsWidget());
    m_dataBaseCombo->insertItem(QString::fromLatin1("Web of Science") ); //, QString::fromLatin1("WOS"));
    m_dataBaseCombo->insertItem(QString::fromLatin1("Current Contents Connect")); //, QString::fromLatin1("CCC"));
//    m_dataBaseCombo->insertItem(QString::fromLatin1("Food Science and Technology Abstracts")); //, QString::fromLatin1("FSTA"));
		
    l->addWidget(m_dataBaseCombo, row, 1);
	w = i18n("The database to search. Currently, KBib can search these databases: Web of Science (WOS) and Current Contents Connect (CCC) ");
    QWhatsThis::add(label, w);
    QWhatsThis::add(m_dataBaseCombo, w);
    label->setBuddy(m_dataBaseCombo);
    KURLLabel *urlLab = new KURLLabel( optionsWidget() );
	l->addWidget(urlLab, ++row, 0);
	urlLab->setText( "More information about WoK");
        urlLab->setURL("http://www.isiwebofknowledge.com/");
    
	connect(urlLab , SIGNAL( leftClickedURL( const QString& ) ), kapp, SLOT( invokeBrowser( const QString& ) ) );

    l->setRowStretch(++row, 1);

    if(searcher_)
    {
        m_hostEdit->setText(searcher_->m_host);
		if ((searcher_->m_dbname).upper() == QString::fromLatin1("CCC") )
			m_dataBaseCombo->setCurrentItem(1);
//		else if ((searcher_->m_dbname).upper() == QString::fromLatin1("FSTA") )
//			m_dataBaseCombo->setCurrentItem(2);
		else
			m_dataBaseCombo->setCurrentItem(0);
    }
	else
		m_hostEdit->setText(WOK_BASE_URL);
    KAcceleratorManager::manage(optionsWidget());
}

void WOKConfigWidget::updateSearcher()
{
    WOKSearcher *s = static_cast<WOKSearcher*>(m_searcher);
    s->m_host = m_hostEdit->text().stripWhiteSpace();
    switch(m_dataBaseCombo->currentItem()){
		case 1:
			s->m_dbname = QString::fromLatin1("CCC");
			break;
//		case 2:
//			s->m_dbname = QString::fromLatin1("FSTA");
//			break;
		default:
			s->m_dbname = QString::fromLatin1("WOS");
			break;	
				
		
	}
}


#include "woksearcher.moc"
