/***************************************************************************
                          dcdebug.cpp  -  description
                             -------------------
    begin                : Don Sep 25 2003
    copyright            : (C) 2003 by Mathias Küster
    email                : mathen@users.berlios.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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef WIN32
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#endif

#include <qprocess.h>
#include <qpushbutton.h>
#include <qfile.h>
#include <qprocess.h>
#include <qapplication.h>
#include <qtextedit.h>
#include <qmessagebox.h>
#include <qfiledialog.h>

#include <dcconfig.h>
#include <dclib/dcos.h>
#include <dclib/dclib.h>
#include <dclib/chttp.h>
#include <dclib/core/cstring.h>

#include "dcdebug.h"

CString DCDebug::arg_0 = "";
CString DCDebug::startup_dir = "";

/** */
DCDebug::DCDebug( QWidget *parent, const char *name ) : DCDialogDebug(parent,name)
{
	m_pHttp = 0;

	InitDocument();
}

/** */
DCDebug::~DCDebug()
{
	m_Timer.stop();

	if ( m_pHttp )
	{
		delete m_pHttp;
	}
}

/** */
void DCDebug::InitDocument()
{
	connect( PushButton_EXIT, SIGNAL(clicked()), this, SLOT(accept()) );
	connect( PushButton_SEND, SIGNAL(clicked()), this, SLOT(slotSendBacktrace()) );
	connect( PushButton_SAVE, SIGNAL(clicked()), this, SLOT(slotSaveBacktrace()) );

	// enable timer
	connect( &m_Timer, SIGNAL(timeout()), this, SLOT(timerDone()) );
}

/** */
bool DCDebug::Init( QString configpath, QString debugopt )
{
	bool res = FALSE;
#ifndef WIN32
	int i;
	QString s = "bt full\nq\n";
	QString filename = configpath+"/"+"gdbrc";
	QFile f(filename);
	QString s1,s2,s3;
	QProcess pr;

	m_sBuffer = "";

	s1 = debugopt.section(",",0,0);
	s2 = debugopt.section(",",1,1);
	s3 = debugopt.section(",",2,2);

	printf("Init debug:\nConfigpath: '%s'\n%s %s %s\n",
			configpath.ascii(),
			s1.ascii(),
			s2.ascii(),
			s3.ascii() ); fflush(stdout);

	f.open( IO_WriteOnly | IO_Truncate );
	f.writeBlock(s,s.length());
	f.close();

	pr.addArgument("gdb");
	pr.addArgument("--nw");
	pr.addArgument("--nx");
	pr.addArgument("--quiet");
	pr.addArgument("--batch");
	pr.addArgument("-x");
	pr.addArgument(filename);
	pr.addArgument(s3);
	pr.addArgument(s1);

	if ( pr.start() == TRUE )
	{
		while( pr.isRunning() )
		{
			sleep(1);
		}

		QString s;
		while( (s = pr.readLineStdout()) != QString::null )
			m_sBuffer += s + "\n";
		while( (s = pr.readLineStderr()) != QString::null )
			m_sBuffer += s + "\n";

		s = "";

		// get version
		QFile f1("/proc/version");

		if ( f1.open( IO_ReadOnly ) )
		{
			while ( (i=f1.getch()) != -1 )
				s += i;

			f1.close();

			s += "\n";
		}

		// create output text
		s += "Valknut: ";
		s += VERSION;
		s += "\n";
		s += "DCLIB: ";
		s += dclibVersion();
		s += "\n";
		s += "QT compiled: ";
		s += QT_VERSION_STR;
		s += "\n";
		s += "QT used: ";
		s += qVersion();
		s += "\n";
		s += "LIBXML compiled: ";
		s += LIBXML_VERSION_STRING;
		s += "\n";
		s += "LIBXML used: ";
		s += xmlParserVersion;
		s += "\n\n";

		TextEdit_LOG->setText(s + m_sBuffer);

		res = TRUE;
	}
	else
	{
		// TODO: error message
		printf("start failed\n"); fflush(stdout);
	}
#endif
	return res;
}

/** */
void DCDebug::timerDone()
{
	if ( m_bTransfer == FALSE )
	{
		if ( m_pHttp )
		{
			if ( m_pHttp->GetHttpError() != 200 )
			{
				QMessageBox::critical( this, tr("Send error"),
		                	tr("Could not send the data. Error '%1'.").arg(m_pHttp->GetHttpError()) );

				PushButton_SEND->setEnabled(TRUE);
			}
			else
			{
				QMessageBox::critical( this, tr("Send"),
		                	tr("Thanks for your help.") );
			}
		}

		PushButton_SAVE->setEnabled(TRUE);
		PushButton_EXIT->setEnabled(TRUE);
	}
	else
	{
		m_Timer.start( 500, TRUE );
	}
}

