/***************************************************************************
 *   Copyright (C) 2004-2006 by Pere Constans
 *   constans@molspaces.com
 *   cb2Bib version 0.6.1
 *   See LICENSE file that comes with this distribution
 ***************************************************************************/
#include "astring.h"
#include "c2bbibparser.h"
#include "c2bbibpreparser.h"
#include "c2bclipedit.h"
#include "c2bpreprocess.h"
#include "c2bsettings.h"
#include "cb2bibbase.h"
#include "cb2bib_par.h"
#include "citeidmaker.h"
#include "journaldb.h"

#include <qapplication.h>
#include <qcombobox.h>
#include <qcursor.h>
#include <qeventloop.h>
#include <qfile.h>
#include <qmessagebox.h>
#include <qstatusbar.h>
#include <qtextedit.h>


c2bBibParser::c2bBibParser(c2bSettings *st, cb2bibbase *c2bMain, QObject *parent, const char *name)
        : bibParser(parent, name)
{
    c2b = c2bMain;
    s = st;

    // Setting Field-Widget Map
    setFieldWidgetMap();

    // Creating Reference Type Combo
    c2b->typecombo->insertStringList( TypesList );

    // Creating Journal Name Database
    if ( s->m_jdbfile.isEmpty() )
        QMessageBox::information( c2b, tr("cb2Bib Info"),
                                  tr("No Abbreviation Journal specified.\n\n") +
                                  tr("Usue cb2Bib Configure button to choose a filename and location.\n") +
                                  tr("NOTE: Restart cb2Bib after configuring Abbreviation Journal filename.\n\n"),
                                  tr("&Continue") );
    else
    {
        QFile jdbfile( s->m_jdbfile );
        if ( !jdbfile.open( IO_ReadOnly ) )  // jdbfile.exists wouldn't produce an errorString
            QMessageBox::warning( c2b, tr("cb2Bib Info"),
                                  tr(QString("Unable to open Journal Abbreviation file %1 for reading.\nError: '%2'\n\n").
                                     arg(s->m_jdbfile).arg(jdbfile.errorString())) +
                                  tr("Usue the Configure button to choose a filename and location.\n\n\n") +
                                  tr("NOTE: Restart cb2Bib after configuring Abbreviation Journal filename.\n\n"),
                                  tr("&Continue") );
        else
            jdbfile.close();
        JDB = new JournalDB( s->m_jdbfile );
    }

    // Creating Preparser Object
    prParser = new c2bBibPreParser( s, c2b );
    connect( prParser, SIGNAL(helpRequested(const QString&)), this, SIGNAL(helpRequested(const QString&)) );
    connect( prParser, SIGNAL(preParserDataAvailable(const QString&)), this, SIGNAL(preParserDataAvailable(const QString&)) );

    // Creating Stream Preprocess Object
    prProc = new c2bPreProcess( c2b );

    // Creating CiteID Maker
    citeIDM = new citeIDMaker( c2b );

    // Connections
    connect( c2b->journal, SIGNAL ( returnPressed() ), this, SLOT( setJournal() ) );
}

c2bBibParser::~c2bBibParser()
{
    delete citeIDM;
    delete JDB;
    delete prParser;
    delete prProc;
}


QString c2bBibParser::makeBib( bool sig )
{
    QString BibString;
    if( sig )
        BibString = "% cb2Bib " + APP_VERSION + "\n";
    else
        BibString = "";
    BibString += "@" + c2b->typecombo->currentText() + "{" + c2b->id->text();
    QStringList::Iterator it = fieldl.begin();
    while ( it != fieldl.end() )
    {
        QString fvalue = RefFields[ *it ]->text();
        if( !fvalue.isEmpty() )
        {
            if( s->m_convert_entries_to_latex )
                fvalue = c2btoBib( fvalue );
            QString fd = *it;
            QString dum;
            dum.fill( ' ', 12 - fd.length() );
            if( *it == "month" && s->m_pp_month )
                BibString += ",\n" + fd + dum + " =   " + fvalue;
            else
                BibString += ",\n" + fd + dum + " = {" + fvalue + "}";
        }
        it++;
    }
    BibString += "\n}\n";
    if( sig )
        BibString += "\n";
    return( BibString );
}

