/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "version.h"
#include "DebuggerMain.h"
#include "Debugger.h"
#include "State.h"
#include "CommentServer.h"
#include "ArchProcessorInterface.h"
#include "DebuggerPluginInterface.h"
#include "DebugEventServer.h"
#include "ByteShiftArray.h"
#include "SymbolManager.h"
#include "DialogAttach.h"
#include "DialogMemoryRegions.h"
#include "Configuration.h"
#include "Expression.h"
#include "BinaryInfo.h"
#include "DebuggerCoreInterface.h"
#include "RecentFileManager.h"
#include "AnalyzerInterface.h"
#include "DialogPlugins.h"
#include "SessionFileInterface.h"
#include "Core.h"
#include "QHexView.h"
#include "DebuggerOps.h"
#include "Instruction.h"

#include <QDir>
#include <QToolButton>
#include <QMessageBox>
#include <QStringListModel>
#include <QFileDialog>
#include <QInputDialog>
#include <QSettings>
#include <QVector>
#include <QtDebug>
#include <QTimer>

#include <sys/types.h>
#include <sys/stat.h>

#include <cstring>

#include <boost/bind.hpp>


//------------------------------------------------------------------------------
// Name: DebuggerMain(QWidget *parent)
// Desc: constructor
//------------------------------------------------------------------------------
DebuggerMain::DebuggerMain(QWidget *parent) : QMainWindow(parent), 
		m_RecentFileManager(new RecentFileManager),
		m_StackCommentServer(new CommentServer),
		m_DebugEventServer(new DebugEventServer(this)), 
		m_TTYProc(new QProcess(this)),
		m_GUIState(TERMINATED), 
		m_StackViewLocked(false),
		m_StepWasRun(false), 
		m_ReenableBreakpointAddress(0) 
#ifdef Q_OS_WIN32
		,m_Timer(new QTimer(this))
#endif
		{

	setupUI();
		
	// register our DebugEvent type so it can be used in 
	// inter-thread signals
	qRegisterMetaType<DebugEvent>("DebugEvent");
		
	// setup the list model for instruction details list
	m_ListModel = new QStringListModel(this);
	ui.listView->setModel(m_ListModel);
	
	// setup the recent file manager
	ui.action_Recent_Files->setMenu(m_RecentFileManager->createMenu());
	connect(m_RecentFileManager.get(), SIGNAL(fileSelected(const QString &)), SLOT(doOpenFile(const QString &)));

	// make us the default event handler
	edb::v1::setDebugEventHandler(this);

	// enable the arch processor
	edb::v1::archProcessor().setupRegisterView(ui.registerList);
			
	// default the working directory to ours
	m_WorkingDirectory = QDir().absolutePath();
}

//------------------------------------------------------------------------------
// Name: setupStackView()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::setupStackView() {
	m_StackView = new QHexView(this);
	ui.stackDock->setWidget(m_StackView);
	
	// setup the context menu
	m_StackView->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(m_StackView, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(mnuStackContextMenu(const QPoint &)));
	
	// we placed a view in the designer, so just set it here
	// this may get transitioned to heap allocated, we'll see
	m_StackViewInfo.view = boost::shared_ptr<QHexView>(m_StackView);
	
	// setup the comment server for the stack viewer
	m_StackView->setCommentServer(m_StackCommentServer);
}

//------------------------------------------------------------------------------
// Name: setupUI()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::setupUI() {
	// setup the global pointers as early as possible. 
	// NOTE:  this should never be changed after this point
	// NOTE:  this is important that this happens BEFORE any components which
	// read settings as it could end up being a memory leak (and therefore never
	// calling it's destructor which writes the settings to disk!).
	edb::v1::debuggerUI = this;

	ui.setupUi(this);
	
	// add toggles for the dock windows
	ui.menu_View->addAction(ui.registersDock->toggleViewAction());
	ui.menu_View->addAction(ui.dataDock->toggleViewAction());
	ui.menu_View->addAction(ui.stackDock->toggleViewAction());
	ui.menu_View->addAction(ui.toolBar->toggleViewAction());
	
	// make sure our widgets use custom context menus
	ui.registerList->setContextMenuPolicy(Qt::CustomContextMenu);
	ui.cpuView->setContextMenuPolicy(Qt::CustomContextMenu);
	
	// set the listbox to about 4 lines
	const QFontMetrics &fm = ui.listView->fontMetrics();
	ui.listView->setFixedHeight(fm.height() * 4);
	
	// setup stack view
	setupStackView();
	
	// setup tabs
	setupTabButtons();
	
	// remove the one in the designer and put in our built one.
	// it's a bit ugly to do it this way, but the designer wont let me 
	// make a tabless entry..and the ones i create look slightly diff 
	// (probably lack of layout manager in mine...
	ui.tabWidget->removeTab(0);
	mnuDumpCreateTab();
	
	// apply any fonts we may have stored
	doApplyDefaultFonts();
	
	// apply the default setting for showing address seperators
	doApplyDefaultShowSeperator();
}

//------------------------------------------------------------------------------
// Name: ~DebuggerMain()
// Desc: destructor
//------------------------------------------------------------------------------
DebuggerMain::~DebuggerMain() {

	doDetachFromProcess(edb::v1::config().closeBehaviour == Configuration::Terminate);
		
	// remove the last tab
	mnuDumpDeleteTab();
	
	// kill our xterm and wait for it to die
	m_TTYProc->kill();
	m_TTYProc->waitForFinished(3000);
}

//------------------------------------------------------------------------------
// Name: closeEvent(QCloseEvent *)
// Desc: triggered on main window close, saves window state
//------------------------------------------------------------------------------
void DebuggerMain::closeEvent(QCloseEvent *) {
	QSettings settings;
	const QByteArray state = saveState();
	settings.beginGroup("Window");
	settings.setValue("window.state", state);
	settings.setValue("window.width", width());
	settings.setValue("window.height", height());
	settings.setValue("window.stack.show_address.enabled", m_StackView->showAddress());
	settings.setValue("window.stack.show_hex.enabled", m_StackView->showHexDump());
	settings.setValue("window.stack.show_ascii.enabled", m_StackView->showAsciiDump());
	settings.setValue("window.stack.show_comments.enabled", m_StackView->showComments());
	settings.setValue("window.stack.row_width", m_StackView->rowWidth());
	settings.setValue("window.stack.word_width", m_StackView->wordWidth());
	settings.endGroup();
}

//------------------------------------------------------------------------------
// Name: showEvent(QShowEvent *)
// Desc: triggered on show, restores window state
//------------------------------------------------------------------------------
void DebuggerMain::showEvent(QShowEvent *) {
	QSettings settings;
	settings.beginGroup("Window");
	const QByteArray state = settings.value("window.state").value<QByteArray>();
	const int width = settings.value("window.width", -1).value<int>();
	const int height = settings.value("window.height", -1).value<int>();
	
	if(width != -1) {
		resize(width, size().height());
	}
	
	if(height != -1) {
		resize(size().width(), height);
	}
	
	m_StackView->setShowAddress(settings.value("window.stack.show_address.enabled", true).value<bool>());
	m_StackView->setShowHexDump(settings.value("window.stack.show_hex.enabled", true).value<bool>());
	m_StackView->setShowAsciiDump(settings.value("window.stack.show_ascii.enabled", false).value<bool>());
	m_StackView->setShowComments(settings.value("window.stack.show_comments.enabled", true).value<bool>());

	int rowWidth = settings.value("window.stack.row_width", 1).value<int>();
	int wordWidth = settings.value("window.stack.word_width", QT_POINTER_SIZE).value<int>();
	
	// normalize values
	if(wordWidth != 1 && wordWidth != 2 && wordWidth != 4 && wordWidth != 8) {
		wordWidth = QT_POINTER_SIZE;
	}
	
	if(rowWidth != 1 && rowWidth != 2 && rowWidth != 4 && rowWidth != 8 && rowWidth != 16) {
		rowWidth = 1;
	}
	
	m_StackView->setRowWidth(rowWidth);
	m_StackView->setWordWidth(wordWidth);
	
	settings.endGroup();
	restoreState(state);
}

//------------------------------------------------------------------------------
// Name: on_actionAbout_QT_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionAbout_QT_triggered() {
	QMessageBox::aboutQt(this, tr("About QT"));
}

//------------------------------------------------------------------------------
// Name: doApplyDefaultFonts()
// Desc: applies the confirations fonts to all neccessary widgets
//------------------------------------------------------------------------------
void DebuggerMain::doApplyDefaultFonts() {

	QFont font;
	const Configuration &config = edb::v1::config();

	// set some default fonts
	if(font.fromString(config.stackFont)) {
		m_StackView->setFont(font);
	}
	
	if(font.fromString(config.registersFont)) {
		ui.registerList->setFont(font);
	}

	if(font.fromString(config.disassemblyFont)) {
		ui.cpuView->setFont(font);
	}
	
	if(font.fromString(config.dataFont)) {
		foreach(boost::shared_ptr<DataViewInfo> dataView, m_DataRegions) {
			dataView->view->setFont(font);
		}
	}
}

