/***************************************************************************
 *   Copyright (C) 2004-2009 by Thomas Fischer                             *
 *   fischer@unix-ag.uni-kl.de                                             *
 *                                                                         *
 *   based on code from or inspired by                                     *
 *   - Tellico by Robby Stephenson                                         *
 *   - kbib by Thach Nguyen                                                *
 *                                                                         *
 *   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.             *
 *                                                                         *
 *                                                                         *
 *   This code is based on code from Tellico 1.2.10                        *
 *                      by Robby Stephenson <robby@periapsis.org>          *
 *   Tellico is released under the GNU GPL 2, too.                         *
 *                      http://www.periapsis.org/tellico/                  *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <tqfile.h>
#include <tqapplication.h>
#include <tqspinbox.h>
#include <tqlayout.h>
#include <tqlabel.h>

#include <tdelocale.h>
#include <kstandarddirs.h>
#include <klineedit.h>
#include <kcombobox.h>
#include <tdemessagebox.h>
#include <kpushbutton.h>
#include <kiconloader.h>
#include <tdeio/netaccess.h>
#include <tdeapplication.h>
#include <kdebug.h>
#include <kurl.h>

#include "fileimporterbibutils.h"
#include "settings.h"
#include "webqueryz3950.h"

namespace KBibTeX
{
    const TQString WebQueryZ3950Widget::attributesI18N[] = { i18n( "any" ), i18n( "title" ), i18n( "author" ), i18n( "publisher" )};

    WebQueryZ3950Widget::WebQueryZ3950Widget( TQWidget *parent, const char *name )
            : WebQueryWidget( parent, name ), lineEditQuery2( NULL )
    {
        init();

        Settings *settings = Settings::self();
        TQString value = settings->getWebQueryDefault( "Z3950_server" );
        value = value == TQString::null || value.isEmpty() ? "0" : value;
        comboBoxServers->setCurrentItem( value.toInt() );
        value = settings->getWebQueryDefault( "Z3950_query1" );
        value = value == TQString::null ? "" : value;
        lineEditQuery->setText( value );
        slotTextChanged( value, true );
        value = settings->getWebQueryDefault( "Z3950_attr1" );
        value = value == TQString::null ? "1" : value;
        comboBoxInAttribute->setCurrentItem( value.toInt() );
        value = settings->getWebQueryDefault( "Z3950_query2" );
        lineEditQuery2->setText( value == TQString::null ? "" : value );
        value = settings->getWebQueryDefault( "Z3950_attr2" );
        value = value == TQString::null ? "1" : value;
        comboBoxInAttribute2->setCurrentItem( value.toInt() );
        value = settings->getWebQueryDefault( "Z3950_booleanOp" );
        value = value == TQString::null || value.isEmpty() ? "0" : value;
        comboBoxBooleanOp->setCurrentItem( value.toInt() );
    }

    void WebQueryZ3950Widget::init()
    {
        TQVBoxLayout *vLayout = new TQVBoxLayout( this, 0, KDialog::spacingHint() );

        TQHBoxLayout *hLayout = new TQHBoxLayout( );
        vLayout->addLayout( hLayout );

        TQLabel *label = new TQLabel( i18n( "Server:" ), this );
        hLayout->setStretchFactor( label, 1 );
        hLayout->addWidget( label );
        comboBoxServers = new KComboBox( false, this );
        hLayout->addWidget( comboBoxServers );
        hLayout->setStretchFactor( comboBoxServers, 7 );
        label->setBuddy( comboBoxServers );
        hLayout->addSpacing( KDialog::spacingHint() * 2 );
        label = new TQLabel( i18n( "&Number of results:" ), this );
        hLayout->addWidget( label );
        hLayout->setStretchFactor( label, 1 );
        spinBoxMaxHits = new TQSpinBox( 1, 50, 1, this );
        spinBoxMaxHits->setValue( 10 );
        hLayout->addWidget( spinBoxMaxHits );
        hLayout->setStretchFactor( spinBoxMaxHits, 3 );
        label->setBuddy( spinBoxMaxHits );

        TQGridLayout *layout = new TQGridLayout( vLayout, 2, 6, KDialog::spacingHint() );

        KPushButton *clearSearchText = new KPushButton( this );
        clearSearchText->setIconSet( TQIconSet( SmallIcon( "locationbar_erase" ) ) );
        layout->addWidget( clearSearchText, 0, 1 );
        label = new TQLabel( i18n( "Search term 1:" ), this );
        layout->addWidget( label, 0, 2 );
        lineEditQuery = new KLineEdit( this );
        layout->addWidget( lineEditQuery, 0, 3 );
        label->setBuddy( lineEditQuery );
        connect( clearSearchText, TQ_SIGNAL( clicked() ), lineEditQuery, TQ_SLOT( clear() ) );
        connect( lineEditQuery, TQ_SIGNAL( textChanged( const TQString& ) ), this, TQ_SLOT( slotTextChanged( const TQString& ) ) );
        TDECompletion *completionQuery = lineEditQuery->completionObject();
        connect( lineEditQuery, TQ_SIGNAL( returnPressed() ), this, TQ_SIGNAL( startSearch() ) );
        connect( lineEditQuery, TQ_SIGNAL( returnPressed( const TQString& ) ), completionQuery, TQ_SLOT( addItem( const TQString& ) ) );
        label = new TQLabel( i18n( "Search in:" ), this );
        layout->addWidget( label, 0, 4 );
        comboBoxInAttribute = new KComboBox( false, this );
        layout->addWidget( comboBoxInAttribute, 0, 5 );
        label->setBuddy( comboBoxInAttribute );

        comboBoxBooleanOp = new KComboBox( false, this );
        layout->addWidget( comboBoxBooleanOp, 1, 0 );

        clearSearchText = new KPushButton( this );
        clearSearchText->setIconSet( TQIconSet( SmallIcon( "locationbar_erase" ) ) );
        layout->addWidget( clearSearchText, 1, 1 );
        label = new TQLabel( i18n( "Search term 2:" ), this );
        layout->addWidget( label, 1, 2 );
        lineEditQuery2 = new KLineEdit( this );
        layout->addWidget( lineEditQuery2, 1, 3 );
        label->setBuddy( lineEditQuery2 );
        connect( clearSearchText, TQ_SIGNAL( clicked() ), lineEditQuery2, TQ_SLOT( clear() ) );
        completionQuery = lineEditQuery->completionObject();
        connect( lineEditQuery2, TQ_SIGNAL( returnPressed() ), this, TQ_SIGNAL( startSearch() ) );
        connect( lineEditQuery2, TQ_SIGNAL( returnPressed( const TQString& ) ), completionQuery, TQ_SLOT( addItem( const TQString& ) ) );
        label = new TQLabel( i18n( "Search in:" ), this );
        layout->addWidget( label, 1, 4 );
        comboBoxInAttribute2 = new KComboBox( false, this );
        layout->addWidget( comboBoxInAttribute2, 1, 5 );
        label->setBuddy( comboBoxInAttribute2 );

#ifdef HAVE_YAZ
        Settings *settings = Settings::self();
        for ( TQMap<TQString, Settings::Z3950Server>::Iterator it = settings->z3950_ServerList.begin(); it != settings->z3950_ServerList.end(); ++it )
            comboBoxServers->insertItem( it.data().name );
#endif // HAVE_YAZ
        for ( unsigned int i = 0; i < sizeof( attributesI18N ) / sizeof( attributesI18N[0] ); ++i )
        {
            comboBoxInAttribute->insertItem( attributesI18N[i] );
            comboBoxInAttribute2->insertItem( attributesI18N[i] );
        }
        comboBoxBooleanOp->insertItem( i18n( "and" ) );
        comboBoxBooleanOp->insertItem( i18n( "or" ) );
    }

    WebQueryZ3950::WebQueryZ3950( TQWidget* parent ) : WebQuery( parent )
#ifdef HAVE_YAZ
            , m_marc21transformer( NULL ), m_unimarctransformer( NULL ), m_modsImporter( NULL ), m_conn( NULL )
#endif // HAVE_YAZ
    {
        m_widget = new WebQueryZ3950Widget( parent );
    }

    WebQueryZ3950::~WebQueryZ3950()
    {
#ifdef HAVE_YAZ
        if ( m_modsImporter != NULL ) delete m_modsImporter;
        if ( m_marc21transformer != NULL ) delete m_marc21transformer;
        if ( m_unimarctransformer != NULL ) delete m_unimarctransformer;
        if ( m_conn != NULL ) delete m_conn;
#endif // HAVE_YAZ
//         delete m_widget; FIXME Why does delete fail here?
    }

    TQString WebQueryZ3950::title()
    {
        return i18n( "Z39.50" );
    }

    TQString WebQueryZ3950::disclaimer()
    {
        return i18n( "Z39.50" );
    }

    TQString WebQueryZ3950::disclaimerURL()
    {
        return "http://[127.0.0.1]/";
    }

    WebQueryWidget *WebQueryZ3950::widget()
    {
        return m_widget;
    }

    void WebQueryZ3950::query()
    {
        WebQuery::query();

        Settings *settings = Settings::self();
        settings->setWebQueryDefault( "Z3950_server", TQString::number( m_widget->comboBoxServers->currentItem() ) );
        settings->setWebQueryDefault( "Z3950_query1", m_widget->lineEditQuery->text() );
        settings->setWebQueryDefault( "Z3950_attr1", TQString::number( m_widget->comboBoxInAttribute->currentItem() ) );
        settings->setWebQueryDefault( "Z3950_query2", m_widget->lineEditQuery2->text() );
        settings->setWebQueryDefault( "Z3950_attr2", TQString::number( m_widget->comboBoxInAttribute2->currentItem() ) );
        settings->setWebQueryDefault( "Z3950_booleanOp", TQString::number( m_widget->comboBoxBooleanOp->currentItem() ) );

#ifdef HAVE_YAZ

        TQString searchTerm = m_widget->lineEditQuery->text().stripWhiteSpace();
        if ( searchTerm.isEmpty() )
        {
            setNumStages( 1 );
            setEndSearch( WebQuery::statusError );
            return;
        }

        TQString query = queryClause( searchTerm, m_widget->comboBoxInAttribute->currentItem() );

        searchTerm = m_widget->lineEditQuery2->text().stripWhiteSpace();
        if ( !searchTerm.isEmpty() )
        {
            if ( m_widget->comboBoxBooleanOp->currentItem() == 0 )
                query = query.prepend( "@and " );
            else
                query = query.prepend( "@or " );
            query = query.append( queryClause( searchTerm, m_widget->comboBoxInAttribute2->currentItem() ) );
        }

        kdDebug() << "query = " << query << endl;

        m_conn = NULL;
        settings = Settings::self();
        for ( TQMap<TQString, Settings::Z3950Server>::Iterator it = settings->z3950_ServerList.begin(); m_conn == NULL && it != settings->z3950_ServerList.end(); ++it )
        {
            if ( it.data().name.compare( m_widget->comboBoxServers->currentText() ) == 0 )
            {
                m_syntax = it.data().syntax;
                m_conn = new KBibTeX::Z3950Connection( this, it.data().host, it.data().port, it.data().database, it.data().charset, m_syntax, "f" );
                m_conn->setUserPassword( it.data().user, it.data().password );
            }
        }

        if ( m_conn != NULL )
        {
            setNumStages( m_widget->spinBoxMaxHits->value() );
            m_started = true;

            m_conn->setQuery( query, m_widget->spinBoxMaxHits->value() );
            m_modsList.clear();
            m_hitCounter = 0;
            m_conn->start();
            kdDebug() << "WebQueryZ3950::query: started" << endl;
        }
        else
#else // HAVE_YAZ
        kdDebug() << "HAVE_YAZ not defined" << endl;
#endif // HAVE_YAZ
        {
            setNumStages( 1 );
            setEndSearch( WebQuery::statusSuccess );
        }
    }

    void WebQueryZ3950::cancelQuery()
    {
#ifdef HAVE_YAZ
        if ( m_started && m_conn != NULL )
        {
            m_started = false;
            m_conn->abort();
            m_conn->wait( 10000 );
            setEndSearch( WebQuery::statusError );
        }
#endif // HAVE_YAZ
    }

#ifdef HAVE_YAZ
    TQString WebQueryZ3950::queryClause( const TQString& text, int field )
    {
        TQString result = "@attr 1=";
        switch ( field )
        {
        case 1: result.append( "4" ); break;
        case 2: result.append( "1003" ); break;
        case 3: result.append( "1006" ); break;
        case 4: result.append( "1016" ); break;
        default: result.append( "1018" ); break;
        }
        result.append( " @attr 2=3 \"" ).append( text ).append( "\" " );

        return result;
    }
#endif // HAVE_YAZ

    void WebQueryZ3950::customEvent( TQCustomEvent* event )
    {
        kdDebug() << "WebQueryZ3950::customEvent of type " << event->type() << endl;

#ifdef HAVE_YAZ
        if ( !m_conn )
        {
            return;
        }

        if ( event->type() == KBibTeX::Z3950ResultFound::uid() )
        {
            KBibTeX::Z3950ResultFound* e = static_cast<KBibTeX::Z3950ResultFound*>( event );
            kdDebug() << "Z3950ResultFound: " << e->result().left( 24 ) << " [..] " << e->result().right( 24 ) << endl;
            storeResult( e->result(), m_syntax );
            m_hitCounter++;
            enterNextStage();
        }

        else if ( event->type() == KBibTeX::Z3950ConnectionDone::uid() )
        {
            KBibTeX::Z3950ConnectionDone* e = static_cast<KBibTeX::Z3950ConnectionDone*>( event );
            kdDebug() << "Z3950ConnectionDone: " << e->message() << " (" << e->messageType() << ")" << endl;
            if ( e->messageType() > -1 )
                KMessageBox::error( m_widget, TQString( i18n( "The server returned the following message:\n\n%1" ) ).arg( e->message() ), i18n( "Error querying Z39.50 server" ) );

            m_started = false;
            if ( m_conn != NULL )
                m_conn->wait();

            if ( !m_aborted )
            {
                evalStoredResults();
                setEndSearch( e->messageType() > -1 ? WebQuery::statusError : WebQuery::statusSuccess );
            }
        }

        tqApp->processEvents();
#endif // HAVE_YAZ
    }

#ifdef HAVE_YAZ
    void WebQueryZ3950::storeResult( const TQString& resultText, const TQString& syntax )
    {
        if ( resultText.isEmpty() ) return;
        TQString convertedResultText = TQString::null;

        if ( syntax == "mods" )
            convertedResultText = resultText;
        else if ( syntax == "usmarc" || syntax == "marc21" )
        {
            if ( m_marc21transformer == NULL ) m_marc21transformer = new BibTeX::XSLTransform( TDEGlobal::dirs()->findResource( "data", "kbibtexpart/xslt/MARC21slim2MODS3.xsl" ) );
            convertedResultText = m_marc21transformer->transform( resultText );
        }
        else if ( syntax == "unimarc" )
        {
            if ( m_unimarctransformer == NULL ) m_unimarctransformer = new BibTeX::XSLTransform( TDEGlobal::dirs()->findResource( "data", "kbibtexpart/xslt/UNIMARC2MODS3.xsl" ) );
            convertedResultText = m_unimarctransformer->transform( resultText );
        }

        m_modsList.append( convertedResultText );
    }

    void WebQueryZ3950::evalStoredResults()
    {
        if ( m_modsImporter == NULL )
            m_modsImporter = new BibTeX::FileImporterBibUtils( BibTeX::File::formatMODS );

        for ( TQStringList::Iterator it = m_modsList.begin(); it != m_modsList.end();++it )
        {
            BibTeX::File *bibtexFile = m_modsImporter->load( *it );
            if ( bibtexFile != NULL )
            {
                for ( BibTeX::File::ElementList::iterator it = bibtexFile->begin(); it != bibtexFile->end(); ++it )
                {
                    BibTeX::Entry *entry = dynamic_cast<BibTeX::Entry*>( *it );
                    if ( entry != NULL )
                    {
                        BibTeX::Entry *newEntry = new BibTeX::Entry( entry );
                        kdDebug() << "entry= " << newEntry->text() << endl;
                        emit foundEntry( newEntry, false );
                    }
                }

                delete bibtexFile;
            }
        }
    }
#endif // HAVE_YAZ

}
#include "webqueryz3950.moc"