void c2bBibParser::makeCiteID()
{
    citeIDM->makeCiteID();
}

QString c2bBibParser::PProc( QString field, QString value )
{
    //  Post Processing of Fields
    value.replace( QRegExp( "<NewLine\\d+>" ), " " );
    value.replace( QRegExp( "<Tab\\d+>" ), " " );
    value = value.simplifyWhiteSpace();

    if( value.isEmpty() )
        return( value );
    if( field == "author" )
    {
        c2bDebug(value);
        AString AuthorList = AString( value, s->m_author_editor_name_full );
        value = AuthorList.bib();
    }
    else if( field == "addauthors" )
    {
        QString PAuthors = c2b->authors->text();
        c2bDebug(value);
        AString AuthorList = AString( value, s->m_author_editor_name_full );
        if( PAuthors.isEmpty() )
            value = AuthorList.bib();
        else
            value = PAuthors + " and " + AuthorList.bib();
    }
    else if( field == "editor" )
    {
        c2bDebug(value);
        AString EditorList = AString( value, s->m_author_editor_name_full );
        value = EditorList.bib();
    }
    else if( field == "addeditor" )
    {
        QString PEditor = c2b->editor->text();
        c2bDebug(value);
        AString EditorList = AString( value, s->m_author_editor_name_full );
        if( PEditor.isEmpty() )
            value = EditorList.bib();
        else
            value = PEditor + " and " + EditorList.bib();
    }
    else if( field == "journal" )
    {
        QString jvalue;
        if ( s->m_journal_name_full )
        {
            value = JDB->retrieveFull( value );
            jvalue = JDB->retrieve( value );
        }
        else
        {
            value = JDB->retrieve( value );
            jvalue = JDB->retrieveFull( value );
        }
    }
    else if( field == "month" )
    {
        if ( s->m_pp_month )
            value = MDB->retrieve( value );
    }
    else if( field == "pages" )
        value = setPages( value );
    else if( field == "title" || field == "booktitle" )
    {
        value = Bibtoc2b( value );
        if ( isUpperCaseString( value ) )
        {
            value = value.lower();
            value[0] = value[0].upper();
        }
    }
    else if( field == "addtitle" )
    {
        value = Bibtoc2b( value );
        if ( isUpperCaseString( value ) )
        {
            value = value.lower();
            value[0] = value[0].upper();
        }
        QString PTitle = c2b->title->text();
        if( !PTitle.isEmpty() )
            value = PTitle + ": " + value;
    }
    QString pvalue = value.simplifyWhiteSpace();
    return( pvalue );
}

QString c2bBibParser::abbreviatedJournal( const QString& j )
{
    return ( JDB->retrieve( j ) );
}

QString c2bBibParser::fullJournal( const QString& j )
{
    return ( JDB->retrieveFull( j ) );
}

void c2bBibParser::setJournal()
{
    c2b->journal->setText( PProc( "journal", c2b->journal->text()) );
}

QString c2bBibParser::setJournalsToFull( const QString& text )
{
    QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
    QString substituted_text = text;
    QRegExp jnre( "\\bjournal\\s*=\\s*[\\{\"](.*)[\\}\"]", FALSE );
    jnre.setMinimal(TRUE);
    int pos = 0;
    uint nj = 0;
    while ( pos >= 0 )
    {
        pos = jnre.search( substituted_text, pos );
        if ( pos > -1 )
        {
            QString line = jnre.cap( 0 );
            QString jn = jnre.cap( 1 );
            line.replace( jn, fullJournal( jn ) );
            substituted_text.replace( pos, jnre.matchedLength(), line );
            pos += line.length();
            nj++;
        }
        emit message( QString("Processed %1 journal names...").arg(nj) );
        qApp->eventLoop()->processEvents( QEventLoop::AllEvents );
    }
    QApplication::restoreOverrideCursor();
    emit message( QString("Processed %1 journal names.").arg(nj) );
    return ( substituted_text );
}

