/*****************************************************************
 * drkonqi - The KDE Crash Handler
 * 
 * Copyright (C) 2000-2003 Hans Petter Bieker <bieker@kde.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *****************************************************************/

#include <tqlayout.h>
#include <tqhbox.h>
#include <tqlabel.h>

#include <kdialog.h>
#include <tdelocale.h>
#include <tdeglobal.h>
#include <tdeglobalsettings.h>
#include <tdefiledialog.h>
#include <tdemessagebox.h>
#include <kpushbutton.h>
#include <kstdguiitem.h>
#include <ktextbrowser.h>
#include <tdetempfile.h>

#include "backtrace.h"
#include "krashconf.h"
#include "debugger.h"
#include "debugger.moc"

KrashDebugger :: KrashDebugger (const KrashConfig *krashconf, TQWidget *parent, const char *name)
  : TQWidget( parent, name ),
    m_krashconf(krashconf),
    m_proctrace(0)
{
  TQVBoxLayout *vbox = new TQVBoxLayout( this, 0, KDialog::marginHint() );
  vbox->setAutoAdd(true);

  m_backtrace = new KTextBrowser(this);
  m_backtrace->setTextFormat(TQt::PlainText);
  m_backtrace->setFont(TDEGlobalSettings::fixedFont());

  TQWidget *w = new TQWidget( this );
  ( new TQHBoxLayout( w, 0, KDialog::marginHint() ) )->setAutoAdd( true );
  m_status = new TQLabel( w );
  m_status->setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Preferred ) );
  //m_copyButton = new KPushButton( KStdGuiItem::copy(), w );
  KGuiItem item( i18n( "C&opy" ), TQString::fromLatin1( "edit-copy" ) );
  m_copyButton = new KPushButton( item, w );
  connect( m_copyButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotCopy() ) );
  m_copyButton->setEnabled( false );
  m_saveButton = new KPushButton( m_krashconf->safeMode() ? KStdGuiItem::save() : KStdGuiItem::saveAs(), w );
  connect( m_saveButton, TQ_SIGNAL( clicked() ), this, TQ_SLOT( slotSave() ) );
  m_saveButton->setEnabled( false );
}

KrashDebugger :: ~KrashDebugger()
{
  // This will SIGKILL gdb and SIGCONT program which crashed.
  //  delete m_proctrace;
}

void KrashDebugger :: slotDone(const TQString& str)
{
  m_status->setText(i18n("Done."));
  m_copyButton->setEnabled( true );
  m_saveButton->setEnabled( true );
  m_backtrace->setText( m_prependText + str ); // replace with possibly post-processed backtrace
}

void KrashDebugger :: slotCopy()
{
  m_backtrace->selectAll();
  m_backtrace->copy();
}

void KrashDebugger :: slotSave()
{
  if (m_krashconf->safeMode())
  {
    KTempFile tf(TQString::fromAscii("/tmp/"), TQString::fromAscii(".tdecrash"), 0600);
    if (!tf.status())
    {
      *tf.textStream() << m_backtrace->text();
      tf.close();
      KMessageBox::information(this, i18n("Backtrace saved to %1").arg(tf.name()));
    }
    else
    {
      KMessageBox::sorry(this, i18n("Cannot create a file in which to save the backtrace"));
    }
  }
  else
  {
    TQString defname = m_krashconf->execName() + TQString::fromLatin1( ".tdecrash" );
    if( defname.contains( '/' ))
        defname = defname.mid( defname.findRev( '/' ) + 1 );
    TQString filename = KFileDialog::getSaveFileName(defname, TQString::null, this, i18n("Select Filename"));
    if (!filename.isEmpty())
    {
      TQFile f(filename);
      
      if (f.exists()) {
        if (KMessageBox::Cancel == 
            KMessageBox::warningContinueCancel( 0,
              i18n( "A file named \"%1\" already exists. "
                    "Are you sure you want to overwrite it?" ).arg( filename ),
              i18n( "Overwrite File?" ),
              i18n( "&Overwrite" ) ))
            return;       
      }           
      
      if (f.open(IO_WriteOnly))
      {
        TQTextStream ts(&f);
        ts << m_backtrace->text();
        f.close();
      }
      else
      {
        KMessageBox::sorry(this, i18n("Cannot open file %1 for writing").arg(filename));
      }
    }
  }
}

void KrashDebugger :: slotSomeError()
{
  m_status->setText(i18n("Unable to create a valid backtrace."));
  m_backtrace->setText(i18n("This backtrace appears to be of no use.\n"
      "This is probably because your packages are built in a way "
      "which prevents creation of proper backtraces, or the stack frame "
      "was seriously corrupted in the crash.\n\n" )
      + m_backtrace->text());
}

void KrashDebugger :: slotAppend(const TQString &str)
{
  m_status->setText(i18n("Loading backtrace..."));

  // append doesn't work here because it will add a newline as well
  m_backtrace->setText(m_backtrace->text() + str);
}

void KrashDebugger :: showEvent(TQShowEvent *e)
{
  TQWidget::showEvent(e);
  startDebugger();
}

void KrashDebugger :: startDebugger()
{
  // Only start one copy
  if (m_proctrace || !m_backtrace->text().isEmpty())
    return;

  TQString msg;
  bool checks = performChecks( &msg );
  if( !checks && !m_krashconf->disableChecks())
  {
    m_backtrace->setText( m_prependText +
        i18n( "The following options are enabled:\n\n" )
        + msg
        + i18n( "\nAs the usage of these options is not recommended -"
                " because they can, in rare cases, be responsible for TDE problems - a backtrace"
                " will not be generated.\n"
                "You need to turn these options off and reproduce"
                " the problem again in order to get a backtrace.\n" ));
    m_status->setText( i18n( "Backtrace will not be created."));
    return;
  }
  if( !msg.isEmpty())
  {
    m_prependText += msg + '\n';
    m_backtrace->setText( m_prependText );
  }
  m_status->setText(i18n("Loading symbols..."));

  m_proctrace = new BackTrace(m_krashconf, this);

  connect(m_proctrace, TQ_SIGNAL(append(const TQString &)),
          TQ_SLOT(slotAppend(const TQString &)));
  connect(m_proctrace, TQ_SIGNAL(done(const TQString&)), TQ_SLOT(slotDone(const TQString&)));
  connect(m_proctrace, TQ_SIGNAL(someError()), TQ_SLOT(slotSomeError()));

  m_proctrace->start();
}

// this function check for "dangerous" settings, returns false
// and message in case some of them are activated
bool KrashDebugger::performChecks( TQString* msg )
{
  bool ret = true;
  TDEConfig kdedcfg( TQString::fromLatin1( "kdedrc" ), true );
  kdedcfg.setGroup( "General" );
  if( kdedcfg.readBoolEntry( "DelayedCheck", false ))
  {
    // ret = false; it's not that dangerous
    *msg += i18n( "System configuration startup check disabled.\n" );
  }
  return ret;
}
