/*
    centralwidget.cpp  -  Central widget for the KBB main window

    copyright   : (c) 2001 by Martijn Klingens
    email       : klingens@kde.org

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

#include <tqsplitter.h>
#include <tqpushbutton.h>
#include <tqwidgetstack.h>
#include <tqlayout.h>

#include <kdialog.h>
#include <kdebug.h>
#include <kcombobox.h>
#include <tdelistview.h>
#include <kinputdialog.h>

#include "kbbprefs.h"
#include "bugsystem.h"
#include "packagelvi.h"
#include "buglvi.h"
#include "msginputdialog.h"
#include "packageselectdialog.h"
#include "cwbugdetails.h"
#include "bugcommand.h"
#include "severityselectdialog.h"
#include "cwsearchwidget.h"
#include "cwbuglistcontainer.h"
#include "cwbugdetailscontainer.h"
#include "bugserver.h"

#include "centralwidget.h"
#include <tdefiledialog.h>
#include <tdemessagebox.h>
#include "loadallbugsdlg.h"

using namespace KBugBusterMainWindow;

CentralWidget::CentralWidget( const TQCString &initialPackage,
                              const TQCString &initialComponent,
                              const TQCString &initialBug, TQWidget *parent,
                              const char * name )
  : TQWidget( parent, name )
{
    // Master layout
    ( new TQVBoxLayout( this, 0,
                       KDialog::spacingHint() ) )->setAutoAdd( true );

    // Create TQSplitter children
    m_vertSplitter = new TQSplitter( TQt::Vertical, this );
    m_listPane     = new CWBugListContainer( m_vertSplitter );
    m_horSplitter  = new TQSplitter( TQt::Horizontal,m_vertSplitter );
// The search pane isn't used. Should we remove the code?
    m_searchPane   = new CWSearchWidget( m_horSplitter );
    m_bugPane      = new CWBugDetailsContainer( m_horSplitter );

    m_searchPane->hide();
//    m_listPane->hide();

    m_searchPane->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum,
                                                    TQSizePolicy::Minimum ) );
    m_horSplitter->setResizeMode( m_searchPane, TQSplitter::FollowSizeHint );

    connect( m_listPane, TQ_SIGNAL( resetProgressBar() ),
             TQ_SIGNAL( resetProgressBar() ) );
    connect( m_bugPane, TQ_SIGNAL( resetProgressBar() ),
             TQ_SIGNAL( resetProgressBar() ) );

    // Start the proper jobs for loading the package lists
    connect( BugSystem::self(),
             TQ_SIGNAL( packageListAvailable( const Package::List & ) ),
             TQ_SLOT( updatePackageList( const Package::List & ) ) );
    connect( BugSystem::self(),
             TQ_SIGNAL( bugListAvailable( const Package &, const TQString &, const Bug::List & ) ),
             TQ_SLOT( updateBugList( const Package &, const TQString &, const Bug::List & ) ) );
    connect( BugSystem::self(),
             TQ_SIGNAL( bugListAvailable( const TQString &, const Bug::List & ) ),
             TQ_SLOT( updateBugList( const TQString &, const Bug::List & ) ) );
    connect( BugSystem::self(),
             TQ_SIGNAL( bugDetailsAvailable( const Bug &, const BugDetails & ) ),
             TQ_SLOT( updateBugDetails( const Bug &, const BugDetails & ) ) );

    connect( BugSystem::self(), TQ_SIGNAL( loadingError( const TQString & ) ),
             TQ_SLOT( showLoadingError( const TQString & ) ) );

    connect( m_bugPane, TQ_SIGNAL( signalCloseBug() ), TQ_SLOT( closeBug() ) );
    connect( m_bugPane, TQ_SIGNAL( signalCloseBugSilently() ), TQ_SLOT( closeBugSilently() ) );
    connect( m_bugPane, TQ_SIGNAL( signalReopenBug() ), TQ_SLOT( reopenBug() ) );
    connect( m_bugPane, TQ_SIGNAL( signalReassignBug() ), TQ_SLOT( reassignBug() ) );
    connect( m_bugPane, TQ_SIGNAL( signalTitleBug() ), TQ_SLOT( titleBug() ) );
    connect( m_bugPane, TQ_SIGNAL( signalSeverityBug() ), TQ_SLOT( severityBug() ) );
    connect( m_bugPane, TQ_SIGNAL( signalReplyBug() ), TQ_SLOT( replyBug() ) );
    connect( m_bugPane, TQ_SIGNAL( signalReplyPrivateBug() ), TQ_SLOT( replyPrivateBug() ) );

    connect( m_bugPane, TQ_SIGNAL( signalClearCommand() ), TQ_SLOT( clearCommand() ) );

    // Add the selection slots for the listviews
    connect( m_searchPane->m_searchPackages,
             TQ_SIGNAL( activated( const TQString & ) ),
             TQ_SLOT( slotRetrieveBugList( const TQString & ) ) );

    connect( m_listPane, TQ_SIGNAL( executed( const Bug & ) ),
             TQ_SLOT( slotRetrieveBugDetails( const Bug & ) ) );
    connect( m_listPane, TQ_SIGNAL( currentChanged( const Bug & ) ),
             TQ_SLOT( slotSetActiveBug( const Bug & ) ) );

    connect( m_listPane, TQ_SIGNAL( searchPackage() ), TQ_SIGNAL( searchPackage() ) );
    connect( m_bugPane, TQ_SIGNAL( searchBugNumber() ), TQ_SIGNAL( searchBugNumber() ) );

    m_bLoadingAllBugs = false;

    initialize( initialPackage, initialComponent, initialBug );
}

CentralWidget::~CentralWidget()
{
//    kdDebug() << "CentralWidget::~CentralWidget()" << endl;
}

void CentralWidget::initialize( const TQString& p, const TQString &c, const TQString& b )
{
//    kdDebug() << "CentralWidget::initialize(): package: '" << p
//              << "' bug: '" << b << "'" << endl;

    BugServerConfig cfg = BugSystem::self()->server()->serverConfig();
    TQString package = p.isEmpty() ? cfg.currentPackage() : p;
    TQString bug = b.isEmpty() ? cfg.currentBug() : b;
    TQString component = c.isEmpty() ? cfg.currentComponent() : c;

    m_listPane->setNoList();
    m_bugPane->setNoBug();

    BugSystem::self()->retrievePackageList();
    if ( !package.isEmpty() ) {
        m_currentPackage = BugSystem::self()->package( package );
        m_currentComponent = component;
        BugSystem::self()->retrieveBugList( m_currentPackage, m_currentComponent );

        if ( !bug.isEmpty() ) {
            m_currentBug = BugSystem::self()->bug( m_currentPackage,
                                                   m_currentComponent, bug );
            BugSystem::self()->retrieveBugDetails( m_currentBug );
        }
    } else {
        if ( !bug.isEmpty() ) {
            // ### bad way to instanciating a bug object! doesn't restore details!
            m_currentBug = Bug::fromNumber( bug ); // bug number specified on cmdline. Is it a problem that we don't have details ?
            BugSystem::self()->retrieveBugDetails( m_currentBug );
        }
    }
}

void CentralWidget::readConfig()
{
    m_horSplitter->setSizes( KBBPrefs::instance()->mSplitter2 );
    m_vertSplitter->setSizes( KBBPrefs::instance()->mSplitter1 );
}

void CentralWidget::writeConfig()
{
#if 0
    kdDebug() << "m_vertSplitter" << endl;
    TQValueList<int> sizes = m_vertSplitter->sizes();
    TQValueList<int>::ConstIterator it;
    for( it = sizes.begin(); it != sizes.end(); ++it ) {
        kdDebug() << "  " << (*it) << endl;
    }
#endif

    KBBPrefs::instance()->mSplitter1 = m_vertSplitter->sizes();
    KBBPrefs::instance()->mSplitter2 = m_horSplitter->sizes();

    BugServer *server = BugSystem::self()->server();
    server->serverConfig().setCurrentPackage( m_currentPackage.name() );
    server->serverConfig().setCurrentComponent( m_currentComponent );
    server->serverConfig().setCurrentBug( m_currentBug.number() );
}

void CentralWidget::slotRetrieveBugList( const TQString &package )
{
    slotRetrieveBugList( package, TQString() );
}

void CentralWidget::slotRetrieveBugList( const TQString &p, const TQString &component )
{
    if ( p.isEmpty() ) return;

    Package package = m_packageList[ p ];

    if ( package.isNull() ) {
       // Invalid package, return
       return;
    }

    if ( ( package == m_currentPackage ) && ( m_currentComponent == component ) ) {
        return; // Nothing to do
    }

    m_currentComponent = component;
    m_currentPackage = package;

    BugSystem::self()->retrieveBugList( m_currentPackage, m_currentComponent );
}

TQString CentralWidget::currentNumber() const
{
   if( m_currentBug.isNull() )
     return "";
   else
     return m_currentBug.number();
}

TQString CentralWidget::currentTitle() const
{
   if( m_currentBug.isNull() )
     return "";
   else
     return m_currentBug.title();
}

void CentralWidget::slotRetrieveBugDetails( const Bug &bug )
{
    if( m_currentBug == bug )
        return; // Nothing to do

    m_currentBug = bug;
    BugSystem::self()->retrieveBugDetails( m_currentBug );
}

void CentralWidget::slotSetActiveBug( const Bug &bug )
{
    if( bug.isNull() )
    {
        return;
    }

    if( m_activeBug == bug )
        return; // Nothing to do

    m_activeBug = bug;
}

void CentralWidget::updatePackageList( const Package::List &pkgs )
{
    // ### needs proper implementation ;-)

    m_searchPane->m_searchPackages->clear();
    m_searchPane->m_searchPackages->completionObject()->clear();
//    m_bugPane->m_bugDetails->m_bugPackage->clear();
    emit resetProgressBar();

    Package::List::ConstIterator it = pkgs.begin();
    for ( ; it != pkgs.end(); ++it )
    {
        m_packageList[ ( *it ).name() ] = *it;
        m_searchPane->m_searchPackages->insertItem( ( *it ).name() );
        m_searchPane->m_searchPackages->
                  completionObject()->addItem( ( *it ).name() );
//        m_bugPane->m_bugDetails->m_bugPackage->insertItem( ( *it ).name() );
    }

/*
    if( m_bugPane->m_bugStack->id(
                m_bugPane->m_bugStack->visibleWidget() ) != 0 )
    {
        m_bugPane->m_bugDetails->m_bugPackage->setCurrentItem( -1 );
    }
*/
}