QString c2bBibParser::setJournalsToAbbreviated( const QString& text )
{
    QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
    QString substituted_text = text;
    QRegExp jnre( "\\bjournal\\s*=\\s*[\\{\"](.*)[\\}\"]", FALSE );
    jnre.setMinimal(TRUE);
    int pos = 0;
    uint nj = 0;
    while ( pos >= 0 )
    {
        pos = jnre.search( substituted_text, pos );
        if ( pos > -1 )
        {
            QString line = jnre.cap( 0 );
            QString jn = jnre.cap( 1 );
            line.replace( jn, abbreviatedJournal( jn ) );
            substituted_text.replace( pos, jnre.matchedLength(), line );
            pos += line.length();
            nj++;
        }
        emit message( QString("Processed %1 journal names...").arg(nj) );
        qApp->eventLoop()->processEvents( QEventLoop::AllEvents );
    }
    QApplication::restoreOverrideCursor();
    emit message( QString("Processed %1 journal names.").arg(nj) );
    return ( substituted_text );
}

void c2bBibParser::setRefType( QString type )
{
    //  Post Processing of Reference Types
    int i = 0;
    while( i < c2b->typecombo->count() )
    {
        if( c2b->typecombo->text(i) == type )
        {
            c2b->typecombo->setCurrentItem(i);
            return;
        }
        i++;
    }
    c2b->typecombo->setCurrentItem(0);
    c2b->typecombo->setCurrentText( type );
}

void c2bBibParser::clearFields()
{
    QStringList::Iterator it = fieldl.begin();
    while ( it != fieldl.end() )
    {
        RefFields[ *it ]->clear();
        it++;
    }
    c2b->typecombo->setCurrentItem(1);
    m_auto_regc = FALSE;
}



/****************************************************************************
 
AUTOMATIC BIB CAPTION
 
*****************************************************************************/

void c2bBibParser::preParseItem( const QString& text )
{
    prParser->preParseItem( text );
}

