//
// C++ Implementation: id2entry
//
// Description: 
//
//
// Author: Thach Nguyen <thach.nguyen@rmit.edu.au>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "id2entry.h"
#include "kbib.h"
#include "crossrefdlg.h"
#include "filters/bibprogs.h"
#include "bibentrytable.h"

#include <kmessagebox.h>
#include <klocale.h>
#include <kio/job.h>
#include <qdom.h>
#include <qfile.h>

#include <iostream>

ID2Entry::ID2Entry(QObject *parent, const char *name, BibEntry *entry)
 : QObject(parent, name), m_step(Begin), m_started(false), progressDlg(0)
{
	setEntry(entry);
	m_timer = new QTimer( this );
    connect( m_timer, SIGNAL(timeout()), this, SLOT(check()) );
}


ID2Entry::~ID2Entry()
{
}

void ID2Entry::stop() {
	if(!m_started) {
		return;
	}
	if(m_job) {
		m_job->kill();
		m_job = 0;
	}
	m_started = false;
	m_data.truncate(0);
	m_step = Begin;
	m_timer->stop();
	if (progressDlg)
		progressDlg->hide();
	if (m_entry)
		delete m_entry;
}

void ID2Entry::search(){
	m_started = true;

    m_data.truncate(0);
	m_step = Search;
	
	switch (m_type){
		case DOI:{
			KConfig* m_config = KGlobal::config();
			KConfigGroupSaver groupSaver(m_config, QString::fromLatin1("Crossref"));
	    	QString user = m_config->readEntry("User");
    		QString password = m_config->readEntry("Password");
			QString st = user;
			st.truncate(5);
			if (st.lower() == QString::fromLatin1("ourl_"))
				user = user.right(user.length()-5);		
			if (user.isEmpty()) {
				CrossRefDlg *crossRefDlg = new CrossRefDlg((KBibApp*) parent());
				if (crossRefDlg->exec() == QDialog::Accepted){
					user = crossRefDlg->getUser();
					password = crossRefDlg->getPassword();	
					if (crossRefDlg->isSaveAccount()){
						m_config->writeEntry ( "User", user );
						m_config->writeEntry ( "Password", password );
					}
				}
				else{
					emit signalResultFound(0);
					stop();
					return;
				}
			}
			
			m_url = KURL(QString::fromLatin1("http://www.crossref.org"));
			m_url.addPath(QString::fromLatin1("openurl/"));
			m_url.addQueryItem(QString::fromLatin1("pid"), QString::fromLatin1("ourl_") +user+QString::fromLatin1(":")+password);
			m_url.addQueryItem(QString::fromLatin1("id"), QString::fromLatin1("doi:")+m_ID);
			m_url.addQueryItem(QString::fromLatin1("noredirect"), QString::fromLatin1("true"));
			
			break;
		}
		case arXiv:
			m_url = KURL(QString::fromLatin1("http://www.citebase.org"));
			m_url.addPath(QString::fromLatin1("openurl/"));
			m_url.addQueryItem(QString::fromLatin1("url_ver"), QString::fromLatin1("Z39.88-2004"));
			m_url.addQueryItem(QString::fromLatin1("svc_id"), QString::fromLatin1("bibtex"));
			m_url.addQueryItem(QString::fromLatin1("rft_id"), QString::fromLatin1("oai:arXiv.org:")+m_ID);
			break; 
		case PMID:
			m_url = KURL(QString::fromLatin1("http://eutils.ncbi.nlm.nih.gov/entrez/eutils/"));
			m_url.addPath(QString::fromLatin1("efetch.fcgi"));
			m_url.addQueryItem(QString::fromLatin1("tool"),       QString::fromLatin1("KBib"));
			m_url.addQueryItem(QString::fromLatin1("retmode"),    QString::fromLatin1("xml"));
			m_url.addQueryItem(QString::fromLatin1("db"),         QString::fromLatin1("pubmed"));
			m_url.addQueryItem(QString::fromLatin1("id"),  m_ID);
			break;
		default:
			stop();
			return;
		
	}
	
	
//	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*)));
	
	if (progressDlg)
		delete progressDlg;
	
	progressDlg = new  KProgressDialog ((KBibApp*) this->parent(), 0, QString::null, i18n("Contacting crossref.org to retrieve information, please wait..."));
	
	progressDlg->progressBar()->setTotalSteps(0);
	progressDlg->setAutoReset(true);
	progressDlg->progressBar()->setPercentageVisible(false);
	
	
	switch(m_type){
		case DOI:	
			progressDlg->setLabel (i18n("Contacting crossref.org to retrieve information, please wait..."));
			break;
		case arXiv:
			progressDlg->setLabel (i18n("Contacting citebase.org to retrieve information, please wait..."));
    		break;
		case PMID:
			progressDlg->setLabel (i18n("Contacting http://eutils.ncbi.nlm.nih.gov to retrieve information, please wait..."));
    		break;
		default:
			break;
	}
	m_timer->start( 100 );
	progressDlg->show();
	 
}