//------------------------------------------------------------------------------
// Name: finishPluginSetup(const QMap<QString, QObject *> &plugins)
// Desc: finalizes plugin setup by adding each to the menu, we can do this now 
//       that we have a GUI widget to attach it to
//------------------------------------------------------------------------------
void DebuggerMain::finishPluginSetup(const QMap<QString, QObject *> &plugins) {
	
	PluginAPI api;
	
	// version 1.0
	api.version								= 0x010000;
	api.debuggerCore						= edb::v1::debuggerBase;
	api.debuggerUI							= this;
	api.currentState						= edb::v1::currentState;
	api.symbolManager						= edb::v1::symbolManager;
	api.memoryRegions						= edb::v1::memoryRegions;
	api.archProcessor						= edb::v1::archProcessor;
	api.removeBreakpoint					= edb::v1::removeBreakpoint;
	api.createBreakpoint					= edb::v1::createBreakpoint;
	api.toggleBreakpoint					= edb::v1::toggleBreakpoint;
	api.dumpDataRange						= edb::v1::dumpDataRange;
	api.dumpData							= edb::v1::dumpData;
	api.dumpStack							= edb::v1::dumpStack;
	api.jumpToAddress						= edb::v1::jumpToAddress;
	api.getValueFromUser					= edb::v1::getValueFromUser;
	api.getBinaryStringFromUser				= edb::v1::getBinaryStringFromUser;
	api.getASCIIStringAtAddress				= edb::v1::getASCIIStringAtAddress;
	api.getUTF16StringAtAddress				= edb::v1::getUTF16StringAtAddress;
	api.loadedLibraries						= edb::v1::loadedLibraries;
	api.config								= edb::v1::config;
	api.edbVersion							= edb::v1::edbVersion;
	api.findFunctionSymbol					= edb::v1::findFunctionSymbol;
	api.getProcessCurrentWorkingDirectory	= edb::v1::getProcessCurrentWorkingDirectory;
	api.getProcessExecutable				= edb::v1::getProcessExecutable;
	api.getProcessArguments					= edb::v1::getProcessArguments;
	api.getDWORD							= edb::v1::getDWORD;
	api.getVariable							= edb::v1::getVariable;
	api.setDebugEventHandler				= edb::v1::setDebugEventHandler;
	api.debugEventHandler					= edb::v1::debugEventHandler;
	api.setAnalyzer							= edb::v1::setAnalyzer;
	api.analyzer							= edb::v1::analyzer;
	api.setSessionFileHandler				= edb::v1::setSessionFileHandler;
	api.sessionFileHandler					= edb::v1::sessionFileHandler;
	api.getInstructionBytes					= edb::v1::getInstructionBytes;
	api.getBinaryInfo						= edb::v1::getBinaryInfo;
	api.getFunctionInfo						= edb::v1::getFunctionInfo;
	api.locateMainFunction					= edb::v1::locateMainFunction;
	api.findPluginByName					= edb::v1::findPluginByName;
	api.reloadSymbols						= edb::v1::reloadSymbols;
	api.repaintCPUView						= edb::v1::repaintCPUView;
	api.primaryDataRegion					= edb::v1::primaryDataRegion;
	api.registerBinaryInfo					= edb::v1::registerBinaryInfo;
	api.setFunctionDB						= edb::v1::setFunctionDB;
	
	// call the init function for each plugin
	foreach(QObject *plugin, plugins) {
		DebuggerPluginInterface *const p = qobject_cast<DebuggerPluginInterface *>(plugin);
		if(p != 0) {
			p->init(api);
		}
	}
	
	// setup the menu for all plugins that which to do so
	foreach(QObject *plugin, plugins) {
		DebuggerPluginInterface *const p = qobject_cast<DebuggerPluginInterface *>(plugin);
		if(p != 0) {
			QMenu *const menu = p->menu(this);
			if(menu != 0) {
				ui.menu_Plugins->addMenu(menu);
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: setupTabButtons()
// Desc: creates the add/remove tab buttons in the data view
//------------------------------------------------------------------------------
void DebuggerMain::setupTabButtons() {
	// add the corner widgets to the data view
	m_AddTab = new QToolButton(ui.tabWidget);
	m_DelTab = new QToolButton(ui.tabWidget);
	
	m_AddTab->setToolButtonStyle(Qt::ToolButtonIconOnly);
	m_DelTab->setToolButtonStyle(Qt::ToolButtonIconOnly);
	
	m_AddTab->setIcon(QIcon(":/debugger/images/edb16-addtab.png"));
	m_DelTab->setIcon(QIcon(":/debugger/images/edb16-deltab.png"));
	m_AddTab->setAutoRaise(true);
	m_DelTab->setAutoRaise(true);
	m_DelTab->setEnabled(false);
	
	ui.tabWidget->setCornerWidget(m_AddTab, Qt::TopLeftCorner);
	ui.tabWidget->setCornerWidget(m_DelTab, Qt::TopRightCorner);
	
	connect(m_AddTab, SIGNAL(clicked()), SLOT(mnuDumpCreateTab()));
	connect(m_DelTab, SIGNAL(clicked()), SLOT(mnuDumpDeleteTab()));
}

//------------------------------------------------------------------------------
// Name: on_registerList_customContextMenuRequested(const QPoint &pos)
// Desc: context menu handler for register view
//------------------------------------------------------------------------------
void DebuggerMain::on_registerList_customContextMenuRequested(const QPoint &pos) {
	QTreeWidgetItem *const item = ui.registerList->itemAt(pos);
	if(item != 0) {
		if(!ui.registerList->isCatagory(item)) {
		
			// a little bit cheesy of a solution, but should work nicely
			edb::reg_t *const value = edb::v1::archProcessor().valueFromItem(*item, State::REG_GPR | State::REG_IP);
			if(value != 0) {
				boost::scoped_ptr<QMenu> menu(new QMenu(this));
				menu->addAction(tr("&Follow In Dump"), this, SLOT(mnuRegisterFollowInDump()));
				menu->addAction(tr("&Follow In Dump (New Tab)"), this, SLOT(mnuRegisterFollowInDumpNewTab()));
				menu->addAction(tr("&Follow In Stack"), this, SLOT(mnuRegisterFollowInStack()));
				
				doAddPluginContextMenu<&DebuggerPluginInterface::registerViewContextMenu>(menu);
								
				menu->exec(ui.registerList->mapToGlobal(pos));
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: on_cpuView_breakPointToggled(edb::address_t address)
// Desc: handler for toggling the breakpoints
//------------------------------------------------------------------------------
void DebuggerMain::on_cpuView_breakPointToggled(edb::address_t address) {
	edb::v1::toggleBreakpoint(address);
}

//------------------------------------------------------------------------------
// Name: on_registerList_itemDoubleClicked(QTreeWidgetItem *item)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_registerList_itemDoubleClicked(QTreeWidgetItem *item) {	
	Q_CHECK_PTR(item);
	edb::reg_t *const value = edb::v1::archProcessor().valueFromItem(*item, State::REG_ANY);
	
	if(value != 0 && edb::v1::getValueFromUser(*value, tr("Register Value"))) {
		edb::v1::debuggerBase->setState(edb::v1::currentState());
		updateGUI();
	}
}

//------------------------------------------------------------------------------
// Name: on_action_About_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_About_triggered() {
	QMessageBox::about(this, tr("About EDB"),
		QString(
			tr(
			"<p>EDB (Evan's Debugger) is designed to be an easy to use, modular, and cross platform debugger.</p>"
			"<p>More information and updates can be found at <a href=\"http://www.codef00.com\">http://www.codef00.com</a></p>"
			"<p>You can also report bugs an feature requests at <a href=\"http://bugs.codef00.com\">http://bugs.codef00.com</a></p>"
			"<p>Written by Evan Teran.</p>"
			"<p>version: %1</p>"
			)).arg(version_string));
}

//------------------------------------------------------------------------------
// Name: doApplyDefaultShowSeperator()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doApplyDefaultShowSeperator() {
	const bool show = edb::v1::config().showAddressSeperator;
	
	ui.cpuView->setShowAddressSeparator(show);
	m_StackView->setShowAddressSeparator(show);
	foreach(boost::shared_ptr<DataViewInfo> dataView, m_DataRegions) {
		dataView->view->setShowAddressSeparator(show);
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Configure_Debugger_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Configure_Debugger_triggered() {
	
	if(edb::v1::dialogOptions()->exec() == QDialog::Accepted) {	

		// reload symbols in case they changed, or our symbol files changes
		edb::v1::reloadSymbols();
		
		// re-read the memory region information
		edb::v1::memoryRegions().sync();
		
		// apply the selected fonts
		doApplyDefaultFonts();
		
		// apply changes to the GUI options
		doApplyDefaultShowSeperator();
		
		// show changes
		refreshGUI();
	}
}

//------------------------------------------------------------------------------
// Name: getGotoExpression(bool &ok)
// Desc: 
//------------------------------------------------------------------------------
edb::address_t DebuggerMain::getGotoExpression(bool &ok) {
    const QString text = QInputDialog::getText(this, tr("Goto Address"), tr("Address:"), QLineEdit::Normal, QString(), &ok);
	
	if (ok && !text.isEmpty()) {
		Expression<edb::address_t> expr(text, &edb::v1::getVariable, 0, &edb::v1::getDWORD, 0);

		ExpressionError err;
		const edb::address_t address = expr.evaluateExpression(ok, err);
		if(ok) {
			return address;
		} else {
			QMessageBox::information(this, tr("Error In Address Expression!"), err.what());
		}
	}
	return 0;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doMemoryFollow(bool ok, edb::address_t address, bool (*followFunc)(edb::address_t)) {
	if(ok && !(*followFunc)(address)) {
		QMessageBox::information(this, 
			tr("No Memory Found"), 
			QString(tr("There appears to be no memory at that location (<strong>0x%1</strong>)")).arg(address, EDB_MAX_HEX, 16, QChar('0')));
	}
}

//------------------------------------------------------------------------------
// Name: followRegisterInDump(bool tabbed)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::followRegisterInDump(bool tabbed) {
	bool ok;
	const edb::address_t address = getFollowRegister(ok);
	if(ok && !edb::v1::dumpData(address, tabbed)) {
		QMessageBox::information(this, 
			tr("No Memory Found"), 
			QString(tr("There appears to be no memory at that location (<strong>0x%1</strong>)")).arg(address, EDB_MAX_HEX, 16, QChar('0')));
	}
}

//------------------------------------------------------------------------------
// Name: mnuStackGotoESP()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackGotoESP() {
	doMemoryFollow(true, edb::v1::currentState().stackPointer(), edb::v1::dumpStack);
}

//------------------------------------------------------------------------------
// Name: mnuStackGotoEBP()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackGotoEBP() {
	doMemoryFollow(true, edb::v1::currentState().framePointer(), edb::v1::dumpStack);
}

//------------------------------------------------------------------------------
// Name: mnuCPUJumpToEIP()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUJumpToEIP() {
	doMemoryFollow(true, edb::v1::currentState().instructionPointer(), edb::v1::jumpToAddress);
}

//------------------------------------------------------------------------------
// Name: mnuCPUJumpToAddress()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUJumpToAddress() {
	bool ok;
	const edb::address_t address = getGotoExpression(ok);
	doMemoryFollow(ok, address, edb::v1::jumpToAddress);
}

//------------------------------------------------------------------------------
// Name: mnuDumpGotoAddress()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuDumpGotoAddress() {
    bool ok;
	const edb::address_t address = getGotoExpression(ok);
	doMemoryFollow(ok, address, edb::v1::dumpData);
}

//------------------------------------------------------------------------------
// Name: mnuStackGotoAddress()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackGotoAddress() {
    bool ok;
	const edb::address_t address = getGotoExpression(ok);
	doMemoryFollow(ok, address, edb::v1::dumpStack);
}

//------------------------------------------------------------------------------
// Name: mnuRegisterFollowInStack()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuRegisterFollowInStack() {
	bool ok;
	const edb::address_t address = getFollowRegister(ok);
	doMemoryFollow(ok, address, edb::v1::dumpStack);
}