void c2bBibParser::processItem( QString text )
{
    /*! \page bibproc Extracting Data from the Clipboard

    Clipboard contents is processed according to the following rules:

    - Perform external, user-defined preparsing on input stream. See \ref c2bconf_input.

    - Perform user-defined substitutions on input stream. See \ref c2bconf_input.

    - Check if input stream is already a BibTeX entry. If so, process entry.

    */

    prProc->preProcStream( text );

    QRegExp bt( "^@(\\w+)" );
    if( bt.search( text ) > -1 )
    {
        QString RefType = bt.cap(1).lower();
        setRefType( RefType );
        c2b->clipb->setText( text );
        text = text.simplifyWhiteSpace();
        text.replace( QRegExp( "\\s*\\}$" ), ",}" );
        QStringList::Iterator it = fieldl.begin();
        while ( it != fieldl.end() )
        {
            QRegExp bf( "\\b" + *it + "\\s*=\\s*[\\{\"](.*)[\\}\"]\\,", FALSE );
            bf.setMinimal(TRUE);
            if( bf.search( text ) > -1 )
            {
                RefFields[ *it ]->setText(PProc( *it, bf.cap( 1 )));
            }
            else
            {
                // Consider non-bracket cases (eg. 'year = 2000,')
                QRegExp bfnc( "\\b" + *it + "\\s*=\\s*(\\w*)\\s*\\,", FALSE );
                bfnc.setMinimal(TRUE);
                if( bfnc.search( text ) > -1 )
                {
                    RefFields[ *it ]->setText(PProc( *it, bfnc.cap( 1 )));
                }
            }
            it++;
        }
        c2bAutoRecogStr = QString("Processed as 'BibTeX'.");
        m_auto_regc = TRUE;
        c2b->statusBar()->message( c2bAutoRecogStr );
        return;
    }

    /*! \page bibproc

    - Check if input stream is a PubMed - Medline Journal entry. If so, process entry.

    - Preprocess author names: PI JOAN III -> Pi III, J.
    (care of name prefixes, suffixes, and removal of ambiguities).

    */

    QRegExp ml( "^PMID-" );
    if( text.contains( ml ) )
    {
        setRefType( "article" );
        c2b->clipb->setText( text );
        text.replace( QRegExp("\\n([A-Z]{1,4}\\s*-)"), "][\\1" );
        text = text.simplifyWhiteSpace();
        if ( !text.contains( QRegExp("\\[FAU\\s+-") ) )
            text.replace( QRegExp("\\[(AU\\s*-\\s*[-'\\w]+)"), "[F\\1 " );
        QStringList fList = QStringList::split( "][", text );
        QString kw = "";

        for ( QStringList::Iterator it = fList.begin(); it != fList.end(); ++it )
        {
            QString fld = *it;
            fld.remove( QRegExp("^[A-Z]{1,4}\\s{0,1}-") );
            if ( QString(*it).startsWith( "AB -" ) )
                RefFields[ "abstract" ]->setText(PProc( "abstract", fld));
            else if ( QString(*it).startsWith( "FAU -" ) )
                RefFields[ "author" ]->setText(PProc( "addauthors", medlToc2b(fld)));
            else if ( QString(*it).startsWith( "TA -" ) )
                RefFields[ "journal" ]->setText(PProc( "journal", fld));
            else if ( QString(*it).startsWith( "IP -" ) )
                RefFields[ "number" ]->setText(PProc( "number", fld));
            else if ( QString(*it).startsWith( "PG -" ) )
                RefFields[ "pages" ]->setText(PProc( "pages", fld));
            else if ( QString(*it).startsWith( "TI -" ) )
                RefFields[ "title" ]->setText(PProc( "title", fld));
            else if ( QString(*it).startsWith( "VI -" ) )
                RefFields[ "volume" ]->setText(PProc( "volume", fld));
            else if ( QString(*it).startsWith( "AID -" ) )
            {
                if ( fld.contains( "[doi]" ) )
                    RefFields[ "doi" ]->setText(PProc( "doi", fld.remove("[doi]")));
            }
            else if ( QString(*it).startsWith( "DP -" ) )
                RefFields[ "year" ]->setText(
                    PProc( "year", fld.replace(QRegExp("^([\\d\\s]+).*$"),"\\1")));
            else if ( QString(*it).startsWith( "MH -" ) )
                kw += "; " + fld;
        }
        if ( !kw.isEmpty() )
            RefFields[ "keywords" ]->setText(PProc( "keywords", kw.remove(0,2) ));
        c2bAutoRecogStr = QString("Processed as 'PubMed - Medline Journals'.");
        m_auto_regc = TRUE;
        c2b->statusBar()->message( c2bAutoRecogStr );
        return;
    }

    /*! \page bibproc

    If otherwise,

    - Extract DOI \n (DOI, URL and FILE/PDF are preprocessed, performed before the automatic
    recognition takes place.)

    */
    QRegExp rxdoi( "(10.[\\d\\.]+/\\S+)" );
    int ndoi = rxdoi.search( text );
    if( ndoi > -1 )
        c2b->doi->setText(PProc( "doi", rxdoi.cap( 1 )));

    /*! \page bibproc

    - Extract URL

    */
    QRegExp rxhtml( "(http://\\S+)" );
    int nhtml = rxhtml.search( text );
    if( nhtml > -1 )
        c2b->html->setText(PProc( "html", rxhtml.cap( 1 )));
    else
    {
        QRegExp rxhtml( "(www\\.\\S+)" );
        nhtml = rxhtml.search( text );
        if( nhtml > -1 )
            c2b->html->setText(PProc( "html", rxhtml.cap( 1 )));
        else
        {
            QRegExp rxhtml( "(\\S+\\.(com|edu|net|org)\\S*)" );
            nhtml = rxhtml.search( text );
            if( nhtml > -1 )
                c2b->html->setText(PProc( "html", rxhtml.cap( 1 )));
        }
    }

    /*! \page bibproc

    - Remove leading and trailing white spaces, TABs and CRs.

    */
    text = text.stripWhiteSpace();

    /*! \page bibproc

    - "\r\n", "\n" and/or "\r" replaced by the line indicator tag "<NewLineN>".

    */
    text.replace(QRegExp("\\r\\n"),"<found_new_line>");  // Windows new line
    text.replace(QRegExp("\\n"),"<found_new_line>");     // Linux new line, LF
    text.replace(QRegExp("\\r"),"<found_new_line>");     // OSX new line, CR
    QStringList spText = QStringList::split( "<found_new_line>", text );
    int n = spText.count();
    text = "";
    for ( int i =0; i < n-1; i++ )
        text += spText[i] + QString("<NewLine%1>").arg(i+1);
    text += spText[n-1];

    /*! \page bibproc

    - Replace "\t" and five or more consecutive "\s" by the tabular tag "<TabN>".

    */
    spText = QStringList::split( QRegExp("(\\s{5,}|\\t)"), text );
    n = spText.count();
    text = "";
    for ( int i =0; i < n-1; i++ )
        text += spText[i] + QString("<Tab%1>").arg(i+1);
    text += spText[n-1];

    /*! \page bibproc

    - Simplify White Spaces

    */
    text = text.simplifyWhiteSpace();
    c2b->clipb->setText( text );

    /*! \page bibproc

    - Start the automatic recognition engine.

    */
    if ( s->m_regexpfl.isEmpty() )
    {
        QMessageBox::information(
            c2b, tr("cb2Bib Info"),
            tr( QString("No RegExp filename specified.\n\n") ) +
            tr( "Usue cb2Bib Configure button to choose a filename and location.\n\n" ),
            tr("&Continue") )
        ;
        return;
    }
    QFile file( s->m_regexpfl );
    if ( ! file.open( IO_ReadOnly ) )
    {
        QMessageBox::warning( c2b, tr("cb2Bib Info"),
                              tr( QString("Unable to open RegExp file %1 for reading.\nError: '%2'\n\n").
                                  arg(s->m_regexpfl).arg(file.errorString()) ) +
                              QString("Usue the Configure button to choose filename and location.\n"
                                      "After manual matching, patterns can be edited and stored in the "
                                      "RegExp file for autodetection.\n\n\n"), tr("&Continue") );
        return;
    }

    QString ItemX;
    QString line;
    QString reftype;
    QString fieldset;
    QTextStream stream( &file );
    int nfilts = 0;

    while ( !stream.atEnd() )
    {
        line = stream.readLine();
        if ( !( line.isEmpty() || line.contains(QRegExp("^#")) ) )
        {
            reftype = stream.readLine();
            fieldset = stream.readLine();
            ItemX = stream.readLine();

            c2bDebug( QString("The RegExp file contains1: |%1|").arg(line) );
            c2bDebug( QString("The RegExp file contains2: |%1|").arg(reftype) );
            c2bDebug( QString("The RegExp file contains3: |%1|").arg(fieldset) );
            c2bDebug( QString("The RegExp file contains4: |%1|").arg(ItemX) );

            QRegExp rx( ItemX );
            rx.setMinimal( TRUE );
            if ( ! rx.isValid() )
                qDebug( QString("cb2Bib: RegExp |%1| is not valid.").arg(ItemX) );

            QStringList list = QStringList::split(" ", fieldset);
            nfilts++;
            int nfields = rx.numCaptures();
            int ncap = rx.search( text );
            c2bDebug( QString("Readable Fields: |%1|").arg(nfields) );
            c2bDebug( QString("Captures: |%1|").arg(ncap) );

            if( ncap > -1 )
            {
                for ( int i = 0; i < nfields; i++)
                {
                    QString listi = list[i];
                    int ii = i+1;
                    c2bDebug( QString("Fields in Template %1: |%2|").arg(i).arg(rx.cap( ii )) );
                    if( listi.contains( REfield ) )
                    {
                        if ( listi == "author" )
                            // Reminder: "addauthors" requires to init RefFields[ "author" ]
                            RefFields[ listi ]->setText(PProc( "addauthors", rx.cap( ii )));
                        else if ( listi == "editor" )
                            // Reminder: "addeditor" requires to init RefFields[ "editor" ]
                            RefFields[ listi ]->setText(PProc( "addeditor", rx.cap( ii )));
                        else if ( listi == "title" )
                            // Reminder: "addtitle" requires to init RefFields[ "title" ]
                            RefFields[ listi ]->setText(PProc( "addtitle", rx.cap( ii )));
                        else
                            RefFields[ listi ]->setText(PProc( listi, rx.cap( ii )));
                    }
                }
                setRefType( reftype );
                c2bAutoRecogStr = QString("Processed as '%1'.").arg(line);
                m_auto_regc = TRUE;
                c2b->statusBar()->message( c2bAutoRecogStr );
                file.close();
                return;
            }
        }
    }
    file.close();

    /*! \page bibproc

    If the automatic recognition engine fails, optionally, a heuristic guessing
    will be performed. See \ref heuristic_guess.

    */

    // Heuristic Bib Parsing

    if( s->m_heuristic_guess )
    {
        guessAbstract( text );
        guessKeywords( text );
        guessVolume( text );
        guessNumber( text );
        guessPages( text );
        guessYear( text );
        guessTitle( text );
        guessISBN( text );
        guessJournal( text );
        c2bAutoRecogStr = tr("Applied %1 filters: No automatic format detection. %2 fields guessed.")
                          .arg(nfilts).arg(countFields());
    }
    else
        c2bAutoRecogStr = tr("Applied %1 filters: No automatic format detection.").arg(nfilts);
    c2b->statusBar()->message( c2bAutoRecogStr, MESSAGE_TIME );
}

