/***************************************************************************
*   Copyright (C) 2004-2009 by Thomas Fischer                             *
*   fischer@unix-ag.uni-kl.de                                             *
*                                                                         *
*   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.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/
#include <tqregexp.h>
#include <tqstring.h>
#include <tqstringlist.h>

#include "value.h"

namespace BibTeX
{

    ValueTextInterface::ValueTextInterface( const ValueTextInterface* other ) : m_text( other->text() )
    {
// nothing
    }

    ValueTextInterface::ValueTextInterface( const TQString& text ) : m_text( text )
    {
// nothing
    }

    void ValueTextInterface::setText( const TQString& text )
    {
        m_text = text;
    }

    TQString ValueTextInterface::text() const
    {
        return m_text;
    }

    TQString ValueTextInterface::simplifiedText() const
    {
        return text().replace( TQRegExp( "\\\\[A-Za-z0-9]+" ), "" ).replace( '{', "" ).replace( '}', "" );
    }

    void ValueTextInterface::replace( const TQString &before, const TQString &after )
    {
        if ( before == text() || before == simplifiedText() )
            setText( after );
    }

    bool ValueTextInterface::containsPattern( const TQString &pattern, bool caseSensitive )
    {
        return text().contains( pattern, caseSensitive ) || simplifiedText().contains( pattern, caseSensitive );
    }

    ValueItem::ValueItem( const TQString& text ) : ValueTextInterface( text )
    {
// nothing
    }

    Keyword::Keyword( Keyword *other ) : ValueTextInterface( other )
    {
// nothing
    }

    Keyword::Keyword( const TQString& text ) : ValueTextInterface( text )
    {
// nothing
    }

    Keyword *Keyword::clone()
    {
        return new Keyword( text() );
    }

    KeywordContainer::KeywordContainer(): ValueItem( "" )
    {
// nothing
    }

    KeywordContainer::KeywordContainer( const TQString& text ) : ValueItem( text )
    {
        setText( text );
    }

    KeywordContainer::KeywordContainer( KeywordContainer *other ) : ValueItem( TQString::null )
    {
        for ( TQValueList<Keyword*>::Iterator it = other->keywords.begin(); it != other->keywords.end(); ++it )
            keywords.append(( *it )->clone() );
    }

    KeywordContainer::KeywordContainer( const TQStringList& list ) : ValueItem( TQString::null )
    {
        setList( list );
    }

    ValueItem *KeywordContainer::clone()
    {
        return new KeywordContainer( this );
    }

    void KeywordContainer::setList( const TQStringList& list )
    {
        keywords.clear();
        for ( TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it )
            keywords.append( new Keyword( *it ) );
    }

    void KeywordContainer::append( const TQString& text )
    {
        bool contains = false;

        for ( TQValueList<Keyword*>::ConstIterator it = keywords.begin(); !contains && it != keywords.end(); ++it )
            contains = TQString::compare(( *it )->text(), text ) == 0;

        if ( contains == 0 )
            keywords.append( new Keyword( text ) );
    }

    void KeywordContainer::remove( const TQString& text )
    {
        bool contains = false;
        for ( TQValueList<Keyword*>::Iterator it = keywords.begin(); !contains && it != keywords.end(); ++it )
            if ( TQString::compare(( *it )->text(), text ) == 0 )
            {
                keywords.remove( it );
                break;
            }
    }

    void KeywordContainer::setText( const TQString& text )
    {
        ValueItem::setText( text );

        TQRegExp splitRegExp;
        if ( text.contains( ";" ) )
            splitRegExp = TQRegExp( "\\s*;\\s*" );
        else
            splitRegExp = TQRegExp( "\\s*,\\s*" );

        keywords.clear();
        TQStringList keywordList = TQStringList::split( splitRegExp, text, false );
        for ( TQStringList::ConstIterator it = keywordList.begin(); it != keywordList.end(); ++it )
            keywords.append( new Keyword( *it ) );
    }

    TQString KeywordContainer::text() const
    {
        TQString result;
        bool first = true;
        for ( TQValueList<Keyword*>::ConstIterator it = keywords.begin(); it != keywords.end(); ++it )
        {
            if ( !first )
                result.append( "; " );
            else first = false;
            result.append(( *it )->text() );
        }
        return result;
    }

    void KeywordContainer::replace( const TQString &before, const TQString &after )
    {
        for ( TQValueList<Keyword*>::ConstIterator it = keywords.begin(); it != keywords.end(); ++it )
            ( *it )->replace( before, after );
    }

    Person::Person( const TQString& text, bool firstNameFirst ) : ValueTextInterface( text ), m_firstNameFirst( firstNameFirst )
    {
        setText( text );
    }

    Person::Person( const TQString& firstName, const TQString& lastName, bool firstNameFirst ) : ValueTextInterface( TQString( firstName ).append( " " ).append( lastName ) ), m_firstName( firstName ), m_lastName( lastName ), m_firstNameFirst( firstNameFirst )
    {
// nothing
    }

    Person *Person::clone()
    {
        return new Person( m_firstName, m_lastName, m_firstNameFirst );
    }

    void Person::setText( const TQString& text )
    {
        ValueTextInterface::setText( text );

        TQStringList segments;
        bool containsComma = splitName( text, segments );
        m_firstName = "";
        m_lastName = "";

        if ( segments.isEmpty() )
            return;

        if ( !containsComma )
        {
            /** PubMed uses a special writing style for names, where the last name is followed by single capital letter, each being the first letter of each first name */
            /** So, check how many single capital letters are at the end of the given segment list */
            int singleCapitalLettersCounter = 0;
            int p = segments.count() - 1;
            while ( segments[p].length() == 1 && segments[p].compare( segments[p].upper() ) == 0 )
            {
                --p;
                ++singleCapitalLettersCounter;
            }

            if ( singleCapitalLettersCounter > 0 )
            {
                /** this is a special case for names from PubMed, which are formatted like "Fischer T" */
                /** all segment values until the first single letter segment are last name parts */
                for ( int i = 0; i < p; ++i )
                    m_lastName.append( segments[i] ).append( " " );
                m_lastName.append( segments[p] );
                /** single letter segments are first name parts */
                for ( unsigned int i = p + 1; i < segments.count() - 1; ++i )
                    m_firstName.append( segments[i] ).append( " " );
                m_firstName.append( segments[segments.count() - 1] );
            }
            else
            {
                int from = segments.count() - 1;
                m_lastName = segments[from];
                /** check for lower case parts of the last name such as "van", "von", "de", ... */
                while ( from > 0 )
                {
                    if ( segments[from - 1].compare( segments[from - 1].lower() ) != 0 )
                        break;
                    --from;
                    m_lastName.prepend( " " );
                    m_lastName.prepend( segments[from] );
                }

                if ( from > 0 )
                {
                    /** there are segments left for the first name */
                    m_firstName = *segments.begin();
                    for ( TQStringList::Iterator it = ++segments.begin(); from > 1; ++it, --from )
                    {
                        m_firstName.append( " " );
                        m_firstName.append( *it );
                    }
                }
            }
        }
        else
        {
            bool inLastName = true;
            for ( unsigned int i = 0; i < segments.count();++i )
            {
                if ( segments[i] == "," )
                    inLastName = false;
                else if ( inLastName )
                {
                    if ( !m_lastName.isEmpty() ) m_lastName.append( " " );
                    m_lastName.append( segments[i] );
                }
                else
                {
                    if ( !m_firstName.isEmpty() ) m_firstName.append( " " );
                    m_firstName.append( segments[i] );
                }
            }
        }
    }

    TQString Person::text() const
    {
        return text( m_firstNameFirst );
    }

    TQString Person::text( bool firstNameFirst ) const
    {

        if ( m_firstName.isEmpty() )
            return m_lastName;
        else
            return firstNameFirst ? m_firstName + " " + m_lastName : m_lastName + ", " + m_firstName;
    }

    TQString Person::firstName()
    {
        return m_firstName;
    }
    TQString Person::lastName()
    {
        return m_lastName;
    }

    /** Splits a name into single words. If the name's text was reversed (Last, First), the result will be true and the comma will be added to segments. Otherwise the functions result will be false. This function respects protecting {...}. */
    bool Person::splitName( const TQString& text, TQStringList& segments )
    {
        int bracketCounter = 0;
        bool result = false;
        TQString buffer = "";

        for ( unsigned int pos = 0; pos < text.length(); ++pos )
        {
            if ( text[pos] == '{' )
                ++bracketCounter;
            else if ( text[pos] == '}' )
                --bracketCounter;

            if ( text[pos] == ' ' && bracketCounter == 0 )
            {
                if ( !buffer.isEmpty() )
                {
                    segments.append( buffer );
                    buffer = "";
                }
            }
            else if ( text[pos] == ',' && bracketCounter == 0 )
            {
                if ( !buffer.isEmpty() )
                {
                    segments.append( buffer );
                    buffer = "";
                }
                segments.append( "," );
                result = true;
            }
            else
                buffer.append( text[pos] );
        }

        if ( !buffer.isEmpty() )
            segments.append( buffer );

        return result;
    }

    PersonContainer::PersonContainer( bool firstNameFirst ) : ValueItem( TQString::null ), m_firstNameFirst( firstNameFirst )
    {
        // nothing
    }

    PersonContainer::PersonContainer( const TQString& text, bool firstNameFirst ) : ValueItem( text ), m_firstNameFirst( firstNameFirst )
    {
        persons.append( new Person( text, m_firstNameFirst ) );
    }

    ValueItem *PersonContainer::clone()
    {
        PersonContainer *result = new PersonContainer( m_firstNameFirst );
        for ( TQValueList<Person*>::ConstIterator it = persons.begin(); it != persons.end(); ++it )
            result->persons.append(( *it )->clone() );

        return result;
    }

    void PersonContainer::setText( const TQString& text )
    {
        ValueTextInterface::setText( text );
        tqDebug( "You cannot set a text ('%s') to a Value object", text.latin1() );
    }

    TQString PersonContainer::text() const
    {
        TQString result;
        bool first = true;

        for ( TQValueList<Person*>::ConstIterator it = persons.begin(); it != persons.end(); ++it )
        {
            if ( !first )
                result.append( " and " );
            else
                first = false;
            result.append(( *it )->text() );
        }

        return result;
    }

    void PersonContainer::replace( const TQString &before, const TQString &after )
    {
        for ( TQValueList<Person*>::ConstIterator it = persons.begin(); it != persons.end(); ++it )
            ( *it )->replace( before, after );
    }

    MacroKey::MacroKey( const TQString& text ) : ValueItem( text )
    {
        m_isValid = isValidInternal();
    }

    ValueItem *MacroKey::clone()
    {
        return new MacroKey( text() );
    }

    void MacroKey::setText( const TQString& text )
    {
        ValueItem::setText( text );
        m_isValid = isValidInternal();
    }

    bool MacroKey::isValid()
    {
        return m_isValid;
    }

    bool MacroKey::isValidInternal()
    {
        return !text().contains( TQRegExp( "![-.:/+_a-zA-Z0-9]" ) );
    }

    PlainText::PlainText( const TQString& text ) : ValueItem( text )
    {
        // nothing
    }

    ValueItem *PlainText::clone()
    {
        return new PlainText( text() );
    }

    Value::Value() : ValueTextInterface( TQString::null )
    {
        // nothing
    }

    Value::Value( const Value *other ) : ValueTextInterface( other )
    {
        for ( TQValueList<ValueItem*>::ConstIterator it = other->items.begin(); it != other->items.end(); ++it )
            items.append(( *it )->clone() );
    }

    Value::Value( const TQString& text, bool isMacroKey ): ValueTextInterface( text )
    {
        ValueItem *item = NULL;
        if ( isMacroKey )
            item = new MacroKey( text );
        else
            item = new PlainText( text );
        items.append( item );
    }

    void Value::setText( const TQString& text )
    {
        ValueTextInterface::setText( text );
        tqDebug( "You cannot set a text ('%s') to a Value object", text.latin1() );
    }

    TQString Value::text() const
    {
        TQString result;

        for ( TQValueList<ValueItem*>::ConstIterator it = items.begin(); it != items.end(); ++it )
            result.append(( *it )->text() );

        return result;
    }

    void Value::replace( const TQString &before, const TQString &after )
    {
        for ( TQValueList<ValueItem*>::ConstIterator it = items.begin(); it != items.end(); ++it )
            ( *it )->replace( before, after );
    }
}