void ID2Entry::check(){
	if (progressDlg->wasCancelled ()){
		m_timer->stop();
		stop();	
		emit signalResultFound(0);
		return;	
	}
	progressDlg->progressBar()->setProgress(progressDlg->progressBar()->progress()+20);
}

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

void ID2Entry::slotComplete(KIO::Job* job_) {
	m_job = 0;

	if(job_->error()) {
		stop();
		KMessageBox::error( (KBibApp*) parent(),job_->errorString());
		emit signalResultFound(0);
		return;
	}
	QString str = QString::fromUtf8(m_data, m_data.size());


	if(m_data.isEmpty()) {
		KMessageBox::error( (KBibApp*) parent(),i18n("No data was received."));
		emit signalResultFound(0);
		stop();
		return;
	}
	switch(m_type){
		case DOI:
			parseCrossRef();
			break;
		case arXiv:
			parseCiteBase();
			break;
		case PMID:
			parsePubmed();
			break;
		default:
			break;		
	}
	stop();
}	
	
void ID2Entry::parseCrossRef(){
	
	QDomDocument dom;
	if(!dom.setContent(m_data, false)) {
		KMessageBox::error( (KBibApp*) parent(),i18n("CrossRef did not return valid XML. This could be due to incorrect user name/password or invalid DOI or the DOI is not known to crossref.org.\nThe DOI was %1.").arg(m_ID));
		emit signalResultFound(0);
		return;
	}

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

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

						if ( o.nodeName() == QString::fromLatin1 ( "query" ) )
						{
							entry = new BibEntry(QString::fromLatin1("article"), QString());
							if (m_entry)
								entry->setFields(*m_entry);
							QDomElement e;
							for ( QDomNode p = o.firstChild(); !p.isNull(); p = p.nextSibling() )
							{
								QString nodeName = p.nodeName();
								QString text;
								e = p.toElement();	
									if (!e.isNull())
										text = e.text();
								if (nodeName == QString::fromLatin1("doi") ){
									if(!text.isEmpty()){
										entry->setField(QString::fromLatin1("doi"), e.text());	
									}
									
									
									QDomNamedNodeMap nodeMap = p.attributes();
									for (int i = 0; i < nodeMap.count(); i++){
										if (nodeMap.item(i).nodeName() == QString::fromLatin1("type")){
											if (nodeMap.item(i).nodeValue() == QString::fromLatin1("conference_paper"))
												entry->setEntryType(QString::fromLatin1("inproceedings"));
										}
									}	
								}
								else if (nodeName == QString::fromLatin1("issn") ){
									if(!text.isEmpty())
										entry->setField(QString::fromLatin1("issn"), e.text());	
									
								}
								else if (nodeName == QString::fromLatin1("journal_title") ){
									if(!text.isEmpty())
										entry->setField(QString::fromLatin1("journal"), e.text());	
									
								}	
								else if (nodeName == QString::fromLatin1("contributors") ){
									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() )
									{
										QString nodeName_ = q.nodeName();
										if (nodeName_ == QString::fromLatin1("contributor") ){
											QString name;
											QDomNode r;
											r = q.namedItem(QString::fromLatin1("surname") );
											if (!r.isNull()){
												name = r.toElement().text();
											}
											if (!name.isEmpty() ){
												r = q.namedItem(QString::fromLatin1("given_name") );
												if (!r.isNull()){
													QString givenName;
													givenName = r.toElement().text();
													if (!givenName.isEmpty()){
														name = name + QString::fromLatin1(", ") + givenName;
													}
												}
											}
											if (!name.isEmpty()){
												if (author.isEmpty() )
													author = name;
												else
													author = author + linkSt + name;
											}
										}
	

									}	


									if(!author.isEmpty())
										entry->setField(QString::fromLatin1("author"), author);	
									
								}	
								else if (nodeName == QString::fromLatin1("volume") ){
									if(!text.isEmpty())
										entry->setField(QString::fromLatin1("volume"), text);	
									
								}	
								else if (nodeName == QString::fromLatin1("issue") ){
									if(!text.isEmpty())
										entry->setField(QString::fromLatin1("number"), text);	
									
								}	
								else if (nodeName == QString::fromLatin1("first_page") ){
									if(!text.isEmpty())
										entry->setField(QString::fromLatin1("pages"), text);	
									
								}	
								else if (nodeName == QString::fromLatin1("year") ){
									if(!text.isEmpty())
										entry->setField(QString::fromLatin1("year"), text);	
									
								}	
								else if (nodeName == QString::fromLatin1("article_title") ){
									if(!text.isEmpty()){
										text.remove(QString::fromLatin1("<![CDATA[") );
										text.remove(QString::fromLatin1("]]>") );
										entry->setField(QString::fromLatin1("title"), text);	
									}
								}	
								else if (nodeName == QString::fromLatin1("volume_title") ){
									if(!text.isEmpty())
										entry->setField(QString::fromLatin1("booktitle"), text);	
									
								}	
							}	
							
							
							break;
						}
					}
				}
			}
		}
	}	
	emit signalResultFound(entry);					
}					