void c2bBibParser::guessItem( QString text )
{
    guessAbstract( text );
    guessKeywords( text );
    guessVolume( text );
    guessNumber( text );
    guessPages( text );
    guessYear( text );
    guessTitle( text );
    guessISBN( text );
    guessJournal( text );
    c2bAutoRecogStr = tr("%2 fields guessed.").arg(countFields());
    c2b->statusBar()->message( c2bAutoRecogStr, MESSAGE_TIME );
}

void c2bBibParser::guessAbstract( const QString& text )
{
    /*! \page heuristic_guess Field Recognition Rules

    - <b>Abstract</b>
      - If <tt>Abstract:{0,1}</tt> is found.

    */
    QRegExp rxH( "<NewLine\\d+>\\s*Abstract:{0,1}\\s*<NewLine\\d+>(.+)(<NewLine|$)", FALSE );
    rxH.setMinimal( TRUE );
    int nH = rxH.search( text );
    if( nH > -1 )
    {
        QString val = rxH.cap( 1 ).remove( QRegExp("^:") );
        c2b->abstract->setText(PProc( "abstract", val ));
        return;
    }
    rxH = QRegExp( "<NewLine\\d+>\\s*Abstract:{0,1}(.+)(<NewLine|$)", FALSE );
    rxH.setMinimal( TRUE );
    nH = rxH.search( text );
    if( nH > -1 )
    {
        QString val = rxH.cap( 1 ).remove( QRegExp("^:") );
        c2b->abstract->setText(PProc( "abstract", val ));
    }
}