//------------------------------------------------------------------------------
// Name: doFollowInStack(const QHexView *hv)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doFollowInStack(const QHexView *hv) {
	bool ok;
	const edb::address_t address = getFollowAddress(hv, ok);
	doMemoryFollow(ok, address, edb::v1::dumpStack);
}

//------------------------------------------------------------------------------
// Name: doFollowInDump(const QHexView *hv)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doFollowInDump(const QHexView *hv) {
	bool ok;
	const edb::address_t address = getFollowAddress(hv, ok);
	doMemoryFollow(ok, address, edb::v1::dumpData);
}

//------------------------------------------------------------------------------
// Name: getFollowRegister(bool &ok)
// Desc: 
//------------------------------------------------------------------------------
edb::reg_t DebuggerMain::getFollowRegister(bool &ok) {
	ok = false;
	const QTreeWidgetItem *const i = ui.registerList->currentItem();
	edb::reg_t ret = 0;
	if(i != 0) {
		edb::reg_t *const p = edb::v1::archProcessor().valueFromItem(*i, State::REG_GPR | State::REG_IP);

		if(p != 0) {
			ok = true;
			ret = *p;
		}
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: getFollowAddress(const QHexView *hv, bool &ok)
// Desc: 
//------------------------------------------------------------------------------
edb::address_t DebuggerMain::getFollowAddress(const QHexView *hv, bool &ok) {
	ok = false;
	
	Q_CHECK_PTR(hv);
	
	if(hv->hasSelectedText()) {
		const QByteArray data = hv->selectedBytes();

		if(data.size() == sizeof(edb::address_t)) {
			edb::address_t d;
			std::memcpy(&d, data.data(), sizeof(edb::address_t));

			ok = true;
			return d;
		}
	}
	
	QMessageBox::information(this, 
		tr("Invalid Selection"), 
		QString(tr("Please select a %1 bytes to use this function.")).arg(sizeof(edb::address_t)));
		
	return 0;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuDumpFollowInDump() {
	doFollowInDump(qobject_cast<QHexView *>(ui.tabWidget->currentWidget()));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuDumpFollowInStack() {
	doFollowInStack(qobject_cast<QHexView *>(ui.tabWidget->currentWidget()));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackFollowInDump() {
	doFollowInDump(m_StackView);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackFollowInStack() {
	doFollowInStack(m_StackView);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionApplication_Arguments_triggered() {
    bool ok;
    const QString text = QInputDialog::getText(this, tr("Application Arguments"), tr("Arguments:"), QLineEdit::Normal, m_Arguments, &ok);
	if(ok) {
		m_Arguments = text;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionApplication_Working_Directory_triggered() {
    bool ok;
    const QString text = QInputDialog::getText(this, tr("Application Working Directory"), tr("Working Directory:"), QLineEdit::Normal, m_WorkingDirectory, &ok);
	if(ok) {
		m_WorkingDirectory = text;
	}
}

//------------------------------------------------------------------------------
// Name: mnuStackPush()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackPush() {
	edb::reg_t value = 0;
	State &state = edb::v1::currentState();
	
	// ask for a replacement
	if(edb::v1::getValueFromUser(value, tr("Enter value to push"))) {

		// if they said ok, do the push, just like the hardware would do
		edb::v1::pushValue(state, value);

		// update the state
		edb::v1::debuggerBase->setState(state);
		updateGUI();
	}
}

//------------------------------------------------------------------------------
// Name: mnuStackPop()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackPop() {
	State &state = edb::v1::currentState();
	edb::v1::popValue(state);
	edb::v1::debuggerBase->setState(state);
	updateGUI();
}

//------------------------------------------------------------------------------
// Name: on_cpuView_customContextMenuRequested(const QPoint &pos)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_cpuView_customContextMenuRequested(const QPoint &pos) {
	boost::scoped_ptr<QMenu> menu(new QMenu(this));
	// TODO: add comment
	//menu->addAction(tr("Add &Comment"), this, SLOT(""));
	//menu->addSeparator();
	menu->addAction(tr("&Goto Address"), this, SLOT(mnuCPUJumpToAddress()));
	menu->addAction(tr("&Goto EIP"), this, SLOT(mnuCPUJumpToEIP()));
	
	const edb::address_t address 	= ui.cpuView->selectedAddress();
	int size						= ui.cpuView->selectedSize();

	quint8 buffer[edb::Instruction::MAX_SIZE + 1];
	if(edb::v1::getInstructionBytes(address, buffer, size)) {
		edb::Instruction insn(buffer, size, address, std::nothrow);
		if(insn.valid()) {
		
			switch(insn.type()) {
			case edb::Instruction::OP_JMP:
			case edb::Instruction::OP_CALL:
			case edb::Instruction::OP_JCC:
				if(insn.operand(0).generalType() == edb::Operand::TYPE_REL) {
				
					QAction *const action = menu->addAction(tr("&Follow"), this, SLOT(mnuCPUFollow()));
					action->setData(static_cast<qlonglong>(insn.operand(0).relativeTarget()));
				}
				break;
			default:
				for(std::size_t i = 0; i < insn.operandCount(); ++i) {
					if(insn.operand(i).generalType() == edb::Operand::TYPE_IMMEDIATE) {
						
						QAction *const action = menu->addAction(tr("Follow Constant In &Dump"), this, SLOT(mnuCPUFollowInDump()));
						action->setData(static_cast<qlonglong>(insn.operand(i).immediate()));
						
						QAction *const action2 = menu->addAction(tr("Follow Constant In &Stack"), this, SLOT(mnuCPUFollowInStack()));
						action2->setData(static_cast<qlonglong>(insn.operand(i).immediate()));
					}
				}
			}
		}
	}
	
	menu->addSeparator();
	menu->addAction(tr("&Set EIP to this Instruction"), this, SLOT(mnuCPUSetEIP()));
	menu->addSeparator();
	menu->addAction(tr("&Edit Bytes"), this, SLOT(mnuCPUModify()));
	menu->addAction(tr("&Fill with 00's"), this, SLOT(mnuCPUFillZero()));
	menu->addAction(tr("&Fill with NOPs"), this, SLOT(mnuCPUFillNop()));
	menu->addSeparator();
	menu->addAction(tr("&Add Breakpoint"), this, SLOT(mnuCPUAddBreakpoint()));
	menu->addAction(tr("&Add Conditional Breakpoint"), this, SLOT(mnuCPUAddConditionalBreakpoint()));
	menu->addAction(tr("&Remove Breakpoint"), this, SLOT(mnuCPURemoveBreakpoint()));
	
	doAddPluginContextMenu<&DebuggerPluginInterface::cpuViewContextMenu>(menu);
	
	menu->exec(ui.cpuView->viewport()->mapToGlobal(pos));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUFollow() {
	QAction *const action = qobject_cast<QAction *>(sender());
	if(action != 0) {
		const edb::address_t address = action->data().toULongLong();
		doMemoryFollow(true, address, edb::v1::jumpToAddress);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUFollowInDump() {
	QAction *const action = qobject_cast<QAction *>(sender());
	if(action != 0) {
		const edb::address_t address = action->data().toULongLong();
		doMemoryFollow(true, address, edb::v1::dumpData);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUFollowInStack() {
	QAction *const action = qobject_cast<QAction *>(sender());
	
	if(action != 0) {
		const edb::address_t address = action->data().toULongLong();
		doMemoryFollow(true, address, edb::v1::dumpStack);
	}
}


//------------------------------------------------------------------------------
// Name: mnuStackToggleLock(bool locked)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackToggleLock(bool locked) {
	m_StackViewLocked = locked;
}

//------------------------------------------------------------------------------
// Name: mnuStackContextMenu(const QPoint &pos)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackContextMenu(const QPoint &pos) {
	QHexView *const s = m_StackView;
	Q_CHECK_PTR(s);
	
	boost::scoped_ptr<QMenu> menu(s->createStandardContextMenu());
	menu->addSeparator();
	menu->addAction(tr("Follow Address In &Dump"), this, SLOT(mnuStackFollowInDump()));
	menu->addAction(tr("Follow Address In &Stack"), this, SLOT(mnuStackFollowInStack()));
	menu->addAction(tr("&Goto Address"), this, SLOT(mnuStackGotoAddress()));
	menu->addAction(tr("Goto E&SP"), this, SLOT(mnuStackGotoESP()));
	menu->addAction(tr("Goto E&BP"), this, SLOT(mnuStackGotoEBP()));
	menu->addSeparator();
	menu->addAction(tr("&Edit Bytes"), this, SLOT(mnuStackModify()));
	menu->addSeparator();
	menu->addAction(tr("&Push DWORD"), this, SLOT(mnuStackPush()));
	menu->addAction(tr("P&op DWORD"), this, SLOT(mnuStackPop()));
	
	// lockable stack feature
	menu->addSeparator();
	QAction *const action = new QAction(tr("&Lock Stack"), this);
    action->setCheckable(true);
    action->setChecked(m_StackViewLocked);
	menu->addAction(action);
	connect(action, SIGNAL(toggled(bool)), SLOT(mnuStackToggleLock(bool)));
	
	doAddPluginContextMenu<&DebuggerPluginInterface::stackViewContextMenu>(menu);

	menu->exec(s->mapToGlobal(pos));
}

//------------------------------------------------------------------------------
// Name: mnuDumpContextMenu(const QPoint &pos)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuDumpContextMenu(const QPoint &pos) {
	QHexView *const s = qobject_cast<QHexView *>(sender());
	
	Q_CHECK_PTR(s);
	
	boost::scoped_ptr<QMenu> menu(s->createStandardContextMenu());
	menu->addSeparator();
	menu->addAction(tr("Follow Address In &Dump"), this, SLOT(mnuDumpFollowInDump()));
	menu->addAction(tr("Follow Address In &Stack"), this, SLOT(mnuDumpFollowInStack()));
	menu->addAction(tr("&Goto Address"), this, SLOT(mnuDumpGotoAddress()));
	menu->addSeparator();
	menu->addAction(tr("&Edit Bytes"), this, SLOT(mnuDumpModify()));
	menu->addSeparator();
	menu->addAction(tr("&Save To File"), this, SLOT(mnuDumpSaveToFile()));
	
	doAddPluginContextMenu<&DebuggerPluginInterface::dataViewContextMenu>(menu);
	
	menu->exec(s->mapToGlobal(pos));
}

//------------------------------------------------------------------------------
// Name: mnuDumpSaveToFile(
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuDumpSaveToFile() {
	QHexView *const s = qobject_cast<QHexView *>(ui.tabWidget->currentWidget());
	
	Q_CHECK_PTR(s);

	const QString filename = QFileDialog::getSaveFileName(this, tr("Save File"), m_LastOpenDirectory, QString());
	if(!filename.isEmpty()) {

		QFile file(filename);
		file.open(QIODevice::WriteOnly);
		if(file.isOpen()) {
			file.write(s->allBytes());
		}			
	}
}

//------------------------------------------------------------------------------
// Name: overwriteCheck(edb::address_t address, unsigned int size)
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerMain::overwriteCheck(edb::address_t address, unsigned int size) {
	bool firstConflict = true;
	for(edb::address_t addr = address; addr != (address + size); ++addr) {
		Breakpoint *const bp = edb::v1::debuggerBase->findBreakpoint(addr);

		if(bp != 0 && bp->enabled()) {
			if(firstConflict) {
			
				const int ret = QMessageBox::question(
						this, 
						tr("Overwritting breakpoint"), 
						tr("You are attempting to modify bytes which overlap with a software breakpoint. Doing this will implicitly remove any breakpoints which are a conflict. Are you sure you want to do this?"), 
						QMessageBox::Yes, 
						QMessageBox::No);
						
				if(ret == QMessageBox::No) {
					return false;
				}
				firstConflict = false;
			}

			edb::v1::removeBreakpoint(addr);
		}
	}
	return true;
}

//------------------------------------------------------------------------------
// Name: doModifyBytesBase(edb::address_t address, unsigned int size, QByteArray &bytes, quint8 fill
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doModifyBytesBase(edb::address_t address, unsigned int size, QByteArray &bytes, quint8 fill) {

	if(size != 0) {
		if(edb::v1::getBinaryStringFromUser(bytes, tr("Edit Binary String"), size)) {			
			if(overwriteCheck(address, size)) {

				// fill bytes
				while(bytes.size() < static_cast<int>(size)) {
					bytes.push_back(fill);
				}

				edb::v1::debuggerBase->writeBytes(address, bytes.data(), size);

				// do a refresh, not full update
				refreshGUI();
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doCPUFill(quint8 byte) {
	const edb::address_t address 	= ui.cpuView->selectedAddress();
	const unsigned int size			= ui.cpuView->selectedSize();
	
	if(size != 0) {
		if(overwriteCheck(address, size)) {
			QByteArray bytes(size, byte);

			edb::v1::debuggerBase->writeBytes(address, bytes.data(), size);

			// do a refresh, not full update
			refreshGUI();
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUAddBreakpoint() {
	const edb::address_t address = ui.cpuView->selectedAddress();
	edb::v1::createBreakpoint(address);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUAddConditionalBreakpoint() {
	bool ok;
	const edb::address_t address = ui.cpuView->selectedAddress();	
	const QString condition = QInputDialog::getText(this, tr("Set Breakpoint Condition"), tr("Expression:"), QLineEdit::Normal, QString(), &ok);
	if(ok) {
		edb::v1::createBreakpoint(address);
		if(!condition.isEmpty()) {
			edb::v1::debuggerBase->setBreakpointCondition(address, condition);
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPURemoveBreakpoint() {
	const edb::address_t address = ui.cpuView->selectedAddress();
	edb::v1::removeBreakpoint(address);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUFillZero() {
	doCPUFill(0x00);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUFillNop() {
	doCPUFill(0x90);
}

//------------------------------------------------------------------------------
// Name: mnuCPUSetEIP()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUSetEIP() {
	const edb::address_t address = ui.cpuView->selectedAddress();
	State &state = edb::v1::currentState();
	state.setInstructionPointer(address);
	edb::v1::debuggerBase->setState(state);
	updateGUI();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUModify() {
	const edb::address_t address 	= ui.cpuView->selectedAddress();
	const unsigned int size			= ui.cpuView->selectedSize();
	
	quint8 buf[edb::Instruction::MAX_SIZE];		
	
	Q_ASSERT(size <= sizeof(buf));
	
	const bool ok = edb::v1::debuggerBase->readBytes(address, buf, size);
	if(ok) {
		QByteArray bytes = QByteArray::fromRawData(reinterpret_cast<const char *>(buf), size);	
		doModifyBytesBase(address, size, bytes, 0x00);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doModifyBytes(QHexView *s) {
	if(s != 0) {
		const edb::address_t address	= s->selectedBytesAddress();
		const unsigned int size 		= s->selectedBytesSize();
		QByteArray bytes				= s->selectedBytes();
		
		doModifyBytesBase(address, size, bytes, 0x00);
	}
}

//------------------------------------------------------------------------------
// Name: mnuDumpModify()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuDumpModify() {
	doModifyBytes(qobject_cast<QHexView *>(ui.tabWidget->currentWidget()));
}

//------------------------------------------------------------------------------
// Name: mnuStackModify()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackModify() {
	doModifyBytes(m_StackView);
}

//------------------------------------------------------------------------------
// Name: updateMenuState(GUI_STATE guiState)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateMenuState(GUI_STATE guiState) {

	switch(guiState) {
	case PAUSED:
	#if 0
		ui.actionRun_Until_Return->setEnabled(true);
	#endif
		ui.action_Restart->setEnabled(true);
		ui.action_Run->setEnabled(true);
		ui.action_Pause->setEnabled(false);
		ui.action_Step_Into->setEnabled(true);
		ui.action_Step_Over->setEnabled(true);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(true);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(true);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(true);
		ui.action_Detach->setEnabled(true);
		ui.action_Kill->setEnabled(true);
		break;
	case RUNNING:
	#if 0
		ui.actionRun_Until_Return->setEnabled(false);
	#endif
		ui.action_Restart->setEnabled(false);
		ui.action_Run->setEnabled(false);
		ui.action_Pause->setEnabled(true);
		ui.action_Step_Into->setEnabled(false);
		ui.action_Step_Over->setEnabled(false);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Detach->setEnabled(true);
		ui.action_Kill->setEnabled(true);
		break;
	case TERMINATED:
	#if 0
		ui.actionRun_Until_Return->setEnabled(false);
	#endif
		ui.action_Restart->setEnabled(false);
		ui.action_Run->setEnabled(false);
		ui.action_Pause->setEnabled(false);
		ui.action_Step_Into->setEnabled(false);
		ui.action_Step_Over->setEnabled(false);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Detach->setEnabled(false);
		ui.action_Kill->setEnabled(false);
		break;
	}
	
	m_GUIState = guiState;
}

//------------------------------------------------------------------------------
// Name: doTrap()
// Desc: returns true if we should resume as if this trap never happened
//------------------------------------------------------------------------------
bool DebuggerMain::doTrap() {

	// we just got a DebugEvent::sigtrap, there are a few possible causes
	// #1: we just did an execve, in which case we want to stop cause we just attached
	// #2: we hit a 0xCC breakpoint, if so, then we want to stop
	// #3: we did a step
	// #4: we are tracing
	// #5: we hit a hardware breakpoint!	
	State &state = edb::v1::currentState();

	// prevEIP will have the address of the instruction which brought us here!
	const edb::address_t prevEIP = state.instructionPointer() - edb::v1::debuggerBase->executionBreakpointSize();

	// look it up in our breakpoint list, make sure it is one of OUR int3s!
	// if it is, we need to backup EIP and pause ourselves
	Breakpoint *const bp = edb::v1::debuggerBase->findBreakpoint(prevEIP);
	if(bp != 0 && bp->enabled()) {
	
		// TODO: check if the breakpoint was corrupted

		++bp->passcount;					

		// back up eip the size of a breakpoint, since we executed a breakpoint 
		// instead of the real code that belongs there
		state.setInstructionPointer(prevEIP);
		edb::v1::debuggerBase->setState(state);
		
		const QString bpCondition = bp->condition;

		// handle conditional breakpoints
		if(m_StepWasRun && !bpCondition.isEmpty()) {

			int conditionVal = 0;
			Expression<edb::address_t> expr(bpCondition, &edb::v1::getVariable, 0, &edb::v1::getDWORD, 0);

			bool ok;
			ExpressionError err;
			conditionVal = expr.evaluateExpression(ok, err);
				
			if(!ok) {
				QMessageBox::information(this, tr("Error In Breakpoint Expression!"), err.what());
				conditionVal = 1;
				// TODO: remove the breakpoint
			}				

			// ok, the condition was false, so skip over this breakpoint...
			if(!conditionVal) {
				return true;
			}
		}
		
		// if it's a one time breakpoint then we should remove it upon
		// triggering, this is mainly used for 'run-until' situations
		// like step over

		if(bp->oneTime()) {
			edb::v1::debuggerBase->removeBreakpoint(prevEIP);
		}
		
		// cancel any runs we were going to do..
		m_StepWasRun = false;
	}
	
	return false;
}

//------------------------------------------------------------------------------
// Name: handleEventStopped()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventStopped() {


	// ok we just came in from a stop, we need to test some things, 
	// generally, we will want to check if it was a step, if it was, was it 
	// because we just hit a break point or because we wanted to run, but had 
	// to step first in case were were on a breakpoint already...
	
	if(m_LastEvent.isError()) {
		const edb::address_t fault_address		= edb::v1::debuggerBase->getFaultAddress(m_LastEvent);
		const QPair<QString, QString> message	= m_LastEvent.errorDescription(fault_address);
		
		QMessageBox::information(this, message.first, message.second);
		m_StepWasRun = false;
		
	} else {
		switch(m_LastEvent.stopCode()) {	
		case DebugEvent::sigstop:
			// user asked to pause the debugged process
			m_StepWasRun = false;
			break;

		case DebugEvent::sigtrap:

			if(doTrap()) {
				doResume(0);
				return;
			}
			break;

		case DebugEvent::sigalrm:
			qDebug("ALRM Stop Signal Recieved!");
			if(m_StepWasRun) {
				edb::v1::debuggerBase->resume(&m_LastEvent);
				return;
			}
			break;

		default:
			QMessageBox::information(this, tr("Stop Event"),
				QString(tr(
				"<p>The debugged has recieved a stop event. <strong>%1</strong></p>"
				"<p>If you would like to pass this event to the application press Shift+[F7/F8/F9]</p>"
				"<p>If you would like to ignore this event, press [F7/F8/F9]</p>")
				).arg(m_LastEvent.stopCode()));
			m_StepWasRun = false;
			break;

		}
	}
	
	edb::v1::debuggerBase->enableBreakpoint(m_ReenableBreakpointAddress);
	m_ReenableBreakpointAddress = 0;

	// if we stepped with the intention of running, well we should run then!
	if(m_StepWasRun) {
		m_StepWasRun = false;
		edb::v1::debuggerBase->resume(0);
	} else {
		updateGUI();
		updateMenuState(PAUSED);
	}
}

//------------------------------------------------------------------------------
// Name: handleEventExited()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventExited() {
	QMessageBox::information(this, tr("Application Exited"), QString(tr("The debugged application exited normally with exit code %1.")).arg(m_LastEvent.exitCode()));
	on_action_Detach_triggered();
}

//------------------------------------------------------------------------------
// Name: handleEventSignaled()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventSignaled() {
	switch(m_LastEvent.signalCode()) {
	case DebugEvent::sigkill:
		QMessageBox::information(this, tr("Application Killed"), tr("The debugged application was killed."));
		on_action_Detach_triggered();
		break;
	default:
		qDebug() << "Unknown Signal Recieved! " << m_LastEvent.signalCode();
		on_action_Detach_triggered();
		break;
	}
}

//------------------------------------------------------------------------------
// Name: handleEvent(DebugEvent event)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEvent(DebugEvent event) {
	
	Q_CHECK_PTR(edb::v1::debuggerBase);
	
	edb::v1::debuggerBase->getState(edb::v1::currentState());
	edb::v1::memoryRegions().sync();
	
	m_LastEvent = event;
	
	switch(m_LastEvent.reason()) {
	case DebugEvent::EVENT_STOPPED:
		handleEventStopped();
		break;
		
	case DebugEvent::EVENT_SIGNALED:
		handleEventSignaled();
		break;
		
	case DebugEvent::EVENT_EXITED:
		handleEventExited();
		break;
		
	case DebugEvent::EVENT_UNKNOWN:
		edb::v1::debuggerBase->resume(0);
		break;
	}
}

//------------------------------------------------------------------------------
// Name: debugEventHandler(DebugEvent event)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::debugEventHandler(DebugEvent event) {
	DebugEventHandlerInterface *const handler = edb::v1::debugEventHandler();
	Q_CHECK_PTR(handler);
	handler->handleEvent(event);
}

//------------------------------------------------------------------------------
// Name: update()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::DataViewInfo::update() {

	Q_CHECK_PTR(stream);
	Q_CHECK_PTR(view);
	
	stream->setRegion(region);	
	view->setAddressOffset(region.start);
	view->setData(stream);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateTabCaption(const boost::shared_ptr<QHexView> &view, edb::address_t start, edb::address_t end) {
	const int index = ui.tabWidget->indexOf(view.get());
	const QString caption = qobject_cast<ETabWidget *>(ui.tabWidget)->caption(index);
	
	if(caption.isEmpty()) {
		ui.tabWidget->setTabText(index, QString("%1-%2").arg(start, EDB_MAX_HEX, 16, QChar('0')).arg(end, EDB_MAX_HEX, 16, QChar('0')));
	} else {
		ui.tabWidget->setTabText(index, QString("[%1] %2-%3").arg(caption).arg(start, EDB_MAX_HEX, 16, QChar('0')).arg(end, EDB_MAX_HEX, 16, QChar('0')));
	}
}

//------------------------------------------------------------------------------
// Name: updateData(const boost::shared_ptr<DataViewInfo> &v)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateData(const boost::shared_ptr<DataViewInfo> &v) {

	Q_CHECK_PTR(v);
	
	boost::shared_ptr<QHexView> view(v->view);
	
	Q_CHECK_PTR(view);

	v->update();
	
	updateTabCaption(view, v->region.start, v->region.end);
}

//------------------------------------------------------------------------------
// Name: clearData(const boost::shared_ptr<DataViewInfo> &v)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::clearData(const boost::shared_ptr<DataViewInfo> &v) {

	Q_CHECK_PTR(v);
	
	boost::shared_ptr<QHexView> view(v->view);
	
	Q_CHECK_PTR(view);

	view->clear();
	view->scrollTo(0);
	
	updateTabCaption(view, edb::nullptr, edb::nullptr);
}

//------------------------------------------------------------------------------
// Name: doJumpToAddress(edb::address_t address, const MemRegion &r, bool scrollTo)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doJumpToAddress(edb::address_t address, const MemRegion &r, bool scrollTo) {

	ui.cpuView->setAddressOffset(r.start);
	ui.cpuView->setRegion(r);
	if(scrollTo && !ui.cpuView->addressShown(address)) {
		ui.cpuView->scrollTo(address);
	}
}

//------------------------------------------------------------------------------
// Name: updateDisassembly(edb::address_t address, const MemRegion &r)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateDisassembly(edb::address_t address, const MemRegion &r) {

	ui.cpuView->setCurrentAddress(address);	
	doJumpToAddress(address, r, true);
	m_ListModel->setStringList(edb::v1::archProcessor().updateInstructionInfo(address));
}

//------------------------------------------------------------------------------
// Name: updateStackView()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateStackView() {
	if(!edb::v1::dumpStack(edb::v1::currentState().stackPointer(), !m_StackViewLocked)) {
		m_StackView->clear();
		m_StackView->scrollTo(0);
	}
}

//------------------------------------------------------------------------------
// Name: updateCPUView(MemRegion &region)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateCPUView(MemRegion &region) {

	const edb::address_t address = edb::v1::currentState().instructionPointer();

	if(!edb::v1::memoryRegions().findRegion(address, region)) {
		ui.cpuView->clear();
		ui.cpuView->scrollTo(0);
		m_ListModel->setStringList(QStringList());
	} else {
		updateDisassembly(address, region);
	}
}

//------------------------------------------------------------------------------
// Name: updateDataViews()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateDataViews() {

	// update all data views with the current region data
	foreach(boost::shared_ptr<DataViewInfo> info, m_DataRegions) {

		// make sure the regions are still valid..
		if(edb::v1::memoryRegions().findRegion(info->region.start)) {
			updateData(info);
		} else {
			clearData(info);
		}
	}
}

//------------------------------------------------------------------------------
// Name: refreshGUI()
// Desc: refreshed all the different displays
//------------------------------------------------------------------------------
void DebuggerMain::refreshGUI() {
	ui.cpuView->repaint();
	m_StackView->repaint();
	
	foreach(boost::shared_ptr<DataViewInfo> info, m_DataRegions) {
		boost::shared_ptr<QHexView> view(info->view);
	
		Q_CHECK_PTR(view);
		view->repaint();
	}
	
	m_ListModel->setStringList(edb::v1::archProcessor().updateInstructionInfo(edb::v1::currentState().instructionPointer()));
}

//------------------------------------------------------------------------------
// Name: updateGUI()
// Desc: updates all the different displays
//------------------------------------------------------------------------------
void DebuggerMain::updateGUI() {

	MemRegion region;
	updateCPUView(region);
	updateDataViews();
	updateStackView();
	edb::v1::archProcessor().updateRegisterView(region.name);
}

//------------------------------------------------------------------------------
// Name: doResume(bool passExceptionToApp)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doResume(bool passExceptionToApp) {
	
	// if we are on a breakpoint, disable it, and set the re-enable 
	// address to the current eip
	// disableBreakpoint returns 0 if there was no breakpoint to disable
	m_ReenableBreakpointAddress = edb::v1::debuggerBase->disableBreakpoint(edb::v1::currentState().instructionPointer());

	// set the state to 'running'
	updateMenuState(RUNNING);
	
	// if neccessary pass the trap to the application, otherwise just step
	if(passExceptionToApp && m_LastEvent.stopped() && m_LastEvent.stopCode() != DebugEvent::sigtrap && m_LastEvent.stopCode() != DebugEvent::sigstop) {
		edb::v1::debuggerBase->step(&m_LastEvent);
	} else {
		edb::v1::debuggerBase->step(0);
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Run_Pass_Signal_To_Application_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Run_Pass_Signal_To_Application_triggered() {
	m_StepWasRun = true;
	doResume(true);	
}

//------------------------------------------------------------------------------
// Name: on_action_Step_Into_Pass_Signal_To_Application_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Into_Pass_Signal_To_Application_triggered() {
	doResume(true);
}

//------------------------------------------------------------------------------
// Name: on_action_Step_Over_Pass_Signal_To_Application_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Over_Pass_Signal_To_Application_triggered() {

	stepOver(
		boost::bind(&DebuggerMain::on_action_Run_Pass_Signal_To_Application_triggered, this),
		boost::bind(&DebuggerMain::on_action_Step_Into_Pass_Signal_To_Application_triggered, this));
}

//------------------------------------------------------------------------------
// Name: on_action_Step_Over_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Over_triggered() {

	stepOver(
		boost::bind(&DebuggerMain::on_action_Run_triggered, this),
		boost::bind(&DebuggerMain::on_action_Step_Into_triggered, this));
}

//------------------------------------------------------------------------------
// Name: on_action_Run_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Run_triggered() {
	m_StepWasRun = true;
	doResume(false);
}

//------------------------------------------------------------------------------
// Name: on_action_Step_Into_triggered(0
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Into_triggered() {
	doResume(false);
}

//------------------------------------------------------------------------------
// Name: on_actionRun_Until_Return_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionRun_Until_Return_triggered() {
	// TODO: implement this
	// we need a generic 'run until <condition>' function
	// likely callback based
}

//------------------------------------------------------------------------------
// Name: on_action_Pause_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Pause_triggered() {
	Q_CHECK_PTR(edb::v1::debuggerBase);
	edb::v1::debuggerBase->pause();
}

//------------------------------------------------------------------------------
// Name: cleanupEventThread()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::cleanupEventThread() {
	
	if(edb::v1::debuggerBase != 0) {
	
		Q_CHECK_PTR(m_DebugEventServer);

		// make sure we don't get any spurious events!
		m_DebugEventServer->disconnect();

		// make sure we are paused...
		if(m_GUIState == RUNNING) {
			edb::v1::debuggerBase->pause();
			updateMenuState(PAUSED);
		}
	#ifdef Q_OS_WIN32
		m_Timer->stop();
		edb::v1::debuggerBase->clearBreakpoints();
	#else
		// remove all breakpoints, we need to do this during this moment so that 
		// we don't get any signals in the child process later!
		edb::v1::debuggerBase->clearBreakpoints();

		// make sure the server thread thinks it should exit
		m_DebugEventServer->m_ShouldExit = true;

	#if 1
		// this is ugly, but it seems to work pretty well
		// basically we have marked the thread as 'should exit'
		// but, we may be blocked (likley) waiting for the next event
		// so we intentionally cause one last event giving it a chance to
		// check the loop invariant one more time, which will allow
		// the thread to exit cleanly
		if(m_GUIState == PAUSED) {
			edb::v1::debuggerBase->step(0);
			updateMenuState(RUNNING);
		}
	#endif		
		m_DebugEventServer->wait();
	#endif
	}
}

//------------------------------------------------------------------------------
// Name: cleanupDebugger()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::cleanupDebugger() {
	
	cleanupEventThread();

	edb::v1::memoryRegions().clear();
	edb::v1::symbolManager().clear();
	edb::v1::currentState().clear();
	edb::v1::archProcessor().reset();

	// clear up the data view
	while(ui.tabWidget->count() > 1) {
		mnuDumpDeleteTab();
	}
	
	Q_ASSERT(!m_DataRegions.isEmpty());
	m_DataRegions.first()->region = MemRegion();
		
	setWindowTitle(tr("EDB"));
	
	updateGUI();
}

//------------------------------------------------------------------------------
// Name: sessionFileName() const
// Desc: 
//------------------------------------------------------------------------------
QString DebuggerMain::sessionFileName() const {
	QString ret;
	if(!m_ProgramExecutable.isEmpty()) {
		const QString basename = edb::core::basename(m_ProgramExecutable);
		ret = edb::v1::config().sessionPath + "/" + basename + ".edb";
	}
	return ret;
}

//------------------------------------------------------------------------------
// Name: doDetachFromProcess(bool kill)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doDetachFromProcess(bool kill) {

	SessionFileInterface *const sessionFile = edb::v1::sessionFileHandler();
	if(sessionFile != 0) {
		const QString filename = sessionFileName();
		if(!filename.isEmpty()) {
			sessionFile->saveSessionData(filename, m_ProgramExecutable);
			m_ProgramExecutable.clear();
		}
	}

	cleanupDebugger();

	if(edb::v1::debuggerBase != 0) {
		if(kill) {
			edb::v1::debuggerBase->kill();
		} else {
			edb::v1::debuggerBase->detach();
		}
	}

	updateMenuState(TERMINATED);
}

