/***************************************************************************
 *   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 <cmath>

#include <qlayout.h>
#include <qlabel.h>
#include <qprogressbar.h>
#include <qtimer.h>

#include <klocale.h>
#include <kpushbutton.h>
#include <kapplication.h>
#include <kmessagebox.h>
#include <ktextedit.h>
#include <klistview.h>
#include <kconfig.h>
#include <kwin.h>

#include <macro.h>
#include <comment.h>
#include <preamble.h>
#include <macrowidget.h>
#include <entrywidget.h>
#include <preamblewidget.h>
#include <settings.h>
#include <fileexporterbibtex.h>
#include "mergeelements.h"

namespace KBibTeX
{
    MergeElementsCliqueItem::MergeElementsCliqueItem( BibTeX::Entry* _entry, BibTeX::Macro* _macro, BibTeX::Preamble* _preamble, QListView *parent )
            : QCheckListItem( parent, _entry == NULL ?( _macro == NULL ? _preamble->value()->text() : _macro->key() ) : _entry->id(), QCheckListItem::CheckBox ), entry( _entry ), macro( _macro ), preamble( _preamble )
    {
        // nothing
    }

    void MergeElementsCliqueItem::stateChange( bool )
    {
        emit stateChanged( this );
    };

    MergeEntriesAlternativesController::MergeEntriesAlternativesController( const QString &label, QListView *parent )
            : QCheckListItem( parent, label, QCheckListItem::RadioButtonController ), fieldType( BibTeX::EntryField::ftUnknown ), fieldName( label )
    {
        // nothing
    }

    MergeEntriesAlternativesController::MergeEntriesAlternativesController( BibTeX::EntryField::FieldType _fieldType, QListView *parent )
            : QCheckListItem( parent, BibTeX::EntryField::fieldTypeToString( _fieldType ), QCheckListItem::RadioButtonController ), fieldType( _fieldType ), fieldName( BibTeX::EntryField::fieldTypeToString( _fieldType ) )
    {
        // nothing
    }

    MergeMacrosAlternativesController::MergeMacrosAlternativesController( bool isKey, QListView *parent )
            : QCheckListItem( parent, isKey ? i18n( "Key" ) : i18n( "Value" ), QCheckListItem::RadioButtonController )
    {
        // nothing
    }

    MergeEntriesAlternativesItem::MergeEntriesAlternativesItem( BibTeX::EntryField *_field, MergeEntriesAlternativesController *parent )
            : QCheckListItem( parent, _field->value()->text(), QCheckListItem::RadioButton ), field( _field )
    {
        // nothing
    }

    MergeMacrosAlternativesItem::MergeMacrosAlternativesItem( BibTeX::Value *_value, MergeMacrosAlternativesController *parent )
            : QCheckListItem( parent, _value->text(), QCheckListItem::RadioButton ), value( _value )
    {
        // nothing
    }

    MergeElementsAlternativesId::MergeElementsAlternativesId( const QString & _id, MergeEntriesAlternativesController *parent ) : QCheckListItem( parent, _id, QCheckListItem::RadioButton ), id( _id )
    {
        // nothing
    }

    MergeMacroAlternativesKey::MergeMacroAlternativesKey( const QString & _key, MergeMacrosAlternativesController *parent ) : QCheckListItem( parent, _key, QCheckListItem::RadioButton ), key( _key )
    {
        // nothing
    }

    MergeEntriesAlternativesEntryType::MergeEntriesAlternativesEntryType( const QString & _typeString, MergeEntriesAlternativesController *parent )
            : QCheckListItem( parent, _typeString, QCheckListItem::RadioButton ), typeString( _typeString ), type( BibTeX::Entry::entryTypeFromString( _typeString ) )
    {
        // nothing
    }

    MergeEntriesAlternativesEntryType::MergeEntriesAlternativesEntryType( BibTeX::Entry::EntryType _type, MergeEntriesAlternativesController *parent )
            : QCheckListItem( parent, BibTeX::Entry::entryTypeToString( _type ), QCheckListItem::RadioButton ), typeString( BibTeX::Entry::entryTypeToString( _type ) ), type( _type )
    {
        // nothing
    }

    MergePreambleAlternativesController::MergePreambleAlternativesController( QListView *parent )
            : QCheckListItem( parent, i18n( "Preamble text" ), QCheckListItem::RadioButtonController )
    {
        // nothing
    }

    MergePreambleAlternatives::MergePreambleAlternatives( const QString &_text, MergePreambleAlternativesController *parent )
            :QCheckListItem( parent, _text, QCheckListItem::RadioButton ), text( _text )
    {
        // nothing
    }

    MergeElements::MergeElements( QWidget *parent )
            : KDialogBase( parent, "MergeElements", true, "undefined", Ok | Cancel | User1 | User2, User1, true, KGuiItem( i18n( "Next" ), "next" ), KGuiItem( i18n( "Previous" ), "previous" ) ), m_currentCliqueIndex( 0 )
    {
        setupGUI();
    }

    MergeElements::~MergeElements()
    {
        KConfig * config = kapp->config();
        config->setGroup( "MergeElements" );
        saveWindowSize( config );
    }

    void MergeElements::setupGUI()
    {
        QWidget *vboxContainer = new QWidget( this );
        setMainWidget( vboxContainer );
        QBoxLayout *vboxLayout = new QVBoxLayout( vboxContainer, 0, KDialog::spacingHint() );
        vboxLayout->setResizeMode( QLayout::Minimum );

        QLabel *label = new QLabel( i18n( "Select elements to merge. At least two elements must be checked to perform a merge operation. Checked entries will be replaced by the merged element, unchecked elements will be kept." ), vboxContainer );
        label->setAlignment( Qt::WordBreak );
        vboxLayout->addWidget( label );
        m_listViewClique = new KListView( vboxContainer );
        m_listViewClique->addColumn( i18n( "Entry/Macro Id" ) );
        m_listViewClique->setFullWidth( true );
        m_listViewClique->setAllColumnsShowFocus( true );
        vboxLayout->addWidget( m_listViewClique );
        vboxLayout->setStretchFactor( m_listViewClique, 1 );
        label->setBuddy( m_listViewClique );

        m_progressBar = new QProgressBar( vboxContainer );
        vboxLayout->addWidget( m_progressBar );

        vboxLayout->addSpacing( KDialog::spacingHint() * 2 );

        label = new QLabel( i18n( "Choose from this list which alternatives you want to keep in the merged element." ), vboxContainer );
        label->setAlignment( Qt::WordBreak );
        vboxLayout->addWidget( label );
        m_listViewAlternatives = new KListView( vboxContainer );
        m_listViewAlternatives->addColumn( i18n( "Field/Key" ) );
        m_listViewAlternatives->setFullWidth( true );
        m_listViewAlternatives->setAllColumnsShowFocus( true );
        vboxLayout->addWidget( m_listViewAlternatives );
        vboxLayout->setStretchFactor( m_listViewAlternatives, 3 );
        label->setBuddy( m_listViewAlternatives );

        connect( m_listViewClique, SIGNAL( doubleClicked( QListViewItem * ) ), this, SLOT( slotPreviewElement( QListViewItem * ) ) );
        connect( this, SIGNAL( user1Clicked() ), this, SLOT( slotNextClique() ) );
        connect( this, SIGNAL( user2Clicked() ), this, SLOT( slotPreviousClique() ) );
        connect( this, SIGNAL( okClicked() ), this, SLOT( saveCurrentMergeSet() ) );
    }

    void MergeElements::setClique( int cliqueIndex )
    {
        if ( m_currentCliqueIndex != cliqueIndex )
            saveCurrentMergeSet();
        m_currentCliqueIndex = cliqueIndex;

        m_listViewClique->clear();
        FindDuplicates::DuplicateClique clique = m_duplicateCliqueList[cliqueIndex];

        for ( FindDuplicates::DuplicateClique::Iterator it = clique.begin(); it != clique.end(); ++it )
        {
            BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( *it );
            if ( entry != NULL )
            {
                MergeElementsCliqueItem *item = new MergeElementsCliqueItem( entry, NULL, NULL, m_listViewClique );
                connect( item, SIGNAL( stateChanged( MergeElementsCliqueItem* ) ), this, SLOT( slotRefreshAlternatives() ) );
            }
            else
            {
                BibTeX::Macro *macro = dynamic_cast<BibTeX::Macro*>( *it );
                if ( macro != NULL )
                {
                    MergeElementsCliqueItem *item = new MergeElementsCliqueItem( NULL, macro, NULL, m_listViewClique );
                    connect( item, SIGNAL( stateChanged( MergeElementsCliqueItem* ) ), this, SLOT( slotRefreshAlternatives() ) );
                }
                else
                {
                    BibTeX::Preamble *preamble = dynamic_cast<BibTeX::Preamble*>( *it );
                    if ( preamble!=NULL )
                    {
                        MergeElementsCliqueItem *item = new MergeElementsCliqueItem( NULL, NULL, preamble, m_listViewClique );
                        connect( item, SIGNAL( stateChanged( MergeElementsCliqueItem* ) ), this, SLOT( slotRefreshAlternatives() ) );
                    }
                }
            }
        }
        restoreCurrentMergeSet();

        enableButton( User1, ( cliqueIndex < ( int )( m_duplicateCliqueList.size() ) - 1 ) && m_duplicateCliqueList.size() > 1 );
        enableButton( User2, cliqueIndex > 0 && m_duplicateCliqueList.size() > 1 );
        m_progressBar->setProgress( cliqueIndex, m_duplicateCliqueList.size() - 1 );
    }

    void MergeElements::saveCurrentMergeSet()
    {
        if ( m_mergeSetList[m_currentCliqueIndex] == NULL )
            m_mergeSetList[m_currentCliqueIndex] = new MergeSet;
        else
        {
            m_mergeSetList[m_currentCliqueIndex]->entries.clear();
            m_mergeSetList[m_currentCliqueIndex]->fields.clear();
        }
        m_mergeSetList[m_currentCliqueIndex]->type = BibTeX::Entry::etUnknown;
        m_mergeSetList[m_currentCliqueIndex]->typeString = QString::null;
        m_mergeSetList[m_currentCliqueIndex]->id = QString::null;
        m_mergeSetList[m_currentCliqueIndex]->macroKey = QString::null;
        m_mergeSetList[m_currentCliqueIndex]->macroValue = NULL;
        m_mergeSetList[m_currentCliqueIndex]->preambleText=QString::null;

        for ( QListViewItemIterator it( m_listViewClique, QListViewItemIterator::Checked ); it.current(); ++it )
        {
            MergeElementsCliqueItem *eci = dynamic_cast<MergeElementsCliqueItem*>( *it );
            BibTeX::Entry *entry = eci->entry;
            if ( entry != NULL )
                m_mergeSetList[m_currentCliqueIndex]->entries.append( entry );
            BibTeX::Macro *macro = eci->macro;
            if ( macro != NULL )
                m_mergeSetList[m_currentCliqueIndex]->macros.append( macro );
            BibTeX::Preamble *preamble = eci->preamble;
            if ( preamble !=NULL )
                m_mergeSetList[m_currentCliqueIndex]->preambles.append( preamble );
        }

        for ( QListViewItemIterator it( m_listViewAlternatives, QListViewItemIterator::Checked ); it.current(); ++it )
        {
            MergeEntriesAlternativesItem *item = dynamic_cast<MergeEntriesAlternativesItem*>( *it );
            if ( item != NULL )
            {
                BibTeX::EntryField *field = item->field;
                m_mergeSetList[m_currentCliqueIndex]->fields.append( field );
            }
            else
            {
                MergeElementsAlternativesId *item = dynamic_cast<MergeElementsAlternativesId*>( *it );
                if ( item != NULL )
                    m_mergeSetList[m_currentCliqueIndex]->id = item->id;
                else
                {
                    MergeEntriesAlternativesEntryType *itemT = dynamic_cast<MergeEntriesAlternativesEntryType*>( *it );
                    if ( itemT != NULL )
                    {
                        m_mergeSetList[m_currentCliqueIndex]->typeString = itemT->typeString;
                        m_mergeSetList[m_currentCliqueIndex]->type = itemT->type;
                    }
                    else
                    {
                        MergeMacroAlternativesKey *itemK = dynamic_cast<MergeMacroAlternativesKey*>( *it );
                        if ( itemK != NULL )
                            m_mergeSetList[m_currentCliqueIndex]->macroKey = itemK->key;
                        else
                        {
                            MergeMacrosAlternativesItem *itemMA = dynamic_cast<MergeMacrosAlternativesItem*>( *it );
                            if ( itemMA != NULL )
                                m_mergeSetList[m_currentCliqueIndex]->macroValue = itemMA->value;
                            else
                            {
                                MergePreambleAlternatives *itemP=dynamic_cast<MergePreambleAlternatives*>( *it );
                                if ( itemP!=NULL )
                                    m_mergeSetList[m_currentCliqueIndex]->preambleText=itemP->text;
                            }
                        }
                    }
                }
            }
        }
    }

    void MergeElements::restoreCurrentMergeSet()
    {
        if ( m_mergeSetList[m_currentCliqueIndex] == NULL )
        {
            m_listViewAlternatives->clear();
            return;
        }

        for ( QListViewItemIterator it( m_listViewClique ); it.current(); ++it )
        {
            MergeElementsCliqueItem *item = dynamic_cast<MergeElementsCliqueItem*>( *it );
            BibTeX::Entry *entry = item->entry;
            BibTeX::Macro *macro = item->macro;
            BibTeX::Preamble *preamble = item->preamble;
            if ( entry != NULL )
                for ( QValueList<BibTeX::Entry*>::Iterator it2 = m_mergeSetList[m_currentCliqueIndex]->entries.begin(); it2 != m_mergeSetList[m_currentCliqueIndex]->entries.end(); ++it2 )
                {
                    if ( entry->id() == ( *it2 )->id() )
                    {
                        item->setOn( true );
                        break;
                    }
                }
            else if ( macro != NULL )
            {
                for ( QValueList<BibTeX::Macro*>::Iterator it2 = m_mergeSetList[m_currentCliqueIndex]->macros.begin(); it2 != m_mergeSetList[m_currentCliqueIndex]->macros.end(); ++it2 )
                    if ( macro->key() == ( *it2 )->key() )
                    {
                        item->setOn( true );
                        break;
                    }
            }
            else if ( preamble!=NULL )
                for ( QValueList<BibTeX::Preamble*>::Iterator it2 = m_mergeSetList[m_currentCliqueIndex]->preambles.begin(); it2 != m_mergeSetList[m_currentCliqueIndex]->preambles.end(); ++it2 )
                    if ( preamble->value()->text() == ( *it2 )->value()->text() )
                    {
                        item->setOn( true );
                        break;
                    }
        }

        slotRefreshAlternatives();

        for ( QListViewItemIterator it( m_listViewAlternatives ); it.current(); ++it )
        {
            MergeEntriesAlternativesItem *item = dynamic_cast<MergeEntriesAlternativesItem*>( *it );
            if ( item != NULL )
            {
                for ( QValueList<BibTeX::EntryField*>::Iterator it2 = m_mergeSetList[m_currentCliqueIndex]->fields.begin(); it2 != m_mergeSetList[m_currentCliqueIndex]->fields.end(); ++it2 )
                    if ( item->field->fieldTypeName().lower() == ( *it2 )->fieldTypeName().lower() &&  item->field->value()->text() == ( *it2 )->value()->text() )
                    {
                        item->setOn( true );
                        break;
                    }
            }
            else
            {
                MergeElementsAlternativesId *item = dynamic_cast<MergeElementsAlternativesId*>( *it );
                if ( item != NULL )
                {
                    if ( item->id == m_mergeSetList[m_currentCliqueIndex]->id )
                        item->setOn( true );
                }
                else
                {
                    MergeEntriesAlternativesEntryType *item = dynamic_cast<MergeEntriesAlternativesEntryType*>( *it );
                    if ( item != NULL )
                    {
                        if (( item->type != BibTeX::Entry::etUnknown && item->type == m_mergeSetList[m_currentCliqueIndex]->type ) || ( item->typeString.lower() == m_mergeSetList[m_currentCliqueIndex]->typeString.lower() ) )
                            item->setOn( true );
                    }
                    else
                    {
                        MergeMacrosAlternativesItem *mai = dynamic_cast<MergeMacrosAlternativesItem*>( *it );
                        if ( mai != NULL )
                        {
                            if ( mai->value == m_mergeSetList[m_currentCliqueIndex]->macroValue )
                                mai->setOn( true );
                        }
                        else
                        {
                            MergeMacroAlternativesKey *mak = dynamic_cast<MergeMacroAlternativesKey*>( *it );
                            if ( mak != NULL )
                            {
                                if ( mak->key == m_mergeSetList[m_currentCliqueIndex]->macroKey )
                                    mak->setOn( true );
                            }
                            else
                            {
                                MergePreambleAlternatives *mpa =dynamic_cast<MergePreambleAlternatives*>( *it );
                                if ( mpa!=NULL )
                                {
                                    if ( mpa->text==m_mergeSetList[m_currentCliqueIndex]->preambleText )
                                        mpa->setOn( true );
                                }
                                else
                                    qDebug( "Item is of unknown type" );
                            }
                        }
                    }
                }
            }
        }
    }

    void MergeElements::applyMergeSet( BibTeX::File *bibTeXFile, BibTeX::File *otherBibTeXFile )
    {
        int n = m_duplicateCliqueList.size();
        for ( int i = 0; i < n; ++i )
        {
            if ( m_mergeSetList[i] == NULL ) continue;

            if ( !m_mergeSetList[i]->entries.isEmpty() )
            {
                QString id = m_mergeSetList[i]->id == QString::null ? ( *m_mergeSetList[i]->entries.begin() )->id() : m_mergeSetList[i]->id;

                BibTeX::Entry *newEntry = NULL;
                if ( m_mergeSetList[i]->type == BibTeX::Entry::etUnknown )
                {
                    if ( m_mergeSetList[i]->typeString == QString::null )
                    {
                        BibTeX::Entry *firstEntry = *m_mergeSetList[i]->entries.begin();
                        if ( firstEntry->entryType() == BibTeX::Entry::etUnknown )
                            newEntry = new BibTeX::Entry( firstEntry->entryTypeString(), id );
                        else
                            newEntry = new BibTeX::Entry( firstEntry->entryType(), id );
                    }
                    else
                        newEntry = new BibTeX::Entry( m_mergeSetList[i]->typeString, id );
                }
                else
                    newEntry = new BibTeX::Entry( m_mergeSetList[i]->type, id );

                for ( QValueList<BibTeX::EntryField*>::Iterator it = m_mergeSetList[i]->fields.begin(); it != m_mergeSetList[i]->fields.end(); ++it )
                {
                    newEntry->addField( new BibTeX::EntryField( *it ) );
                }

                for ( QValueList<BibTeX::Entry*>::Iterator it = m_mergeSetList[i]->entries.begin(); it != m_mergeSetList[i]->entries.end(); ++it )
                    for ( QValueList<BibTeX::EntryField*>::ConstIterator fIt = ( *it )->begin(); fIt != ( *it )->end(); ++fIt )
                        if ( newEntry->getField(( *fIt )->fieldTypeName() ) == NULL )
                        {
                            newEntry->addField( new BibTeX::EntryField( *fIt ) );
                        }

                for ( QValueList<BibTeX::Entry*>::Iterator it = m_mergeSetList[i]->entries.begin(); it != m_mergeSetList[i]->entries.end(); ++it )
                {
                    BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( bibTeXFile->containsKey(( *it )->id() ) );
                    if ( entry != NULL )
                        bibTeXFile->deleteElement( entry );
                    else
                    {
                        BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( otherBibTeXFile->containsKey(( *it )->id() ) );
                        if ( entry != NULL )
                            otherBibTeXFile->deleteElement( entry );
                    }
                }
                bibTeXFile->appendElement( newEntry );
            }
            else if ( !m_mergeSetList[i]->macros.isEmpty() )
            {
                BibTeX::Macro *newMacro = new BibTeX::Macro( m_mergeSetList[i]->macroKey );
                newMacro->setValue( m_mergeSetList[i]->macroValue );

                for ( QValueList<BibTeX::Macro*>::Iterator it = m_mergeSetList[i]->macros.begin(); it != m_mergeSetList[i]->macros.end(); ++it )
                {
                    bibTeXFile->deleteElement( *it );
                    if ( otherBibTeXFile != NULL )
                        otherBibTeXFile->deleteElement( *it );
                }

                bibTeXFile->appendElement( newMacro );
            }
            else if ( !m_mergeSetList[i]->preambles.isEmpty() )
            {
                BibTeX::Preamble *newPreamble = new BibTeX::Preamble( m_mergeSetList[i]->preambleText );

                for ( QValueList<BibTeX::Preamble*>::Iterator it = m_mergeSetList[i]->preambles.begin(); it != m_mergeSetList[i]->preambles.end(); ++it )
                {
                    bibTeXFile->deleteElement( *it );
                    if ( otherBibTeXFile != NULL )
                        otherBibTeXFile->deleteElement( *it );
                }

                bibTeXFile->appendElement( newPreamble );
            }
        }
    }

    int MergeElements::mergeDuplicates( BibTeX::File *bibTeXFile )
    {
        setCaption( i18n( "Find Duplicates" ) );
        Settings * settings = Settings::self( NULL );
        int sensitivity = ( int )( FindDuplicates::maxDistance / exp( log( 10 ) * settings->editing_findDuplicatesSensitivity / 10.0 ) );
        qDebug( "sensitivity= %i / %i", sensitivity, FindDuplicates::maxDistance );
        FindDuplicates findDuplicates( m_duplicateCliqueList, sensitivity, bibTeXFile, parentWidget( true ) );

        if ( m_duplicateCliqueList.isEmpty() )
        {
            KMessageBox::information( parentWidget( true ), i18n( "No duplicates found." ), i18n( "Find Duplicates" ) );
            return QDialog::Rejected;
        }

        m_mergeSetList = new MergeSet*[m_duplicateCliqueList.size()];
        memset( m_mergeSetList, 0, sizeof( MergeSet* )*m_duplicateCliqueList.size() );

        qDebug( "%i cliques", m_duplicateCliqueList.size() );
        setClique( 0 );

        int result = exec();
        if ( result == QDialog::Accepted )
            applyMergeSet( bibTeXFile );

        delete[] m_mergeSetList;

        return result;
    }

    void MergeElements::slotRefreshAlternatives()
    {
        QMap<BibTeX::EntryField::FieldType, MergeEntriesAlternativesController*> mapFieldToController;
        QMap<BibTeX::EntryField::FieldType, First> firstEntryData;
        bool first = true;
        MergePreambleAlternativesController* preambleController = NULL;
        MergeMacrosAlternativesController* macroKeyController = NULL;
        MergeMacrosAlternativesController* macroValueController = NULL;
        MergeEntriesAlternativesController *idController = NULL;
        MergeEntriesAlternativesController *typeController = NULL;
        QString firstId = QString::null;
        QString firstMacroKey = QString::null;
        BibTeX::Value *firstMacroValue = NULL;
        QString firstPreambleText = QString::null;
        BibTeX::Entry::EntryType firstType = BibTeX::Entry::etUnknown;
        QString firstTypeString = QString::null;

        m_listViewAlternatives->clear();

        for ( QListViewItemIterator it( m_listViewClique, QListViewItemIterator::Checked ); it.current(); ++it )
        {
            MergeElementsCliqueItem *meci = dynamic_cast<MergeElementsCliqueItem*>( *it );
            BibTeX::Entry *entry = NULL;
            BibTeX::Macro *macro = NULL;
            BibTeX::Preamble *preamble = NULL;
            if ( meci != NULL && ( entry = meci->entry ) != NULL )
            {
                if ( first )
                {
                    firstId = entry->id();
                    firstType = entry->entryType();
                    firstTypeString = entry->entryTypeString();
                    for ( BibTeX::Entry::EntryFields::const_iterator efi = entry->begin(); efi != entry->end(); ++efi )
                    {
                        First first;
                        first.entry = entry;
                        first.field = *efi;
                        firstEntryData.insert(( *efi )->fieldType(), first );
                    }
                }
                else
                {
                    if ( idController == NULL )
                    {
                        if ( entry->id() != firstId )
                        {
                            idController = new MergeEntriesAlternativesController( i18n( "Id" ), m_listViewAlternatives );
                            idController->setOpen( true );
                            MergeElementsAlternativesId *item = new MergeElementsAlternativesId( firstId, idController );
                            item->setOn( true );
                            new MergeElementsAlternativesId( entry->id(), idController );
                        }
                    }
                    else
                    {
                        QString thisText = entry->id();
                        bool isNew = true;
                        for ( QListViewItem *cur = idController->firstChild(); isNew && cur != NULL; cur = cur->nextSibling() )
                        {
                            MergeElementsAlternativesId *meai = dynamic_cast<MergeElementsAlternativesId*>( cur );
                            isNew = meai->id != thisText;
                        }
                        if ( isNew )
                            new MergeElementsAlternativesId( thisText, idController );
                    }

                    if ( typeController == NULL )
                    {
                        if (( firstType != BibTeX::Entry::etUnknown && entry->entryType() != firstType ) || ( entry->entryTypeString().lower() != entry->entryTypeString().lower() ) )
                        {
                            typeController = new MergeEntriesAlternativesController( i18n( "Type" ), m_listViewAlternatives );
                            typeController->setOpen( true );
                            MergeEntriesAlternativesEntryType *item = firstType != BibTeX::Entry::etUnknown ? new MergeEntriesAlternativesEntryType( firstType, typeController ) : new MergeEntriesAlternativesEntryType( firstTypeString, typeController );
                            item->setOn( true );
                            if ( entry->entryType() != BibTeX::Entry::etUnknown )
                                new MergeEntriesAlternativesEntryType( entry->entryType(), typeController );
                            else
                                new MergeEntriesAlternativesEntryType( entry->entryTypeString(), typeController );
                        }
                    }
                    else
                    {
                        QString typeString = entry->entryTypeString();
                        BibTeX::Entry::EntryType type = entry->entryType();
                        bool isNew = true;
                        for ( QListViewItem *cur = typeController->firstChild(); isNew && cur != NULL; cur = cur->nextSibling() )
                        {
                            MergeEntriesAlternativesEntryType *meat = dynamic_cast<MergeEntriesAlternativesEntryType*>( cur );
                            isNew = type == BibTeX::Entry::etUnknown && meat->typeString != typeString || meat->type != type;
                        }
                        if ( isNew )
                        {
                            if ( type != BibTeX::Entry::etUnknown )
                                new MergeEntriesAlternativesEntryType( type, typeController );
                            else
                                new MergeEntriesAlternativesEntryType( typeString, typeController );
                        }
                    }

                    for ( BibTeX::Entry::EntryFields::const_iterator efi = entry->begin(); efi != entry->end(); ++efi )
                        if ( mapFieldToController.contains(( *efi )->fieldType() ) )
                        {
                            MergeEntriesAlternativesController *controller = mapFieldToController[( *efi )->fieldType()];
                            QString thisText = ( *efi )->value()->text();
                            bool isNew = true;
                            for ( QListViewItem *cur = controller->firstChild(); isNew && cur != NULL; cur = cur->nextSibling() )
                            {
                                MergeEntriesAlternativesItem *meai = dynamic_cast<MergeEntriesAlternativesItem*>( cur );
                                isNew = meai->field->value()->text() != thisText;
                            }
                            if ( isNew )
                                new MergeEntriesAlternativesItem( *efi, controller );
                        }
                        else if ( firstEntryData.contains(( *efi )->fieldType() ) )
                        {
                            QString firstText = firstEntryData[( *efi )->fieldType()].field->value()->text();
                            QString thisText = ( *efi )->value()->text();
                            if ( firstText != thisText )
                            {
                                MergeEntriesAlternativesController *controller = new MergeEntriesAlternativesController(( *efi )->fieldType(), m_listViewAlternatives );
                                controller->setOpen( true );
                                MergeEntriesAlternativesItem *item = new MergeEntriesAlternativesItem( firstEntryData[( *efi )->fieldType()].field, controller );
                                item->setOn( true );
                                item = new MergeEntriesAlternativesItem( *efi, controller );
                                mapFieldToController.insert(( *efi )->fieldType(), controller );
                            }
                        }
                        else
                        {
                            First first;
                            first.entry = entry;
                            first.field = *efi;
                            firstEntryData.insert(( *efi )->fieldType(), first );
                        }
                }
            }
            else if ( meci != NULL && ( macro = meci->macro ) != NULL )
            {
                if ( first )
                {
                    firstMacroKey = macro->key();
                    firstMacroValue = macro->value();
                }
                else
                {
                    if ( macroKeyController == NULL )
                    {
                        if ( macro->key() != firstMacroKey )
                        {
                            macroKeyController = new MergeMacrosAlternativesController( true, m_listViewAlternatives );
                            macroKeyController->setOpen( true );
                            MergeMacroAlternativesKey *item = new MergeMacroAlternativesKey( firstMacroKey, macroKeyController );
                            item->setOn( true );
                            new MergeMacroAlternativesKey( macro->key(), macroKeyController );
                        }
                    }
                    else
                    {
                        QString thisText = macro->key();
                        bool isNew = true;
                        for ( QListViewItem *cur = macroKeyController->firstChild(); isNew && cur != NULL; cur = cur->nextSibling() )
                        {
                            MergeMacroAlternativesKey *mak = dynamic_cast<MergeMacroAlternativesKey*>( cur );
                            isNew = mak->key != thisText;
                        }
                        if ( isNew )
                            new MergeMacroAlternativesKey( thisText, macroKeyController );
                    }
                }

                if ( macroValueController == NULL )
                {
                    if ( firstMacroValue->text() != macro->value()->text() )
                    {
                        macroValueController = new MergeMacrosAlternativesController( false, m_listViewAlternatives );
                        macroValueController->setOpen( true );
                        MergeMacrosAlternativesItem *item = new MergeMacrosAlternativesItem( firstMacroValue, macroValueController );
                        item->setOn( true );
                        new MergeMacrosAlternativesItem( macro->value(), macroValueController );
                    }
                }
                else
                {
                    QString macroString = macro->value()->text();
                    bool isNew = true;
                    for ( QListViewItem *cur = macroValueController->firstChild(); isNew && cur != NULL; cur = cur->nextSibling() )
                    {
                        MergeMacrosAlternativesItem *mai = dynamic_cast<MergeMacrosAlternativesItem*>( cur );
                        isNew = macroString != mai->value->text();
                    }
                    if ( isNew )
                        new MergeMacrosAlternativesItem( macro->value(), macroValueController );
                }
            }
            else if ( meci != NULL && ( preamble = meci->preamble ) != NULL )
            {
                if ( first )
                    firstPreambleText = preamble->value()->text();
                else
                {
                    if ( preambleController == NULL )
                    {
                        if ( preamble->value()->text() != firstPreambleText )
                        {
                            preambleController = new MergePreambleAlternativesController( m_listViewAlternatives );
                            preambleController->setOpen( true );
                            MergePreambleAlternatives *item = new MergePreambleAlternatives( firstPreambleText, preambleController );
                            item->setOn( true );
                            new MergePreambleAlternatives( preamble->value()->text(), preambleController );
                        }
                    }
                    else
                    {
                        QString thisText = preamble->value()->text();
                        bool isNew = true;
                        for ( QListViewItem *cur = preambleController->firstChild(); isNew && cur != NULL; cur = cur->nextSibling() )
                        {
                            MergePreambleAlternatives *mpa = dynamic_cast<MergePreambleAlternatives*>( cur );
                            isNew = mpa->text != thisText;
                        }
                        if ( isNew )
                            new MergePreambleAlternatives( thisText, preambleController );
                    }
                }
            }
            first = false;
        }
    }

    void MergeElements::slotNextClique()
    {
        if ( m_currentCliqueIndex < ( int )( m_duplicateCliqueList.size() ) - 1 )
        {
            setClique( m_currentCliqueIndex + 1 );
        }
        else
            enableButton( User1, false );
    }

    void MergeElements::slotPreviousClique()
    {
        if ( m_currentCliqueIndex > 0 )
        {
            setClique( m_currentCliqueIndex - 1 );
        }
        else
            enableButton( User2, false );
    }

    void MergeElements::slotPreviewElement( QListViewItem *item )
    {
        MergeElementsCliqueItem *meci = dynamic_cast<MergeElementsCliqueItem*>( item );
        if ( meci != NULL && meci->entry != NULL )
            KBibTeX::EntryWidget::execute( meci->entry, NULL, TRUE, FALSE, this );
        else if ( meci != NULL && meci->macro != NULL )
            KBibTeX::MacroWidget::execute( meci->macro, TRUE, this );
        else if ( meci!=NULL&& meci->preamble!=NULL )
            KBibTeX::PreambleWidget::execute( meci->preamble, TRUE, this );
    }

    /* This function was taken form KMainWindow of KDE 3.5 and modified to fit KBibTeX */
    void MergeElements::saveWindowSize( KConfig *config ) const
    {
        int scnum = QApplication::desktop()->screenNumber( parentWidget() );
        QRect desk = QApplication::desktop()->screenGeometry( scnum );
        int w, h;
#if defined Q_WS_X11
        // save maximalization as desktop size + 1 in that direction
        KWin::WindowInfo info = KWin::windowInfo( winId(), NET::WMState );
        w = info.state() & NET::MaxHoriz ? desk.width() + 1 : width();
        h = info.state() & NET::MaxVert ? desk.height() + 1 : height();
#else
        if ( isMaximized() )
        {
            w = desk.width() + 1;
            h = desk.height() + 1;
        }
        //TODO: add "Maximized" property instead "+1" hack
#endif
        QRect size( desk.width(), w, desk.height(), h );
        bool defaultSize = false;//( size == d->defaultWindowSize );
        QString widthString = QString::fromLatin1( "Width %1" ).arg( desk.width() );
        QString heightString = QString::fromLatin1( "Height %1" ).arg( desk.height() );
        if ( !config->hasDefault( widthString ) && defaultSize )
            config->revertToDefault( widthString );
        else
            config->writeEntry( widthString, w );

        if ( !config->hasDefault( heightString ) && defaultSize )
            config->revertToDefault( heightString );
        else
            config->writeEntry( heightString, h );
    }

    void MergeElements::showEvent( QShowEvent * )
    {
        qDebug( "showEvent" );
        QTimer::singleShot( 10, this, SLOT( slotRestore() ) );
    }

    void MergeElements::slotRestore()
    {
        qDebug( "slotRestore" );
        KConfig * config = kapp->config();
        config->setGroup( "MergeElements" );
        restoreWindowSize( config );
    }

    /* This function was taken form KMainWindow of KDE 3.5 and modified to fit KBibTeX */
    void MergeElements::restoreWindowSize( KConfig *config )
    {
        // restore the size
        int scnum = QApplication::desktop()->screenNumber( parentWidget() );
        QRect desk = QApplication::desktop()->screenGeometry( scnum );
        QSize size( config->readNumEntry( QString::fromLatin1( "Width %1" ).arg( desk.width() ), 0 ),
                    config->readNumEntry( QString::fromLatin1( "Height %1" ).arg( desk.height() ), 0 ) );
        if ( size.isEmpty() )
        {
            // try the KDE 2.0 way
            size = QSize( config->readNumEntry( QString::fromLatin1( "Width" ), 0 ),
                          config->readNumEntry( QString::fromLatin1( "Height" ), 0 ) );
            if ( !size.isEmpty() )
            {
                // make sure the other resolutions don't get old settings
                config->writeEntry( QString::fromLatin1( "Width" ), 0 );
                config->writeEntry( QString::fromLatin1( "Height" ), 0 );
            }
        }
        if ( !size.isEmpty() )
        {
#ifdef Q_WS_X11
            int state = ( size.width() > desk.width() ? NET::MaxHoriz : 0 )
                        | ( size.height() > desk.height() ? NET::MaxVert : 0 );
            if (( state & NET::Max ) == NET::Max )
                ; // no resize
            else if (( state & NET::MaxHoriz ) == NET::MaxHoriz )
                resize( width(), size.height() );
            else if (( state & NET::MaxVert ) == NET::MaxVert )
                resize( size.width(), height() );
            else
                resize( size );
            // QWidget::showMaximized() is both insufficient and broken
            KWin::setState( winId(), state );
#else
            if ( size.width() > desk.width() || size.height() > desk.height() )
                setWindowState( WindowMaximized );
            else
                resize( size );
#endif
        }
    }

}
#include "mergeelements.moc"
