/* ****************************************************************************
  This file is part of KBabel

  Copyright (C) 2002-2003 by Marco Wegner <mail@marcowegner.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.

**************************************************************************** */


// Qt include files
#include <qcheckbox.h>
#include <qcombobox.h>
#include <qfileinfo.h>
#include <qframe.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qlistbox.h>
#include <qpushbutton.h>
#include <qregexp.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qtextedit.h>
// KDE include files
#include <kconfig.h>
#include <kdebug.h>
#include <kglobal.h>
#include <klocale.h>
#include <kprocess.h>
// Project specific include files
#include "cvsdialog.h"


CVSDialog::CVSDialog( CVS::Command cmd, QWidget * parent, const char * name, bool modal, WFlags f )
  : KDialog( parent, name, modal, f )
{
  _cmd = cmd;
  p=0L;
  setCaption( i18n( "CVS Dialog" ) );

  QString temp;

  QVBoxLayout * layout = new QVBoxLayout( this, 6, 6, "MAIN LAYOUT" );

  // Set the label's text depending on the CVS command.
  switch ( cmd ) {
    case CVS::Update:
      temp = i18n( "Update the following files:" );
      break;
    case CVS::Commit:
      temp = i18n( "Commit the following files:" );
      break;
    case CVS::Status:
      temp = i18n( "Get status for the following files:" );
      break;
  }
  layout->addWidget( new QLabel( temp, this ) );

  // Widget for showing the list of files.
  filebox = new QListBox( this );
  layout->addWidget( filebox );

  // Add special widgets for 'cvs commit'.
  if ( cmd == CVS::Commit ) {
    QLabel * label;

    // Combobox for displaying old log messages.
    label = new QLabel( i18n( "&Old messages:" ), this );
    oldMessages = new QComboBox( this );
    oldMessages->setDuplicatesEnabled( false );
    label->setBuddy( oldMessages );
    layout->addWidget( label );
    layout->addWidget( oldMessages );

    // Textfield for entering a log message.
    label = new QLabel( i18n( "&Log message:" ), this );
    logedit = new QTextEdit( this );
    label->setBuddy( logedit );
    layout->addWidget( label );
    layout->addWidget( logedit );

    connect( oldMessages, SIGNAL( activated( const QString& ) ),
      logedit, SLOT( setText( const QString& ) ) );
  }

  QHBoxLayout * buttons = new QHBoxLayout( 0, 0, 6, "BUTTON LAYOUT" );
  // Add special buttons for 'cvs commit'.
  if ( cmd == CVS::Commit ) {
    autoAddBox = new QCheckBox( i18n( "Auto&matically add files if necessary" ), this );
    buttons->addWidget( autoAddBox );
    silentCommit = new QCheckBox( i18n( "&Silent commit" ), this );
    buttons->addWidget( silentCommit );
  }
  buttons->addItem( new QSpacerItem( 1, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ) );

  // Set the main button's text depending on the CVS comand.
  switch ( cmd ) {
    case CVS::Update:
      temp = i18n( "&Update" );
      break;
    case CVS::Commit:
      temp = i18n( "&Commit" );
      break;
    case CVS::Status:
      temp = i18n( "&Get Status" );
      break;
  }
  mainBtn = new QPushButton( temp, this );
  mainBtn->setDefault( true );
  buttons->addWidget( mainBtn );

  cancelBtn = new QPushButton( i18n( "C&ancel" ), this );
  buttons->addWidget( cancelBtn );
  layout->addLayout( buttons );

  QFrame * line = new QFrame( this );
  line->setFrameStyle( QFrame::HLine | QFrame::Sunken );
  layout->addWidget( line );

  layout->addWidget( new QLabel( i18n( "Command output:" ), this ) );

  output = new QTextEdit( this );
  output->setReadOnly( true );
  layout->addWidget( output );

  resize( QSize( 600, 450 ).expandedTo( minimumSizeHint( ) ) );

  if ( cmd == CVS::Commit )
    logedit->setFocus( );

  readSettings( );

  connect( mainBtn, SIGNAL( clicked( ) ), this, SLOT( slotExecuteCommand( ) ) );
  connect( cancelBtn, SIGNAL( clicked( ) ), this, SLOT( reject( ) ) );
}

CVSDialog::~CVSDialog()
{
    delete p;
}

void CVSDialog::accept( )
{
  saveSettings( );
  KDialog::accept( );
}

void CVSDialog::setFiles( const QStringList& files )
{
  filebox->insertStringList( files );
}

void CVSDialog::setCommandLine( const QString& command )
{
  _commandLine = command;
}

void CVSDialog::setAddCommand( const QString& command )
{
  _addCommand = command;
}

