/***************************************************************************
                          bibentry.cpp  -  description
                             -------------------
    begin                : Sat May 24 2003
    copyright            : (C) 2003 by Thach Nguyen
    email                : thach.nguyen@rmit.edu.au
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <iostream>
#include <map>
#include <stdlib.h>
#include <ctype.h>
#include <vector>
using namespace std;


#include "bibentry.h"

#include <qstring.h>
#include <qstringlist.h>
#include <qstylesheet.h>
//extern int flag_allow_warnings;

int flag_allow_warnings = 0;

BibEntry::BibEntry()
{
	def = 0;
	req_field = 0;
	opt_field = 0;
	index = -1;
	nextra = 0;
}

BibEntry::BibEntry ( const QString n, const QString k )
{
	def = BibEntryDefTable::self()->getBibEntryDef ( n );

	key = k;

	if (!def)
		def = BibEntryDefTable::self()->getBibEntryDef ( QString::fromLatin1("other") );
	
	if ( def )
	{
//        special = false;
//		preamble = false;
//		stringMacro = false;
		req_field = new QString[def->numRequired() ];
		opt_field = new QString[def->numOptional() ];
		index = -1;
		asStringMacro = new bool[def->numRequired() +def->numOptional() ];
		for ( int i = 0; i < def->numRequired() +def->numOptional(); i++ )
			asStringMacro[i] = false;
//        setField(QString::fromLatin1("lockkey"), "N");
	}

	nextra = 0;
}


BibEntry::BibEntry ( BibEntry& bib )
{
	def = bib.def;
	key = bib.key;

//    special =  bib.special;
//    preamble = bib.preamble;
//    stringMacro = bib.stringMacro;
	index = bib.getIndex();

	if ( def )
	{
		req_field = new QString[def->numRequired() ];
		opt_field = new QString[def->numOptional() ];
		asStringMacro = new bool[def->numRequired() +def->numOptional() ];
	}
	/*    else if (special)
	    {
	        req_field = new string[1];
	        opt_field = 0;
	    }
	*/
	nextra = 0;
	setFields ( bib );
}



BibEntry& BibEntry::operator= ( BibEntry &bib )
{
	if ( def )
	{
		delete[] req_field;
		delete[] opt_field;
		delete[] asStringMacro;
		extra_field.clear();
		extra_field_string_macro.clear();
	}

	def = bib.def;
	key = bib.key;

//    special =  bib.special;
//	preamble = bib.preamble;

	if ( def )
	{
		req_field = new QString[def->numRequired() ];
		opt_field = new QString[def->numOptional() ];
		asStringMacro = new bool[def->numRequired() +def->numOptional() ];
	}

	nextra = 0;
	setFields ( bib );
	return ( *this );
}


BibEntry::~BibEntry()
{
	if ( def )
	{
		if ( req_field )
			delete[] req_field;
		if ( opt_field )
			delete[] opt_field;
		if ( asStringMacro );
		delete[] asStringMacro;
	}
}

QString BibEntry::getEntryType() const
{
	if ( def )
		return def->name;
	else
		return QString::null;
}

void BibEntry::setEntryType ( QString et )
{
	BibEntry bib ( et, key );

	if ( def )
		bib.setFields ( *this );
	else
	{
		def = BibEntryDefTable::self()->getBibEntryDef ( et );
		if ( def )
		{
			req_field = new QString[def->numRequired() ];
			opt_field = new QString[def->numOptional() ];
		}
		else
		{
			req_field = new QString[1];
			opt_field = 0;
		}
		nextra = 0;
	}

	*this = bib;
}

QString BibEntry::getKey() const
{
	return key;
}

QString BibEntry::getFieldName ( int i ) const
{
	if ( !def )
		return 0;

	if ( i<0 || i >= getNoFields() )
		return 0;

	if ( i < def->numRequired() + def->numOptional() )
		return ( i < def->numRequired() ) ? def->getRequired ( i ) :
		       def->getOptional ( i - def->numRequired() );

	i -= def->numRequired() + def->numOptional();

	QMap<QString, QString>::ConstIterator it = extra_field.begin();
	for ( ; i > 0 && it!=extra_field.end(); i-- )
	{
		it++;
	}
	return it.key();
}

QString BibEntry::getField ( const QString fn ) const
{
	if ( fn.lower() == QString::fromLatin1 ( "citation key" ) )
		return key;

	int x=getNoFields();

	for ( int i=0; i< x; i++ )
	{
		if ( fn.lower() == getFieldName ( i ).lower() )
		{
			return getField ( i );
		}
	}
	return 0;
}

