/***************************************************************************
 *   Copyright (C) 2004-2017 by Pere Constans
 *   constans@molspaces.com
 *   cb2Bib version 1.9.7. Licensed under the GNU GPL version 3.
 *   See the LICENSE file that comes with this distribution.
 *
 *   Improvements and modifications:
 *   July 2009 - Added <<author_all_abbreviated>>, (C) 2009 by Dayu Huang
 ***************************************************************************/
#include "idMaker.h"

#include "cb2bib_utilities.h"
#include "settings.h"


idMaker::idMaker(const QString& patternKey, QObject* parento) : QObject(parento), _pattern_key(patternKey)
{
    loadSettings();
    connect(settings::instance(), SIGNAL(newSettings()), this, SLOT(loadSettings()));
}


/** \page idplaceholders Predefined cite and document ID placeholders

    - <tt><<author_all_abbreviated>></tt> Takes first three letters of the last
    word of all authors's last name in cite, and converts to lowercase.

    - <tt><<author_all_initials>></tt> Takes capitalized initials of all
    authors in cite.

    - <tt><<author_first>></tt> Takes first author last name/s.

    - <tt><<citeid>></tt> This placeholder is meant to be used <b>alone, and
    only for document IDs</b>. It takes the pattern defined for the cite ID. If
    the cite ID is modified manually, the document ID is synchronized
    automatically.

    - <tt><<journal_initials>></tt> Takes capitalized initials of journal name.

    - <tt><<pages_first>></tt> First page.

    - <tt><<ppages_first>></tt> First page, written as, e. g., 'p125'.

    - <tt><<title>></tt> Title. To truncate titles exceeding a maximum length
    <tt>l</tt> use <<title_l>>, where <tt>l</tt> stands for an integer value.

    - <tt><<title_underscored>></tt> Title with blanks set to underscores. To
    truncate title to <tt>l</tt> characters use <<title_underscored_l>>.

    - <tt><<volume>></tt> Volume number.

    - <tt><<year_abbreviated>></tt> Last two digits from year.

    - <tt><<year_full>></tt> All digits from year.


    <b>Note:</b> If <tt>author</tt> is empty, <tt>editor</tt> will be
    considered instead. On conference proceedings or monographs this situation
    is usual. Similarly, if <tt>title</tt> is empty, <tt>booktitle</tt> is
    considered.

    <b>Note:</b> Only one placeholder of a given field, e. g. <<author_first>>
    or <<author_all_initials>>, should be used to compose the ID patterns. The
    cb2Bib only performs one substitution per field placeholder.

    <b>Note:</b> The cb2Bib performs a series of string manipulations, such as
    stripping diacritics and ligatures, aimed to provide ID values suitable for
    BibTeX keys and platform independent filenames. Currently only ASCII
    characters are considered.

*/
QString idMaker::makeID(const bibReference& reference)
{
    if (_id_pattern.isEmpty())
        return QString();
    if (_id_pattern.contains("<<citeid>>"))
        return reference.citeidName;

    // Initialize fields
    _author = reference.anyAuthor();
    _journal = reference.value("journal");
    _pages = reference.value("pages");
    _title = reference.anyTitle();
    _volume = reference.value("volume");
    _year = reference.value("year");

    // Set cite ID
    QString id(_id_pattern);
    if (_id_pattern.contains("<<author_first>>"))
        make_author_first(&id);
    else if (_id_pattern.contains("<<author_all_abbreviated>>"))
        make_author_all_abbreviated(&id);
    else if (_id_pattern.contains("<<author_all_initials>>"))
        make_author_all_initials(&id);

    if (_id_pattern.contains("<<journal_initials>>"))
        make_journal_initials(&id);

    if (_id_pattern.contains("<<pages_first>>"))
        make_pages_first(&id);
    else if (_id_pattern.contains("<<ppages_first>>"))
        make_ppages_first(&id);

    if (!_title_pattern.isEmpty())
        if (_id_pattern.contains(_title_pattern))
            make_title(&id);

    if (_id_pattern.contains("<<volume>>"))
        make_volume(&id);

    if (_id_pattern.contains("<<year_abbreviated>>"))
        make_year_abbreviated(&id);
    else if (_id_pattern.contains("<<year_full>>"))
        make_year_full(&id);

    return id;
}