void ID2Entry::parseCiteBase(){
	QString st = QString(m_data);
	int dumm;
	BibEntry* entry	= processBibEntryFromString(st.ascii(), dumm);
	if (!entry){
		KMessageBox::error( (KBibApp*) parent(),i18n("citebase.org did not return a valid BibTex entry. This could be due to invalid arXiv ID or the arXiv ID is not known to citebase.org.\nThe arXiv ID was %1.").arg(m_ID));
		emit signalResultFound(0);
		return;	
	}
	else{
		BibEntry *_entry = new BibEntry(entry->getEntryType(), entry->getKey());
		if (m_entry)
			_entry->setFields(*m_entry);
		for (int i = 0; i < entry->getNoFields(); i++){
			QString field = entry->getField(i);
			if (!field.isEmpty()){
				_entry->setField(entry->getFieldName(i), field);
				_entry->setStringMacroIndicator(entry->getFieldName(i), entry->stringMacroIndicator(i));
			}	
		}
		emit signalResultFound(_entry);
		delete entry;	
	}
}

void ID2Entry::parsePubmed(){
	QFile f(QString::fromLatin1("/tmp/kbib-entrez-pubmed.xml"));
	if(f.open(IO_WriteOnly)) {
		QTextStream t(&f);
		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);
	
	if (entryList->size() < 1){
		KMessageBox::error( (KBibApp*) parent(),i18n("http://eutils.ncbi.nlm.nih.gov did not return valid XML file. This could be due to invalid PubMed ID.\nThe PubMed ID was %1.").arg(m_ID));
		emit signalResultFound(0);
		return;	
	}
	
	for (int i = 0; i < entryList->size(); i++){
		BibEntry *entry = entryList->get_entry(i);
		if (entry){
			BibEntry *_entry = new BibEntry(entry->getEntryType(), entry->getKey());
			if (m_entry)
				_entry->setFields(*m_entry);
			for (int i = 0; i < entry->getNoFields(); i++){
				QString field = entry->getField(i);
				if (!field.isEmpty()){
					_entry->setField(entry->getFieldName(i), field);
					_entry->setStringMacroIndicator(entry->getFieldName(i), entry->stringMacroIndicator(i));
				}
			}	
			emit signalResultFound(_entry);
		}
			
	}
	delete entryList;
	
}

#include "id2entry.moc"