bool BibEntry::stringMacroIndicator ( QString fieldnamex )
{
	QString fieldname = fieldnamex.lower();

	int i = def->getFieldIdx ( fieldname );
	if ( i>=0 )
	{

		return asStringMacro[i];
	}
	else
	{
		QMap<QString, bool>::Iterator it = extra_field_string_macro.begin();
		for ( ; it!=extra_field_string_macro.end(); it++ )
		{
			if ( ( it.key() ).lower() == fieldname ){
				return it.data();
			}
		}
		return false;
	}
}



QString BibEntry::getField ( int i ) const
{
	if ( !def )
		return ( i==0 ) ?  req_field[i] : 0;

	if ( i<0 || i >= getNoFields() )
		return 0;
	if ( i < def->numRequired() + def->numOptional() )
	{
		return ( i < def->numRequired() ) ? req_field[i] :
		       opt_field[i - def->numRequired() ];
	}
	i -= def->numRequired() + def->numOptional();
	QMap<QString, QString>::ConstIterator it = extra_field.begin();
	for ( ; i > 0 && it!=extra_field.end(); i-- )
		it++;
	return it.data();
}

QString BibEntry::getReqField ( int i ) const
{
	return ( i < def->numRequired() ) ? req_field[i] : 0;
}


QString BibEntry::getOptField ( int i ) const
{
	return ( i < def->numOptional() ) ? opt_field[i] : 0;
}

QString BibEntry::getExtField ( int i ) const
{

	int index = 0;
	QMap<QString, QString>::ConstIterator it = extra_field.begin();
	for ( ; it!=extra_field.end(); ++it )
	{
		if ( index == i )
			return it.data();
		index++;
	}


	return 0;
}


void BibEntry::setField ( int i, QString field )
{
	if ( i < 0 || i >= getNoFields() )
		return;

	if ( !def && req_field )
	{
		req_field[0] = field;
		return;
	}


	//    cerr << "sField " << key << ":" << i << ": " << field << "\n";
	if ( i < def->numRequired() )
	{
		req_field[i] = field;
	}
	else
	{
		if ( i < def->numRequired() + def->numOptional() )
		{
			opt_field[i - def->numRequired() ] = field;
		}


		/*        else
		        {
		            i -= def->numRequired() + def->numOptional();
					QMap<QString, QString>::Iterator it = extra_field.begin();
		    		for (; i > 0 && it!=extra_field.end(); i--)
						it++;
					it.data() = field;
				}
		*/
	}

}

void BibEntry::setField ( QString fieldnamex, QString field )
{
	//Convert fieldname to lowercaseless_findBibEntryBibEntry
	QString fieldname = fieldnamex.lower();

	if ( !def && req_field )
	{
		req_field[0] = field;
		return;
	}

	int i = def->getFieldIdx ( fieldname );

	if ( i>=0 )
	{
		setField ( i, field );
		//	cerr << key << ":Field " << fieldname << "|" << field << "\n";
	}
	else
	{
		if ( flag_allow_warnings )
			cerr << "Warning " << key << ": Ignored field \"" << fieldname
			<< "\" in the entry type <" << getEntryType() << ">.\n";
		if ( !extra_field.contains ( fieldname ) ){
			nextra++;
			//create macro indicator storage location
			extra_field_string_macro.insert ( fieldname, false );
		}
		extra_field.insert ( fieldname, field );
	}
}


void BibEntry::setStringMacroIndicator ( QString fieldnamex, bool s )
{
	//Convert fieldname to lowercaseless_findBibEntryBibEntry
	QString fieldname = fieldnamex.lower();

	int i = def->getFieldIdx ( fieldname );

	if ( i>=0 )
	{
		setStringMacroIndicator ( i, s );
	}
	else
	{
		//if (flag_allow_warnings)
		//cerr << "Set string warning " << key << ": Ignored field \"" << fieldname << "\" in the entry type <" << getEntryType() << ">.\n";
		extra_field_string_macro.insert ( fieldname, s );
	}
}

/*
void BibEntry::setFields(BibEntry &entry)
{
	clearExtraField();
	for (int i=0; i<entry.getNoFields(); i++)
    {
        string s = entry.getField(i);
        //if (!s.empty()) {
        setField(entry.getFieldName(i), const_cast<char*>(s.c_str()));
        int j = def->getFieldIdx(entry.getFieldName(i));
        if (j >= 0)
            asStringMacro[j] = entry.stringMacroIndicator(i);
		else
			setStringMacroIndicator(entry.getFieldName(i), entry.stringMacroIndicator(entry.getFieldName(i)) );
        //}
    }
}
*/