void CentralWidget::updateBugList( const Package &pkg, const TQString &component, const Bug::List &bugs )
{
    m_listPane->setBugList( pkg, component, bugs );
}

void CentralWidget::updateBugList( const TQString &label, const Bug::List &bugs )
{
    m_listPane->setBugList( label, bugs );
}

void CentralWidget::updateBugDetails( const Bug &bug, const BugDetails &bd )
{
    if ( !m_bLoadingAllBugs )
        m_bugPane->setBug( bug, bd );
}

void CentralWidget::slotReloadPackageList()
{
    BugSystem::self()->cache()->invalidatePackageList();
    BugSystem::self()->retrievePackageList();
}

void CentralWidget::slotReloadPackage()
{
    if (!m_currentPackage.isNull()) {
        BugSystem::self()->cache()->invalidateBugList( m_currentPackage, m_currentComponent );
        BugSystem::self()->retrieveBugList( m_currentPackage, m_currentComponent );
    }
}

void CentralWidget::slotLoadMyBugs()
{
    BugSystem::self()->retrieveMyBugsList();
}

void CentralWidget::slotReloadBug()
{
    if (!m_currentBug.isNull()) {
        BugSystem::self()->cache()->invalidateBugDetails( m_currentBug );
        BugSystem::self()->retrieveBugDetails( m_currentBug );
    }
}