//------------------------------------------------------------------------------
// Name: on_action_Detach_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Detach_triggered() {
	doDetachFromProcess(false);
}

//------------------------------------------------------------------------------
// Name: on_action_Kill_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Kill_triggered() {
	doDetachFromProcess(true);
}

//------------------------------------------------------------------------------
// Name: setInitialDebuggerState()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::setInitialDebuggerState() {

	updateMenuState(PAUSED);
	connect(m_DebugEventServer, SIGNAL(debugEvent(DebugEvent)), SLOT(debugEventHandler(DebugEvent)));

#ifdef Q_OS_WIN32
	// on windows, we have timeouts so we integrate with the main event loop
	connect(m_Timer.get(), SIGNAL(timeout()), m_DebugEventServer, SLOT(run()));
	m_Timer->start(0);
#else
	m_DebugEventServer->start();
#endif

	edb::v1::symbolManager().loadSymbols(edb::v1::config().symbolPath);
	
	edb::v1::memoryRegions().setPID(edb::v1::debuggerBase->pid());
	edb::v1::memoryRegions().sync();
	
	Q_ASSERT(m_DataRegions.size() > 0);
	
	m_DataRegions.first()->region = edb::v1::primaryDataRegion();
	
	edb::v1::currentState().clear();

	AnalyzerInterface *const analyzer = edb::v1::analyzer();
	if(analyzer != 0) {
		analyzer->invalidateAnalysis();
	}
	
	m_StepWasRun = false;
	
	setDebuggerCaption(edb::v1::getProcessExecutable());
	
	m_ProgramExecutable.clear();
	if(edb::v1::debuggerBase->pid() != 0) {
		m_ProgramExecutable	= edb::v1::getProcessExecutable();		
	}

	SessionFileInterface *const sessionFile = edb::v1::sessionFileHandler();
	if(sessionFile != 0) {
		const QString filename = sessionFileName();
		if(!filename.isEmpty()) {
			sessionFile->loadSessionData(filename, m_ProgramExecutable);
		}
	}
}