void idMaker::make_author_first(QString* id)
{
    QRegExp rx("([-'\\s\\w]+)(?:\\sand|$)");
    rx.setMinimal(true);
    rx.indexIn(_author);
    _author = rx.cap(1);
    if (_author.contains(c2bUtils::nonAsciiLetter))
        _author = c2bUtils::toAscii(_author, c2bUtils::Cleanup);
    id->replace("<<author_first>>", _author);
}

void idMaker::make_author_all_abbreviated(QString* id)
{
    // If there is less than 3 letters in their last name's last word,
    // then use all the letters in the last name's last word
    QString temp_author;
    QRegExp rx("([-'\\w]{1,3})(?:[-'\\w]*)(?:\\sand|$)");
    rx.setMinimal(true);
    rx.indexIn(_author);
    int pos(0);
    while ((pos = rx.indexIn(_author, pos)) != -1)
    {
        temp_author += rx.cap(1);
        pos += rx.matchedLength();
    }
    _author = temp_author.toLower();
    if (_author.contains(c2bUtils::nonAsciiLetter))
        _author = c2bUtils::toAscii(_author, c2bUtils::Cleanup);
    id->replace("<<author_all_abbreviated>>", _author);
}

void idMaker::make_author_all_initials(QString* id)
{
    _author.remove(QRegExp("\\b\\w\\b"));
    _author.remove(" and ");
    if (_author.contains(c2bUtils::nonAsciiLetter))
        _author = c2bUtils::toAscii(_author, c2bUtils::Cleanup);
    _author.remove(QRegExp("[a-z]"));
    id->replace("<<author_all_initials>>", _author);
}

void idMaker::make_journal_initials(QString* id)
{
    _journal.remove(QRegExp("[^A-Z]"));
    id->replace("<<journal_initials>>", _journal);
}

void idMaker::make_pages_first(QString* id)
{
    _pages = c2bUtils::firstPage(_pages);
    id->replace("<<pages_first>>", _pages);
}

void idMaker::make_ppages_first(QString* id)
{
    _pages = c2bUtils::firstPage(_pages);
    if (!_pages.isEmpty())
        if (_pages.at(0).isDigit())
            _pages = 'p' + _pages;
    id->replace("<<ppages_first>>", _pages);
}

void idMaker::make_title(QString* id)
{
    if (_title.contains(c2bUtils::nonAsciiLetter))
        _title = c2bUtils::toAscii(_title, c2bUtils::FromBibTeX);
    _title = _title.left(_title_max_length).trimmed(); // Avoid possible trailing blank
    if (_is_title_underscored)
        _title.replace(' ', '_');
    id->replace(_title_pattern, _title);
}

void idMaker::make_volume(QString* id)
{
    _volume.remove(' ');
    id->replace("<<volume>>", _volume);
}

void idMaker::make_year_abbreviated(QString* id)
{
    _year = _year.right(2);
    id->replace("<<year_abbreviated>>", _year);
}

void idMaker::make_year_full(QString* id)
{
    id->replace("<<year_full>>", _year);
}

void idMaker::loadSettings()
{
    _id_pattern = settings::instance()->value(_pattern_key).toString();
    QRegExp title_pattern("(<<title(?:_underscored)?(?:_\\d+)?>>)");
    if (title_pattern.indexIn(_id_pattern) > -1)
    {
        _title_pattern = title_pattern.cap(1);
        _title_pattern.remove(QRegExp("\\D"));
        _title_max_length = _title_pattern.toInt();
        if (_title_max_length == 0)
            _title_max_length = -1;
        _title_pattern = title_pattern.cap(1);
        _is_title_underscored = _title_pattern.contains(QRegExp("_underscored(?:_\\d+)?>>"));
    }
    else
        _title_pattern.clear();
}