void CentralWidget::updatePackage()
{
    if (!m_currentPackage.isNull()) {
        BugSystem::self()->retrieveBugList( m_currentPackage, m_currentComponent );
    }
}

void CentralWidget::slotExtractAttachments()
{
    if (!m_currentBug.isNull()) {
        // Grab bug details (i.e. full-text) from cache, then extract attachments from it
        BugDetails details = BugSystem::self()->cache()->loadBugDetails( m_currentBug );
        TQValueList<BugDetails::Attachment> attachments = details.extractAttachments();
        if ( !attachments.isEmpty() )
        {
            TQStringList fileList;
            for ( TQValueList<BugDetails::Attachment>::Iterator it = attachments.begin() ; it != attachments.end() ; ++it )
            {
                // Handle duplicates
                if ( fileList.contains( (*it).filename ) )
                {
                    int n = 2; // looks stupid to have "blah" and "1-blah", start at 2
                    TQString fn = TQString::number(n) + '-' + (*it).filename;
                    while ( fileList.contains( fn ) )
                    {
                        ++n;
                        fn = TQString::number(n) + '-' + (*it).filename;
                    }
                    (*it).filename = fn;
                }
                fileList += (*it).filename;
            }

            int res = KMessageBox::questionYesNoList( this,
                                                      i18n("Found the following attachments. Save?"),
                                                      fileList, TQString(), KStdGuiItem::save(), KStdGuiItem::dontSave() );
            if ( res == KMessageBox::No )
                return;
            TQString dir = KFileDialog::getExistingDirectory( TQString(), this, i18n("Select Folder Where to Save Attachments") );
            if ( !dir.isEmpty() )
            {
                if ( !dir.endsWith( "/" ) )
                    dir += '/';
                for ( TQValueList<BugDetails::Attachment>::Iterator it = attachments.begin() ; it != attachments.end() ; ++it )
                {
                    TQString filename = m_currentBug.number() + '-' + (*it).filename;
                    TQFile file( dir + filename );
                    if ( file.open( IO_WriteOnly ) )
                        file.writeBlock( (*it).contents );
                    else
                        kdError() << "Couldn't save attachment to " << filename << endl;
                    file.close();
                }
            }
        }
    }
}