//------------------------------------------------------------------------------
// Name: testNativeBinary(const QString &s)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::testNativeBinary(const QString &s) {
	boost::scoped_ptr<BinaryInfo> binfo(edb::v1::getBinaryInfo(s));
	if(binfo != 0) {
		if(!binfo->nativeBinary()) {
			QMessageBox::warning(
				this,
				tr("Not A Native Binary"),
				tr("The program you just attached to was built for a different architecture than the one that EDB was built for. "
				"For example a 32-bit binary on x86-64. "
				"This is not supported yet, so you may need to use a version of EDB that was compiled for thr same architecture as your target program")
				);
		}
	}
}

//------------------------------------------------------------------------------
// Name: setInitialBreakpoint(const QString &s)
// Desc: sets the initial breakpoint so we can stop at the entry point of the 
//       application
//------------------------------------------------------------------------------
void DebuggerMain::setInitialBreakpoint(const QString &s) {

	edb::address_t entryPoint = 0;
	
	if(edb::v1::config().initialBreakpoint == Configuration::MainSymbol) {
		const QString mainSymbol = edb::core::basename(s) + "::main";
		const SymbolManager::Symbol *const sym = edb::v1::symbolManager().findSymbol(mainSymbol);

		if(sym != 0) {
			entryPoint = sym->address;
		} else if(edb::v1::config().findMain) {
				
			// try to figure out where main would be
			entryPoint = edb::v1::locateMainFunction(s);
		}
	}
	
	if(entryPoint == 0 || edb::v1::config().initialBreakpoint == Configuration::EntryPoint) {
		boost::scoped_ptr<BinaryInfo> binfo(edb::v1::getBinaryInfo(s));
		if(binfo != 0) {
			entryPoint = binfo->getEntryPoint();
		}
	}

	if(entryPoint != 0) {
		edb::v1::debuggerBase->addBreakpoint(entryPoint, true);
	}
}