void BibEntry::setFields ( BibEntry &entry )
{
	clearExtraField();
	//Clear all main field;
	for ( int i = 0; i< ( getNoReqFields() + getNoOptFields() ); i++ )
	{
		setField ( i, 0 );
		setStringMacroIndicator ( i, false );
	}
	
	for ( int i=0; i< ( entry.getNoReqFields() + entry.getNoOptFields() ); i++ )
	{
		QString st = entry.getField ( i );
		if ( !st.isEmpty() )
		{
			setField ( entry.getFieldName ( i ), entry.getField ( i ) );
			setStringMacroIndicator ( entry.getFieldName ( i ), entry.stringMacroIndicator ( entry.getFieldName ( i ) ) );
		}

	}

	for ( int i = 0; i < entry.getNoExtraFields(); i++ )
	{
		int j = i+entry.getNoReqFields() + entry.getNoOptFields();
		QString st = entry.getField ( j );
		if ( !st.isEmpty() )
		{
			setField ( entry.getFieldName ( j ), st );
			setStringMacroIndicator ( entry.getFieldName ( j ), entry.stringMacroIndicator ( entry.getFieldName ( j ) ) );
		}

	}
}



int BibEntry::createKey ( QString connectingString )
{
	QString fn = getField ( "lockkey" );
	if ( fn == QString::fromLatin1 ( "Y" ) )
		return -1;

	QString authors = getField ( QString::fromLatin1 ( "author" ) );

	if ( authors.isEmpty() )
	{
		//if (strlen(authors)==0){
		authors = getField ( "editor" );
	}
	if ( !authors.isEmpty() )
	{
		QString firstAuthor = ( QString ( authors ).stripWhiteSpace() ).section ( "and", 0, 0 );
		firstAuthor = firstAuthor.stripWhiteSpace();
		QString firstAuthorSurname;
		if ( firstAuthor.contains ( ',' ) )
		{
			firstAuthorSurname = firstAuthor.section ( ',', 0, 0 );

		}
		else
			firstAuthorSurname = firstAuthor.section ( ' ', -1, -1 );
		key = firstAuthorSurname + connectingString + getField ( QString::fromLatin1 ( "year" ) );
		return 1;
	}
	return 0;
}


int BibEntry::getIndex()
{
	return index;
}

void BibEntry::setIndex ( int i )
{
	index = i;
}

bool BibEntry::replaceString ( bool allField, QString field, QString oldString, QString newString )
{
	if ( oldString == newString )
		return false;
	QString oldValue;
	QString newValue;
	bool change = false;
	if ( allField )
	{
		for ( int i=0; i<getNoFields(); i++ )
		{
			oldValue = getField ( i );
			if ( oldValue.contains ( oldString ) > 0 )
			{
				newValue = oldValue.replace ( oldString, newString );
				QString fn = getFieldName ( i );
				setField ( fn, newValue );
				change = true;
			}
		}

	}
	else
	{
		if (field.isEmpty())
			return false;
		oldValue = getField ( field );
		if ( oldValue )
		{
			if ( oldValue.contains ( oldString ) > 0 )
			{
				newValue = oldValue.replace ( oldString, newString );
				setField ( field, newValue );
				change = true;
			}
		}

	}
	return change;
}

int compareBibEntry ( BibEntry *entry1, BibEntry *entry2, int fields, bool compare_key, bool compare_lockkey )
{
	//return 1 if different, 0 if identical
	QString st1, st2;

	st1 = QString ( entry1->getEntryType() ).lower();
	st2 = QString ( entry2->getEntryType() ).lower();

	if ( st1 != st2 )
		return 1;

	QString entryTypeName = entry1->getEntryType();
	BibEntryDef *entryType = BibEntryDefTable::self()->getBibEntryDef ( entryTypeName );

	if ( compare_key )
	{
		st1 = entry1->getKey().lower();
		st2 = entry2->getKey().lower();
		if ( st1 != st2 )
			return 1;
	}

	for ( int i=0; i < entryType->numRequired(); i++ )
	{

		st1 = ( QString ( entry1->getField ( entryType->getRequired ( i ) ) ).lower() ).simplifyWhiteSpace();
		st2 = ( QString ( entry2->getField ( entryType->getRequired ( i ) ) ).lower() ).simplifyWhiteSpace();
		if ( QString ( entryType->getRequired ( i ) ) == QString ( "author" ) || QString ( entryType->getRequired ( i ) ) == QString ( "editor" ) )
		{
			st1 = reformatAuthors ( st1, 0 );
			st2 = reformatAuthors ( st2, 0 );
		}
		if ( st1 != st2 )
			return 1;
	}

	if ( fields == 1 || fields==2 )
	{
		for ( int i=0; i < entryType->numOptional(); i++ )
		{
			st1 = ( QString ( entry1->getField ( entryType->getOptional ( i ) ) ).lower() ).simplifyWhiteSpace();
			st2 = ( QString ( entry2->getField ( entryType->getOptional ( i ) ) ).lower() ).simplifyWhiteSpace();

			if ( QString ( entryType->getOptional ( i ) ) == QString ( "author" ) || QString ( entryType->getOptional ( i ) ) == QString ( "editor" ) )
			{
				st1 = reformatAuthors ( st1, 0 );
				st2 = reformatAuthors ( st2, 0 );
			}

			if ( QString ( entryType->getOptional ( i ) ).lower() == QString ( "lockkey" ) )
			{
				if ( compare_lockkey )
				{
					if ( st1 != st2 )
						return 1;
				}
			}

			else
			{
				if ( st1 != st2 )
					return 1;
			}
		}

	}

	if ( fields == 2 )
	{
		if ( entry1->getNoExtraFields() != entry2->getNoExtraFields() )
			return 1;
		else
		{
			for ( int i = 0; i < entry1->getNoExtraFields(); i++ )
			{
				st1 = ( QString ( entry1->getField ( entryType->numRequired() + entryType->numOptional() + i ) ).lower() ).simplifyWhiteSpace();
				st2 = ( QString ( entry2->getField ( entry1->getFieldName ( entryType->numRequired() + entryType->numOptional() + i ) ) ).lower() ).simplifyWhiteSpace();

				if ( st1 != st2 )
					return 1;
			}

		}

	}
	return 0;
}