void CentralWidget::mergeBugs()
{
    TQStringList bugNumbers = m_listPane->selectedBugs();
    if ( bugNumbers.count() >= 2 ) {
      BugSystem::self()->queueCommand(
          new BugCommandMerge( bugNumbers, m_currentPackage ) );
    }
}

void CentralWidget::unmergeBugs()
{
    BugSystem::self()->queueCommand(
        new BugCommandUnmerge( m_currentBug, m_currentPackage ) );
}

void CentralWidget::closeBug()
{
    MsgInputDialog *dlg = new MsgInputDialog( MsgInputDialog::Close,
                                              m_currentBug, m_currentPackage,
					      m_bugPane->bugDetailsWidget()->selectedText(), this );
    dlg->show();
}

void CentralWidget::closeBugSilently()
{
    BugSystem::self()->queueCommand(
            new BugCommandCloseSilently( m_currentBug, m_currentPackage ) );
}

void CentralWidget::reopenBug()
{
    BugSystem::self()->queueCommand(
        new BugCommandReopen( m_currentBug, m_currentPackage ) );
}

void CentralWidget::reassignBug()
{
    PackageSelectDialog *dlg = new PackageSelectDialog( this );
    dlg->exec();

    dlg->setPackages( BugSystem::self()->packageList() );
    BugServerConfig cfg = BugSystem::self()->server()->serverConfig();
    dlg->setRecentPackages( cfg.recentPackages() );

    Package package = dlg->selectedPackage();

    if ( package.isNull() ) {
        return;
    }

    BugSystem::self()->queueCommand(
        new BugCommandReassign( m_currentBug, package.name(), m_currentPackage ) );
}

void CentralWidget::titleBug()
{
    bool ok = false;
    TQString title = KInputDialog::getText( i18n("Change Bug Title"),
                                           i18n( "Please enter a new title:" ),
                                           m_currentBug.title(), &ok, this );
    if ( ok && !title.isEmpty() ) {
        BugSystem::self()->queueCommand(
            new BugCommandRetitle( m_currentBug, title, m_currentPackage ) );
    }
}

void CentralWidget::severityBug()
{
    SeveritySelectDialog *dlg = new SeveritySelectDialog( this );
    dlg->setSeverity( m_currentBug.severity() );
    int result = dlg->exec();
    if ( result == TQDialog::Accepted ) {
        BugSystem::self()->queueCommand(
            new BugCommandSeverity( m_currentBug,
                                    dlg->selectedSeverityAsString(), m_currentPackage ) );
    }
}

void CentralWidget::replyBug()
{
    MsgInputDialog *dlg = new MsgInputDialog( MsgInputDialog::Reply,
                                              m_currentBug, m_currentPackage,
					      m_bugPane->bugDetailsWidget()->selectedText(), this );
    dlg->show();
}

void CentralWidget::replyPrivateBug()
{
    MsgInputDialog *dlg = new MsgInputDialog( MsgInputDialog::ReplyPrivate,
                                              m_currentBug, m_currentPackage,
					      m_bugPane->bugDetailsWidget()->selectedText(), this );
    dlg->show();
}

void CentralWidget::clearCommand()
{
  BugSystem::self()->clearCommands( m_currentBug.number() );
}

void CentralWidget::searchBugByTitle( int options, const TQString& pattern )
{
  m_listPane->searchBugByTitle( options, pattern );
}

void CentralWidget::slotRetrieveAllBugDetails()
{
    m_bLoadingAllBugs = true;
    // Make a modal dialog to show the progress, and block usage of main window.
    LoadAllBugsDlg dlg( m_currentPackage, m_currentComponent );
    dlg.exec();
    m_bLoadingAllBugs = false;
}

void CentralWidget::showLoadingError( const TQString &text )
{
    KMessageBox::error( this, text );
}

CWBugDetails *CentralWidget::bugDetailsWidget()
{
    return m_bugPane->bugDetailsWidget();
}

#include "centralwidget.moc"