//------------------------------------------------------------------------------
// Name: ttyProcFinished(int exitCode, QProcess::ExitStatus exitStatus)
// Desc: cleans up the data associated with a TTY when the terminal dies
//------------------------------------------------------------------------------
void DebuggerMain::ttyProcFinished(int exitCode, QProcess::ExitStatus exitStatus) {
	Q_UNUSED(exitCode);
	Q_UNUSED(exitStatus);
	
	m_TTYFile.clear();
}

//------------------------------------------------------------------------------
// Name: createTTY()
// Desc: creates a TTY object for our command line I/O
//------------------------------------------------------------------------------
QString DebuggerMain::createTTY() {
#ifdef Q_OS_LINUX
	// we attempt to reuse an open output window
	if(edb::v1::config().ttyEnabled && m_TTYProc->state() != QProcess::Running) {
	
		// ok, creating a new one...	
		// first try to get a 'unique' filename, i would love to use a system 
		// temp file API... but there doesn't seem to be one which will create 
		// a pipe...only ordinary files!
		const QString tempPipe = QString("%1/edb_temp_file_%2_%3").arg(QDir::tempPath()).arg(rand()).arg(getpid());

		// make sure it isn't already there, and then make the pipe
		::unlink(qPrintable(tempPipe));
		::mkfifo(qPrintable(tempPipe), S_IRUSR | S_IWUSR);

		// this is a basic shell script which will output the tty to a file (the pipe),
		// ignore kill sigs, close all standard IO, and then just hang
		const QString shellScript = QString(
		    "tty > %1;"
			"trap \"\" INT QUIT TSTP;"
			"exec<&-; exec>&-;"
			"while :; do sleep 3600; done"
			).arg(tempPipe);

		// parse up the command from the options, white space delimited
		QStringList procargs = edb::core::parseCommandLine(edb::v1::config().ttyCommand);
		const QString ttyCommand = procargs.takeFirst();

		// start constructing the arguments for the term
		procargs << "-title" <<  tr("EDB Output") << "-e" << "sh" <<  "-c" << shellScript;

		// make the tty process object and connect it's death signal to our cleanup
		connect(m_TTYProc, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(ttyProcFinished(int, QProcess::ExitStatus)));

		m_TTYProc->start(ttyCommand, procargs);
		const bool ok = m_TTYProc->waitForStarted();
				
		if(ok) {
			// get the output, this should block until the xterm actually gets a chance to write it
			QFile file(tempPipe);
			if(file.open(QIODevice::ReadOnly)) {
				m_TTYFile = file.readLine().trimmed();
			}
		} else {
			qDebug().nospace() << "Could not launch the desired terminal [" << ttyCommand << "], please check that it exists and you have proper permissions.";
			m_TTYFile.clear();
		}

		// cleanup, god i wish there was an easier way than this!
		::unlink(qPrintable(tempPipe));
	}
#endif	
	return m_TTYFile;
}