void CVSDialog::slotExecuteCommand( )
{
  // Nothing to do here.
  if ( _commandLine.isEmpty( ) ) return;

  output->append( i18n( "[ Starting command ]" ) );

  // Create a new shell process
  p = new KProcess;
  p->setUseShell( true, "/bin/sh" );

  if ( _cmd == CVS::Commit ) {
    // Include command for 'cvs add'.
    if ( autoAddBox->isChecked( ) && !_addCommand.isEmpty( ) )
      _commandLine.prepend( _addCommand );

    QString msg( logedit->text( ) );

    // FIXME: better use masking instead of remove.
    if ( !msg.isEmpty( ) )
      msg.remove( QRegExp( "'" ) );

    // Silent commit requested.
    if ( silentCommit->isChecked( ) )
      msg.prepend( "CVS_SILENT " );

    // Include the commit log message from the input field.
    _commandLine.replace( QRegExp( "@LOGMESSAGE@" ), "\'" + msg + "\'" );

    // Update the list of log messages
    QString temp = logedit->text( );
    if ( !temp.isEmpty( ) ) {
      // Remove the message from the list if it already exists
      if ( logMessages.findIndex( temp ) >= 0 )
        logMessages.remove( temp );
      // Prepend the current message to the list
      logMessages.prepend( temp );
    }
  }

  // Set the KProcess' command line.
  *p << _commandLine;

  connect( p, SIGNAL( receivedStdout( KProcess*, char*, int ) ),
    this, SLOT ( slotProcessStdout( KProcess*, char*, int ) ) );
  connect( p, SIGNAL( receivedStderr( KProcess*, char*, int ) ),
    this, SLOT ( slotProcessStderr( KProcess*, char*, int ) ) );
  connect( p, SIGNAL( processExited( KProcess* ) ),
    this, SLOT( slotProcessExited( KProcess* ) ) );

  if ( p->start( KProcess::NotifyOnExit, KProcess::Communication( KProcess::AllOutput ) ) ) {
    // Disable the main button (and the log edit if in commit mode) to
    // indicate activity.
    mainBtn->setEnabled( false );
    if ( _cmd == CVS::Commit )
      logedit->setEnabled( false );
  } else
    kdDebug( ) << "Process could not be startet." << endl;
}

void CVSDialog::slotProcessStdout( KProcess*, char * buffer, int len )
{
  output->append( QString::fromUtf8( buffer, len ) );
  // Set the cursor's position at the end of the output.
  output->setCursorPosition( output->lines( ), 0 );

  // If the command is 'cvs status' collect the output of stdout.
  if ( _cmd == CVS::Status )
    _statusOutput += QString::fromUtf8( buffer, len );
}

void CVSDialog::slotProcessStderr( KProcess*, char * buffer, int len )
{
  // If an error occurs while executing the command display stderr in
  // another color.
  QColor oldColor( output->color( ) );
  output->setColor( Qt::red );
  output->append( QString::fromUtf8( buffer, len ) );
  output->setColor( oldColor );
  output->setCursorPosition( output->lines( ), 0 );
}

void CVSDialog::slotProcessExited( KProcess * p )
{
  if ( p->exitStatus( ) )
    output->append( i18n( "[ Exited with status %1 ]" ).arg( p->exitStatus( ) ) );
  else
    output->append( i18n( "[ Finished ]" ) );

  // The command is finished. Now we can reconnect the main button.
  disconnect( mainBtn, 0, 0, 0 );
  mainBtn->setText( i18n( "&Close" ) );
  connect( mainBtn, SIGNAL( clicked( ) ), this, SLOT( accept( ) ) );

  // Reenable the button and the log edit now that the process is finished.
  mainBtn->setEnabled( true );
  if ( _cmd == CVS::Commit )
    logedit->setEnabled( true );
}

QString CVSDialog::statusOutput( )
{
  return _statusOutput;
}

void CVSDialog::readSettings( )
{
  // FIXME: does not care about different projects yet
  KConfig * config = KGlobal::config( );
  config->setGroup( "CVSSupport" );

  if ( _cmd == CVS::Commit ) {
    autoAddBox->setChecked( config->readBoolEntry( "AutoAddFiles", true ) );
    silentCommit->setChecked( config->readBoolEntry( "SilentCommit", true ) );

    // Fill the combobox with old messages.
    logMessages.clear( );
    for ( int cnt = 0; cnt < 10; cnt++ )
      if ( config->hasKey( QString( "CommitLogMessage%1" ).arg( cnt ) ) )
        logMessages << config->readEntry( QString( "CommitLogMessage%1" ).arg( cnt ) );
    oldMessages->insertStringList( logMessages );

    // Set the last log message as the one to be used.
    if ( config->readBoolEntry( "PresetLastUsedMessage", false ) )
      logedit->setText( logMessages.first( ) );
  }
}

void CVSDialog::saveSettings( )
{
  // FIXME: does not care about different projects yet
  KConfig * config = KGlobal::config( );
  config->setGroup( "CVSSupport" );
  if ( _cmd == CVS::Commit ) {
    config->writeEntry( "AutoAddFiles", autoAddBox->isChecked( ) );
    config->writeEntry( "SilentCommit", silentCommit->isChecked( ) );

    // Write the log messages to the config file.
    int cnt = 0;
    QStringList::Iterator it;
    for ( it = logMessages.begin( ); it != logMessages.end( ) && cnt < 10 ; ++it, ++cnt )
      config->writeEntry( QString( "CommitLogMessage%1" ).arg( cnt ), *it );
  }
}

#include "cvsdialog.moc"