/** */
void DCDebug::slotSendBacktrace()
{
	QString text;

	if ( m_pHttp )
		delete m_pHttp;

	PushButton_SAVE->setEnabled(FALSE);
	PushButton_SEND->setEnabled(FALSE);
	PushButton_EXIT->setEnabled(FALSE);

	m_bTransfer = TRUE;
	m_Timer.start( 500, TRUE );

	m_pHttp = new CHttp();
	m_pHttp->SetCallBackFunction( new CCallback<DCDebug>( this, &DCDebug::HttpCallBack ) );

	text  = "--- Comment\n\n";
	text += TextEdit_COMMENT->text() + "\n";
	text += "--- Backtrace\n\n";
	text += TextEdit_LOG->text() + "\n";
	text += "--- End\n";

	m_pHttp->GetUrl("http://dcgui.berlios.de/crash.php",text.ascii());
}

/** */
void DCDebug::slotSaveBacktrace()
{
	QString s = QFileDialog::getSaveFileName(
                    "",
                    "Text (*.txt)",
                    this,
                    "save file dialog"
                    "Choose a filename to save under" );

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

	QFile *file = new QFile(s);

	if ( !file->open( IO_WriteOnly ) )
	{
        	QMessageBox::critical( this, tr("Save error"),
                	tr("Can't open file '%1' for writing.").arg(s) );
	}
	else
	{
		file->writeBlock( TextEdit_LOG->text().ascii(), TextEdit_LOG->text().length() ); 
		file->close();
	}

	delete file;
}

/** http callback function */
int DCDebug::HttpCallBack( CObject *, CObject * object )
{
	switch(((CDCMessage*)object)->m_eType)
	{
		case DC_MESSAGE_CONNECTION_STATE:
		{
			CMessageConnectionState *msg = (CMessageConnectionState*)object;

			if ( msg->m_eState == estDISCONNECTED )
			{
				m_bTransfer = FALSE;
			}

			break;
		}

		case DC_MESSAGE_TRANSFER:
		{
			break;
		}

		default:
		{
			break;
		}
	}

	if ( object )
	{
		delete object;
	}

	return 0;
}

#ifndef WIN32
/** */
static void crash_handler( int sig );

/*!
 *\brief	install crash handlers
 */
void crash_install_handlers(void)
{
	sigset_t mask;
	sigemptyset(&mask);

#ifdef SIGSEGV
	signal(SIGSEGV, crash_handler);
	sigaddset(&mask, SIGSEGV);
#endif
	
#ifdef SIGFPE
	signal(SIGFPE, crash_handler);
	sigaddset(&mask, SIGFPE);
#endif

#ifdef SIGILL
	signal(SIGILL, crash_handler);
	sigaddset(&mask, SIGILL);
#endif

#ifdef SIGABRT
	signal(SIGABRT, crash_handler);
	sigaddset(&mask, SIGABRT);
#endif

#ifdef SIGTRAP
	signal(SIGTRAP, crash_handler);
	sigaddset(&mask, SIGTRAP);
#endif

#ifdef SIGTERM
//	signal(SIGTERM, crash_handler);
//	sigaddset(&mask, SIGTERM);
#endif

	sigprocmask(SIG_UNBLOCK, &mask, 0);
}

/*!
 *\brief	this handler will probably evolve into 
 *		something better.
 */
static void crash_handler(int sig)
{
	pid_t pid;
	static volatile unsigned long crashed_ = 0;
                             
	/*
	 * let's hope startup_dir and argv0 aren't trashed.
	 * both are defined in main.c.
	 */
	if (sig < 0 || sig > 32) { /* what's that ? */
//		char *buf = g_strdup_printf(
//				_("Caught strange signal (%d). This is probably\n"
//				  "due to a broken compilation; Exiting."), sig);
//		ay_do_error( _("Error"), buf );
//		g_free(buf);
		printf("SIGNAL_HANDLER: unknown signal %d\n",sig);
		_exit(0);
	}
	/*
	 * besides guarding entrancy it's probably also better 
	 * to mask off signals
	 */
	if (crashed_) _exit(253);

	crashed_++;

#ifdef SIGTERM
//	if (sig == SIGTERM)
//	{
//		printf("SIGNAL_HANDLER: SIGTERM catched\n");
//		exit(1);
//	}
#endif

	printf("CRASH CRASH CRASH \n");fflush(stdout);

	QProcess pr;
	CString cp = g_pConfig->GetConfigPath().Data();

	if (0 == (pid = fork()))
	{
		char buf[50];
		char *args[6];
		CString cp = g_pConfig->GetConfigPath().Data();

		args[0] = DCDebug::arg_0.Data(); 
		args[1] = "-c";
		args[2] = cp.Data();
		args[3] = "-C";
		snprintf(buf, sizeof(buf), "%d,%d,%s", getppid(), sig, DCDebug::arg_0.Data());
		args[4] = buf;
		args[5] = NULL;

		chdir(DCDebug::startup_dir.Data());
		setgid(getgid());
		setuid(getuid());
		execvp(DCDebug::arg_0.Data(), args);
	} else {
		waitpid(pid, NULL, 0);
		_exit(253);
	}

	_exit(253);
}

#endif