//------------------------------------------------------------------------------
// Name: on_action_Restart_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Restart_triggered() {
	
	Q_CHECK_PTR(edb::v1::debuggerBase);

	m_WorkingDirectory	= edb::v1::getProcessCurrentWorkingDirectory();	
	QStringList args	= edb::v1::getProcessArguments();
	const QString s		= edb::v1::getProcessExecutable();

	if(!args.empty()) {
		args.removeFirst();
	}

	if(!s.isEmpty()) {
		doDetachFromProcess(true);
		commonOpen(s, args);
	}
}

//------------------------------------------------------------------------------
// Name: commonOpen(const QString &s, const QStringList &args)
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerMain::commonOpen(const QString &s, const QStringList &args) {
	const QString tty = createTTY();
	bool ret = false;
	if(edb::v1::debuggerBase->open(s, m_WorkingDirectory, args, tty)) {
		testNativeBinary(s);
		setInitialDebuggerState();
		setInitialBreakpoint(s);
		ret = true;		
	} else {
		QMessageBox::information(this, tr("Could Not Open"), tr("Failed to open and attach to process, please check priviledges and try again."));
	}

	edb::v1::debuggerBase->getState(edb::v1::currentState());
	updateGUI();
	return ret;
}

//------------------------------------------------------------------------------
// Name: doRun(const QString &s, const QStringList &args)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doRun(const QString &s, const QStringList &args) {
	const bool ok = commonOpen(s, args);
	if(ok) {
		// update recent file list
		m_RecentFileManager->addFile(s);
	}
}