void c2bBibParser::guessKeywords( const QString& text )
{
    /*! \page heuristic_guess

    - <b>Keywords</b>
      - If <tt>Key\\s{0,1}words:{0,1}</tt> is found.

    */
    QRegExp rxH( "<NewLine\\d+>\\s*Key\\s{0,1}words:{0,1}(.+)(<NewLine|$)", FALSE );
    rxH.setMinimal( TRUE );
    int nH = rxH.search( text );
    if( nH > -1 )
    {
        QString val = rxH.cap( 1 ).remove( QRegExp("^:") );
        c2b->keywords->setText(PProc( "keywords", val ));
    }
}

void c2bBibParser::guessVolume( const QString& text )
{
    /*! \page heuristic_guess

    - <b>Volume</b>
      - If <tt>Volume:{0,1}</tt> is found.
      - If <tt>Vol.{0,1}</tt> is found.
      - If <tt>\\b(\\d+)\\s*\\(\\d+\\)</tt> is found.

    */
    QRegExp rxH( "Volume:{0,1}\\s*(\\d+)", FALSE );
    int nH = rxH.search( text );
    if( nH > -1 )
    {
        c2b->volume->setText(PProc( "volume", rxH.cap( 1 ) ));
        return;
    }
    rxH = QRegExp( "Vol\\.{0,1}\\s*(\\d+)", FALSE );
    nH = rxH.search( text );
    if( nH > -1 )
    {
        c2b->volume->setText(PProc( "volume", rxH.cap( 1 ) ));
        return;
    }
    rxH = QRegExp( "\\b(\\d+)\\s*\\(\\d+\\)" );
    nH = rxH.search( text );
    if( nH > -1 )
        c2b->volume->setText(PProc( "volume", rxH.cap( 1 ) ));
}