void processAuthor ( QString st, QString &surname, QString &givenname )
{
	QString author = ( st.simplifyWhiteSpace() );

	/*    author = author.lower();
	    QChar c = author.at(0);
	    author = author.replace(0, 1, c.upper());

	    int i = 0;
	    while(i!=-1)
	    {
	            i=author.find(QString(" "), i+1);
	            c = author.at(i+1);
	            author = author.replace(i+1, 1, c.upper());
	    }

	    i = 0;
	    while(i!=-1)
	    {
	            i=author.find(QString("-"), i+1);
	            c = author.at(i+1);
	            author = author.replace(i+1, 1, c.upper());
	    }
	*/
	if ( author.contains ( ',' ) )
	{
		surname = author.section ( ',', 0, 0 );
		givenname = author.section ( ',', 1, 1 );
	}
	else
	{
		surname = author.section ( ' ', -1, -1 );
		givenname = author.section ( ' ', -100, -2 );
	}
	surname = surname.simplifyWhiteSpace();
	givenname = givenname.simplifyWhiteSpace();

}


QString reformatAuthors ( QString st, int numAuthorsDisplayed )
{
	QStringList authors = QStringList::split ( " and ", st );
	QString formatedAuthors=QString ( "" );
	bool first = true;
	int num=0;
	for ( QStringList::Iterator it = authors.begin(); it != authors.end(); ++it )
	{
		QString author = ( *it );

//        author = (author.simplifyWhiteSpace()).lower();
		author = author.simplifyWhiteSpace();
		QString surname, givenname;
		processAuthor ( author, surname, givenname );

		if ( givenname.isEmpty() )
			author = surname;
		else author = surname + QString ( ", " ) + givenname;

		/*        author = author.lower();
		        QChar c = author.at(0);
		        author = author.replace(0, 1, c.upper());

		        int i = 0;
		        while(i!=-1)
		        {
		            i=author.find(QString(" "), i+1);
		            c = author.at(i+1);
		            author = author.replace(i+1, 1, c.upper());
		        }
		*/
		if ( first )
			formatedAuthors = author;
		else
			formatedAuthors = formatedAuthors + " and " + author;
		first = false;
		num++;
		if ( numAuthorsDisplayed > 0 )
		{
			if ( numAuthorsDisplayed >= ( int ) ( authors.count() ) && num == ( int ) ( authors.count() ) )
				break;
			else if ( numAuthorsDisplayed == num )
			{
				formatedAuthors = formatedAuthors + " et al";
				break;
			}
		}
	}
	return formatedAuthors;
}




QString entryToString ( BibEntry *e, int fieldDelimiter )
{
	QString ld, rd;
	switch ( fieldDelimiter )
	{
		case 1:
			rd = ld="\"";
			break;
		case 2:
			ld = "(";
			rd = ")";
			break;
		default:
			ld = "{";
			rd = "}";
			break;
	}
	QString st;
	st=st+"@"+e->getEntryType() +"{"+e->getKey();
	for ( int j=0; j < e-> getNoFields(); j++ )
	{
		if ( ! ( ( QString ( e->getField ( j ) ) ).simplifyWhiteSpace() ).isEmpty() )
		{
			if ( e->stringMacroIndicator ( e->getFieldName ( j ) ) )
				st=st+",\n\t"+e->getFieldName ( j ) +"="+e->getField ( j );
			else
				st=st+",\n\t"+e->getFieldName ( j ) +"="+QString ( ld ) +e->getField ( j ) +QString ( rd );
		}

	}
	st=st+"\n}\n";
	return st;
}

