/*
Copyright (C) 2006 - 2009 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.
*/

#ifndef DEBUGGERMAIN_20060508_H_
#define DEBUGGERMAIN_20060508_H_

#include "DebugEventHandlerInterface.h"
#include "DebuggerUI.h"
#include "Util.h"
#include "ScopedPointer.h"

class BinaryInfo;
class Breakpoint;
class CommentServerInterface;
class DebuggerPluginInterface;
class DialogArguments;
class QStringListModel;
class QTimer;
class QToolButton;
class RecentFileManager;

class DebuggerMain : public DebuggerUI, public DebugEventHandlerInterface {
	Q_OBJECT

public:
	DebuggerMain(QWidget *parent = 0);
	virtual ~DebuggerMain();
	
protected:
	virtual void closeEvent(QCloseEvent *event);
	virtual void showEvent(QShowEvent *event);

public:
	void doAttach(edb::pid_t pid);
	void doRun(const QString &s, const QStringList &args);
	void doJumpToAddress(edb::address_t address, const MemRegion &r, bool scrollTo);
	void refreshGUI();
	void updateGUI();
	
public:
	bool jumpToAddress(edb::address_t address);
	bool dumpDataRange(edb::address_t address, edb::address_t end_address, bool new_tab);
	bool dumpData(edb::address_t address, bool new_tab);
	bool dumpStack(edb::address_t address, bool scroll_to);
	
public Q_SLOTS:
	// the autoconnected slots
	void on_action_Plugins_triggered();
	void on_actionAbout_QT_triggered();
	void on_actionApplication_Arguments_triggered();
	void on_actionApplication_Working_Directory_triggered();
	void on_actionRun_Until_Return_triggered();
	void on_action_About_triggered();
	void on_action_Attach_triggered();
	void on_action_Configure_Debugger_triggered();
	void on_action_Detach_triggered();
	void on_action_Kill_triggered();
	void on_action_Memory_Regions_triggered();
	void on_action_Open_triggered();
	void on_action_Pause_triggered();
	void on_action_Restart_triggered();
	void on_action_Run_Pass_Signal_To_Application_triggered();
	void on_action_Run_triggered();
	void on_action_Step_Into_Pass_Signal_To_Application_triggered();
	void on_action_Step_Into_triggered();
	void on_action_Step_Over_Pass_Signal_To_Application_triggered();
	void on_action_Step_Over_triggered();
	void on_cpuView_breakPointToggled(edb::address_t);
	void on_registerList_itemDoubleClicked(QTreeWidgetItem *);
	void on_cpuView_customContextMenuRequested(const QPoint &);
	void on_registerList_customContextMenuRequested(const QPoint &);
	void on_comboThreads_currentIndexChanged(int index);

public Q_SLOTS:
	// the manually connected CPU slots
	void mnuCPUAddBreakpoint();
	void mnuCPUAddConditionalBreakpoint();
	void mnuCPURemoveBreakpoint();	
	void mnuCPUJumpToAddress();
	void mnuCPUJumpToEIP();
	void mnuCPUFillNop();
	void mnuCPUFillZero();
	void mnuCPUModify();
	void mnuCPUSetEIP();
	void mnuCPUFollow();
	void mnuCPUFollowInDump();
	void mnuCPUFollowInStack();

public Q_SLOTS:
	// the manually connected Register slots
	void mnuRegisterFollowInDump()			{ followRegisterInDump(false); }
	void mnuRegisterFollowInDumpNewTab()	{ followRegisterInDump(true); }
	void mnuRegisterFollowInStack();
	
public Q_SLOTS:
	// the manually connected Dump slots	
	void mnuDumpContextMenu(const QPoint &pos);
	void mnuDumpFollowInStack();
	void mnuDumpFollowInDump();
	void mnuDumpGotoAddress();
	void mnuDumpCreateTab();
	void mnuDumpDeleteTab();
	void mnuDumpModify();
	void mnuDumpSaveToFile();
	
public Q_SLOTS:
	// the manually connected Stack slots
	void mnuStackContextMenu(const QPoint &);
	void mnuStackFollowInDump();
	void mnuStackGotoEBP();
	void mnuStackGotoESP();
	void mnuStackPush();
	void mnuStackPop();
	void mnuStackGotoAddress();
	void mnuStackModify();
	void mnuStackFollowInStack();
	void mnuStackToggleLock(bool locked);
	
private Q_SLOTS:
	void nextDebugEvent();
	void doOpenFile(const QString &s);
	void tabContextMenu(int index, const QPoint &pos);
	
public:
	virtual void handleEvent(const DebugEvent &event);
	
private:
	enum STEP_MODE {
		MODE_STEPPING,
		MODE_TRACING,		// just like steping, but we keep doing it
		MODE_RUNNING_STEP,	// this is a step (to work with BP's) but we intend to run
		MODE_RUNNING
	};

public:
	void updateData(const QSharedPointer<DataViewInfo> &v);
	void clearData(const QSharedPointer<DataViewInfo> &v);

private:
	QString sessionFileName() const;
	bool commonOpen(const QString &s, const QStringList &args);
	bool handleBreakpoint();
	bool handleStepping();
	bool handleTrap();
	bool isTrap(const DebugEvent &event) const;
	void cleanupDebugger();
	void debugEventHandler(const DebugEvent &event);
	void doApplyDefaultFonts();
	void doApplyDefaultShowSeperator();
	void doCPUFill(quint8 byte);
	void doDetachFromProcess(bool kill);
	void doMemoryFollow(bool ok, edb::address_t address, bool (*follow_func)(edb::address_t));
	void followRegisterInDump(bool tabbed);
	void handleEventExited(const DebugEvent &event);
	void handleEventSignaled(const DebugEvent &event);
	void handleEventStopped(const DebugEvent &event);
	void resumeExecution(bool pass_exception, STEP_MODE mode);
	void setInitialBreakpoint(const QString &s);
	void setInitialDebuggerState();
	void setupStackView();
	void setupTabButtons();
	void setupUI();
	void testNativeBinary();
	void updateCPUView(MemRegion &region);
	void updateDataViews();
	void updateDisassembly(edb::address_t address, const MemRegion &r);
	void updateStackView();
	void updateTabCaption(const QSharedPointer<QHexView> &view, edb::address_t start, edb::address_t end);
	void updateThreadList();

private:
	template <QList<QAction *> (DebuggerPluginInterface::*F)(), class T>
	void doAddPluginContextMenu(const T &menu);
	
	template <class T>
	void doFollowInDump(const T &hv);
	
	template <class T>
	void doFollowInStack(const T &hv);
	
	template <class T>
	void doModifyBytes(const T &hv);
	
private:
	QSharedPointer<QHexView>				m_StackView;
	QStringListModel						*m_ListModel;
	QStringListModel						*m_ThreadListModel;
	DialogArguments							*m_DialogArguments;
	QTimer									*m_Timer;
	RecentFileManager						*m_RecentFileManager;
	
	QSharedPointer<CommentServerInterface>	m_StackCommentServer;
	QSharedPointer<Breakpoint>				m_ReenableBreakpoint;
	SCOPED_POINTER<BinaryInfo>				m_BinaryInfo;
	
	QString									m_LastOpenDirectory;
	QString									m_WorkingDirectory;
	QString									m_ProgramExecutable;
	bool									m_StackViewLocked;
	STEP_MODE								m_StepMode;
	DebugEvent								m_LastEvent;
};

#endif