//------------------------------------------------------------------------------
// Name: doOpenFile(const QString &s)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doOpenFile(const QString &s) {
	if(!s.isEmpty()) {
		m_LastOpenDirectory = QFileInfo(s).canonicalFilePath();
	
		doDetachFromProcess(false);
		
		const QStringList args = edb::core::parseCommandLine(m_Arguments);
		doRun(s, args);
		
		AnalyzerInterface *const analyzer = edb::v1::analyzer();
		if(analyzer != 0) {
			analyzer->invalidateAnalysis();
		}
	}
}

//------------------------------------------------------------------------------
// Name: doAttach(edb::pid_t pid)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doAttach(edb::pid_t pid) {
	
	// TODO: we need a core concept of debugger capabilities which
	// may restrict some actions
	
	if(pid == edb::v1::debuggerBase->pid()) {
		QMessageBox::information(this, tr("EDB"), tr("You are already debugging that process!"));
	} else {
		doDetachFromProcess(false);

		if(edb::v1::debuggerBase->attach(pid)) {
				
			m_WorkingDirectory = edb::v1::getProcessCurrentWorkingDirectory();

			setInitialDebuggerState();
			
			QStringList args = edb::v1::getProcessArguments();
			if(!args.empty()) {
				args.removeFirst();
			}
			
			// make our arguments match what it was run with
			// TODO: this might be a mistake
			// perhaps we should have a seperate variable which represents 
			// 'actual arguments'
			m_Arguments = args.join(" ");
		} else {
			QMessageBox::information(this, tr("EDB"), tr("Failed to attach to process, please check priviledges and try again."));
		}

		edb::v1::debuggerBase->getState(edb::v1::currentState());
		updateGUI();
	}
}

//------------------------------------------------------------------------------
// Name: setDebuggerCaption(const QString &appname)
// Desc: sets the caption part to also show the application name and pid
//------------------------------------------------------------------------------
void DebuggerMain::setDebuggerCaption(const QString &appname) {
	setWindowTitle(QString(tr("EDB - %1 [%2]")).arg(appname).arg(edb::v1::debuggerBase->pid()));
}

//------------------------------------------------------------------------------
// Name: on_action_Open_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Open_triggered() {

	// TODO: we need a core concept of debugger capabilities which
	// may restrict some actions

	const QString filename = QFileDialog::getOpenFileName(this, tr("Choose a file"), m_LastOpenDirectory, QString());
	doOpenFile(filename);
}

//------------------------------------------------------------------------------
// Name: on_action_Attach_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Attach_triggered() {

	// TODO: we need a core concept of debugger capabilities which
	// may restrict some actions

	static DialogAttach *const dlg = new DialogAttach(this);
	const int ret = dlg->exec();
	
	if(ret == QDialog::Accepted) {
		bool ok;
		const edb::pid_t pid = dlg->selectedPID(ok);
		if(ok) {
			doAttach(pid);
		}
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Memory_Regions_triggered()
// Desc: displays the memory regions dialog, and optionally dumps some data
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Memory_Regions_triggered() {

	// TODO: we need a core concept of debugger capabilities which
	// may restrict some actions
	
	static DialogMemoryRegions *const dlg = new DialogMemoryRegions(this);	
	const int ret = dlg->exec();
	
	if(ret == QDialog::Accepted) {
		const MemRegion *const region = dlg->selectedRegion();
		if(region != 0) {
			edb::v1::dumpData(region->start);
		}
	}
}

//------------------------------------------------------------------------------
// Name: mnuDumpCreateTab()
// Desc: duplicates the current tab creating a new one
//------------------------------------------------------------------------------
void DebuggerMain::mnuDumpCreateTab() {
	
	const int currentIndex = currentTab();
	
	// duplicate the current region
	DataViewInfo *const newDataView = new DataViewInfo(
			(currentIndex != -1) ? m_DataRegions[currentIndex]->region : MemRegion()
			);
		
	QHexView *const hv = new QHexView(this);

	newDataView->view = boost::shared_ptr<QHexView>(hv);
		
	// setup the context menu
	hv->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(hv, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(mnuDumpContextMenu(const QPoint &)));
	
	// show the initial data for this new view
	hv->setAddressOffset(newDataView->region.start);
	hv->setData(newDataView->stream);
	
	const Configuration &config = edb::v1::config();
		
	// set the default view options
	hv->setShowAddress(config.dataShowAddress);
	hv->setShowHexDump(config.dataShowHex);
	hv->setShowAsciiDump(config.dataShowAscii);
	hv->setShowComments(config.dataShowComments);
	hv->setRowWidth(config.dataRowWidth);
	hv->setWordWidth(config.dataWordWidth);
	
	// set the default font
	QFont dumpFont;
	dumpFont.fromString(config.dataFont);
	hv->setFont(dumpFont);

	m_DataRegions.push_back(boost::shared_ptr<DataViewInfo>(newDataView));

	// create the tab!
	ui.tabWidget->addTab(hv, QString("%1-%2").arg(newDataView->region.start, EDB_MAX_HEX, 16, QChar('0')).arg(newDataView->region.end, EDB_MAX_HEX, 16, QChar('0')));

	ui.tabWidget->setCurrentIndex(ui.tabWidget->count() - 1);

	m_DelTab->setEnabled(ui.tabWidget->count() > 1);
}

//------------------------------------------------------------------------------
// Name: mnuDumpDeleteTab() 
// Desc: handles removing of a memory view tab
//------------------------------------------------------------------------------
void DebuggerMain::mnuDumpDeleteTab() {
	const int currentIndex = currentTab();
	
	// get a pointer to the info we need (before removing it from the list!)
	// this seems redundant to currentDataViewInfo(), but we need the
	// index too, so may as well waste one line to avoid duplicate work
	boost::shared_ptr<DataViewInfo> info = m_DataRegions[currentIndex];
		
	// remove it from the list
	m_DataRegions.remove(currentIndex);
	
	// remove the tab associated with it
	ui.tabWidget->removeTab(currentIndex);
	
	// update the delete button state
	m_DelTab->setEnabled(ui.tabWidget->count() > 1);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
template<QList<QAction *> (DebuggerPluginInterface::*F)(), class T>
void DebuggerMain::doAddPluginContextMenu(const T &menu) {
	foreach(QObject *plugin, edb::v1::pluginList()) {
		DebuggerPluginInterface *const p = qobject_cast<DebuggerPluginInterface *>(plugin);
		if(p != 0) {
			QList<QAction *> acts = (p->*F)();
			if(!acts.isEmpty()) {
				menu->addSeparator();
				menu->addActions(acts);
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: currentTab() const
// Desc: 
//------------------------------------------------------------------------------
int DebuggerMain::currentTab() const {
	return ui.tabWidget->currentIndex();
}

//------------------------------------------------------------------------------
// Name: currentDataViewInfo() const
// Desc: 
//------------------------------------------------------------------------------
boost::shared_ptr<DebuggerMain::DataViewInfo> DebuggerMain::currentDataViewInfo() const {
	return m_DataRegions[currentTab()];
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Plugins_triggered() {
	static QDialog *const dlg = new DialogPlugins(this);
	dlg->show();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerMain::jumpToAddress(edb::address_t address) {	
	MemRegion region;
	if(edb::v1::memoryRegions().findRegion(address, region)) {		
		doJumpToAddress(address, region, true);
		return true;
	}
	
	return false;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerMain::dumpDataRange(edb::address_t address, edb::address_t end_address, bool newTab) {
	bool ret = false;
		
	MemRegion region;
	if(edb::v1::memoryRegions().findRegion(address, region)) {
		if(newTab) {
			mnuDumpCreateTab();
		}

		boost::shared_ptr<DebuggerMain::DataViewInfo> info = currentDataViewInfo();

		if(info != 0) {
			info->region = region;

			if(end_address >= info->region.start && end_address < info->region.end) {
				info->region.end = end_address;
			}

			if(address >= info->region.start && address < info->region.end) {
				info->region.start = address;
			}

			updateData(info);
			ret = true;
		}
	}

	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerMain::dumpData(edb::address_t address, bool newTab) {
	bool ret = false;
	
	MemRegion region;
	if(edb::v1::memoryRegions().findRegion(address, region)) {

		if(newTab) {
			mnuDumpCreateTab();
		}

		boost::shared_ptr<DebuggerMain::DataViewInfo> info = currentDataViewInfo();

		if(info != 0) {
			info->region = region;
			updateData(info);
			info->view->scrollTo(address - info->region.start);
			ret = true;
		}
	}

	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerMain::dumpStack(edb::address_t address, bool ScrollTo) {
	bool ret = false;

	const MemRegion last_region = m_StackViewInfo.region;

	if(edb::v1::memoryRegions().findRegion(address, m_StackViewInfo.region)) {
		m_StackViewInfo.update();
		if(ScrollTo || m_StackViewInfo.region != last_region) {
			m_StackView->scrollTo(address - m_StackViewInfo.region.start);
		}
		ret = true;
	}
	
	return ret;
}