void c2bBibParser::guessNumber( const QString& text )
{
    /*! \page heuristic_guess

    - <b>Number</b>
      - If <tt>Number:{0,1}</tt> is found.
      - If <tt>No\\.{0,1}\\s*(\\d+)</tt> is found.
      - If <tt>Issue\\:{0,1}\\s*(\\d+)</tt> is found.
      - If <tt>\\d\\s*\\((\\d+)\\)</tt> is found.

    */
    QRegExp rxH( "Number\\:{0,1}\\s*(\\d+)", FALSE );
    int nH = rxH.search( text );
    if( nH > -1 )
    {
        c2b->number->setText(PProc( "number", rxH.cap( 1 ) ));
        return;
    }
    rxH = QRegExp( "No\\.{0,1}\\s*(\\d+)", FALSE );
    nH = rxH.search( text );
    if( nH > -1 )
    {
        c2b->number->setText(PProc( "number", rxH.cap( 1 ) ));
        return;
    }
    rxH = QRegExp( "Issue\\:{0,1}\\s*(\\d+)", FALSE );
    nH = rxH.search( text );
    if( nH > -1 )
    {
        c2b->number->setText(PProc( "number", rxH.cap( 1 ) ));
        return;
    }
    rxH = QRegExp( "\\d\\s*\\((\\d+)\\)" );
    nH = rxH.search( text );
    if( nH > -1 )
    {
        c2b->number->setText(PProc( "number", rxH.cap( 1 ) ));
        return;
    }
}

void c2bBibParser::guessPages( const QString& text )
{
    /*! \page heuristic_guess

    - <b>Pages</b>
      - If <tt>\\b(\\d+)\\s*-\\s*(\\d+)\\b</tt> is found.
      - If <tt>\\bp{1,2}\\.{0,1}\\s+(\\d+)</tt> is found.

    */
    QRegExp rxH( "\\b(\\d+)\\s*-\\s*(\\d+)\\b" );
    int nH = 0;
    while ( nH >= 0 )
    {
        nH = rxH.search( text, nH );
        if ( nH > -1 )
        {
            if( !rxH.cap( 1 ).startsWith("0") )
            {
                int fp = rxH.cap( 1 ).toInt();
                int lp = rxH.cap( 2 ).toInt();
                if( lp-fp < 150 )
                {
                    c2b->pages->setText(PProc( "pages", rxH.cap( 1 )+"-"+rxH.cap( 2 ) ));
                    return;
                }
            }
            nH  += rxH.matchedLength();
        }
    }
    rxH = QRegExp( "\\bp{1,2}\\.{0,1}\\s+(\\d+)" );
    nH = rxH.search( text );
    if( nH > -1 )
        c2b->pages->setText(PProc( "pages", rxH.cap( 1 ) ));
}

void c2bBibParser::guessYear( const QString& text )
{
    /*! \page heuristic_guess

    - <b>Year</b>
      - If <tt>\\b(19|20)(\\d\\d)\\b</tt> is found.

    */
    QRegExp rxH( "\\b(19|20)(\\d\\d)\\b" );
    int nH = rxH.search( text );
    if( nH > -1 )
        c2b->year->setText(PProc( "year", rxH.cap( 1 )+rxH.cap( 2 ) ));
}

void c2bBibParser::guessTitle( const QString& text )
{
    /*! \page heuristic_guess

    - <b>Title</b>
      - If <tt>\\bTitle:{0,1}</tt> is found.

    */
    QRegExp rxH( "\\bTitle:{0,1}\\s*<NewLine\\d+>(.+)(<NewLine|$)", FALSE );
    rxH.setMinimal( TRUE );
    int nH = rxH.search( text );
    if( nH > -1 )
    {
        QString val = rxH.cap( 1 ).remove( QRegExp("^:") );
        c2b->title->setText(PProc( "title", val ));
        return;
    }
    rxH = QRegExp( "\\bTitle:{0,1}(.+)(<NewLine|$)", FALSE );
    rxH.setMinimal( TRUE );
    nH = rxH.search( text );
    if( nH > -1 )
    {
        QString val = rxH.cap( 1 ).remove( QRegExp("^:") );
        c2b->title->setText(PProc( "title", val ));
    }
}

void c2bBibParser::guessISBN( const QString& text )
{
    /*! \page heuristic_guess

    - <b>ISBN</b>
      - If <tt>\\bISBN\\b:{0,1}\\s*(\\d+-[\\d-]+-\\d+)</tt> is found.

    */
    QRegExp rxH( "\\bISBN\\b:{0,1}\\s*(\\d+-[\\d-]+-\\d+)", FALSE );
    int nH = rxH.search( text );
    if( nH > -1 )
        c2b->isbn->setText(PProc( "isbn", rxH.cap( 1 ) ));
}

void c2bBibParser::guessJournal( QString text )
{
    /*! \page heuristic_guess

    - <b>Journal</b>
      - Check cb2Bib internal database.

    */
    text.replace( QRegExp( "\\W" ), "" );
    QString jn = "";
    for ( int i = 0; i < JDB->nitems; i++ )
    {
        if( text.contains( JDB->JExtended_simp[i], FALSE ) )
        {
            if( jn.length() < JDB->JExtended_simp[i].length() )
                jn = JDB->JExtended_simp[i];
        }
    }
    if( !jn.isEmpty() )
    {
        c2b->journal->setText(PProc( "journal", jn ));
        return;
    }
    for ( int i = 0; i < JDB->nitems; i++ )
    {
        if( text.contains( JDB->JAbbrev_simp[i], FALSE ) )
        {
            if( jn.length() < JDB->JAbbrev_simp[i].length() )
                jn = JDB->JAbbrev_simp[i];
        }
    }
    if( !jn.isEmpty() )
        c2b->journal->setText(PProc( "journal", jn ));
}

void c2bBibParser::setFieldWidgetMap()
{
    RefFields.insert( "abstract", c2b->abstract );
    RefFields.insert( "address", c2b->address );
    RefFields.insert( "annote", c2b->annote );
    RefFields.insert( "author", c2b->authors );
    RefFields.insert( "booktitle", c2b->booktitle );
    RefFields.insert( "chapter", c2b->chapter );
    RefFields.insert( "doi", c2b->doi );
    RefFields.insert( "edition", c2b->edition );
    RefFields.insert( "editor", c2b->editor );
    RefFields.insert( "eprint", c2b->eprint );
    RefFields.insert( "file", c2b->pdf );
    RefFields.insert( "institution", c2b->institution );
    RefFields.insert( "isbn", c2b->isbn );
    RefFields.insert( "issn", c2b->issn );
    RefFields.insert( "journal", c2b->journal );
    RefFields.insert( "keywords", c2b->keywords );
    RefFields.insert( "month", c2b->month );
    RefFields.insert( "note", c2b->note );
    RefFields.insert( "number", c2b->number );
    RefFields.insert( "organization", c2b->organization );
    RefFields.insert( "pages", c2b->pages );
    RefFields.insert( "publisher", c2b->publisher );
    RefFields.insert( "school", c2b->school );
    RefFields.insert( "series", c2b->series );
    RefFields.insert( "title", c2b->title );
    RefFields.insert( "url", c2b->html );
    RefFields.insert( "volume", c2b->volume );
    RefFields.insert( "year", c2b->year );

    // Tracking Field Editor Line changes
    QStringList::Iterator it = fieldl.begin();
    while ( it != fieldl.end() )
    {
        connect( RefFields[ *it ], SIGNAL( textChanged (const QString &) ), this, SIGNAL( bibModified() ) );
        it++;
    }
    connect( c2b->id, SIGNAL( textChanged (const QString &) ), this, SIGNAL( bibModified() ) );
    connect( c2b->typecombo, SIGNAL( textChanged (const QString &) ), this, SIGNAL( bibModified() ) );
}

int c2bBibParser::countFields()
{
    // Counting Non Empty Fields
    QStringList::Iterator it = fieldl.begin();
    int n = 0;
    while ( it != fieldl.end() )
    {
        if( !RefFields[ *it ]->text().isEmpty() )
            n++;
        it++;
    }
    return ( n );
}
