/*
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.
*/

#ifndef DEBUGGERMAIN_20060508_H_
#define DEBUGGERMAIN_20060508_H_

#include "ByteStreamRegion.h"
#include "Util.h"
#include "ui_debuggerui.h"

class QToolButton;
class QStringListModel;
class DebugEventServer;
class DebuggerPluginInterface;
class RecentFileManager;

#include <QProcess>
#include "SessionFileManager.h"

class DebuggerMain : public QMainWindow, public DebugEventHandlerInterface {
	Q_OBJECT

public:
	struct DataViewInfo;

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

public:
	void finishPluginSetup(const QList<DebuggerPluginInterface *> &plugins);
	void doAttach(edb::pid_t pid);
	void doRun(const QString &s, const QStringList &args);
	void doJumpToAddress(edb::address_t address, const MemoryRegions::Region &r, bool scrollTo);
	int currentTab() const;
	DataViewInfo *currentDataViewInfo() const;
	
public slots:
	// the autoconnected slots
	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_stackView_customContextMenuRequested(const QPoint &);
	
public slots:
	// the manually connected CPU slots
	void mnuCPUAddBreakpoint();
	void mnuCPUAddConditionalBreakpoint();
	void mnuCPURemoveBreakpoint();	
	void mnuCPUJumpToAddress();
	void mnuCPUJumpToEIP();
	void mnuCPUFillNop();
	void mnuCPUFillZero();
	void mnuCPUModify();

public slots:
	// the manually connected Register slots
	void mnuRegisterFollowInDump()			{ followRegisterInDump(false); }
	void mnuRegisterFollowInDumpNewTab()	{ followRegisterInDump(true); }
	void mnuRegisterFollowInStack();
	
public 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 slots:
	// the manually connected Stack slots
	void mnuStackFollowInDump();
	void mnuStackGotoEBP();
	void mnuStackGotoESP();
	void mnuStackPush();
	void mnuStackPop();
	void mnuStackGotoAddress();
	void mnuStackModify();
	void mnuStackFollowInStack();
	void mnuStackToggleLock(bool locked);
	
private slots:
	void debugEventHandler(DebugEvent event);
	void ttyProcFinished(int exitCode, QProcess::ExitStatus exitStatus);
	void doOpenFile(const QString &s);
			
public:
	virtual void handleEvent(DebugEvent event);
	
private:
	enum GUI_STATE {
		PAUSED,
		RUNNING,
		TERMINATED
	};

public:
	struct DataViewInfo {
	public:
		DataViewInfo(const MemoryRegions::Region &r = MemoryRegions::Region()) : region(r), view(0) {
			stream = new ByteStreamRegion(region);
		}
		
		~DataViewInfo() {
			release(stream);
		}
	
	private:
		DataViewInfo(const DataViewInfo &);
		DataViewInfo &operator=(const DataViewInfo &);
		
	public:
		MemoryRegions::Region	region;
		ByteStreamRegion		*stream;
		QHexView				*view;
	
	public:
		void update();
	};
	
	void updateData(DataViewInfo *v);
	void clearData(DataViewInfo *v);

private:
	void setupUI();
	QString createTTY();
	void loadWindowState();
	void saveWindowState();
	void followRegisterInDump(bool tabbed);
	void doCPUFill(quint8 byte);
	void doResume(bool passExceptionToApp);
	void updateMenuState(GUI_STATE guiState);
	void updateGUI();
	void refreshGUI();
	void updateDisassembly(edb::address_t address, const MemoryRegions::Region &r);
	void handleEventSignaled();
	void handleEventStopped();
	void handleEventExited();
	void setInitialDebuggerState();
	void setupTabButtons();
	void cleanupDebugger();
	void cleanupEventThread();
	void setInitialBreakpoint(const QString &s);
	void doFollowInDump(const QHexView *hv);
	void doFollowInStack(const QHexView *hv);
	void doDetachFromProcess(bool kill);
	void updateStackView();
	void updateCPUView(MemoryRegions::Region &region);
	void updateDataViews();
	void doMemoryFollow(bool ok, edb::address_t address, bool (*followFunc)(edb::address_t));
	void doApplyDefaultFonts();
	void doModifyBytes(QHexView *s);
	void doModifyBytesBase(edb::address_t address, unsigned int size, QByteArray &bytes, quint8 fill);
	bool doTrap();
	edb::address_t getGotoExpression(bool &ok);
	edb::address_t getFollowAddress(const QHexView *hv, bool &ok);
	edb::reg_t getFollowRegister(bool &ok);
	void setDebuggerCaption(const QString &appname);
	QString sessionFileName() const;
	bool commonOpen(const QString &s, const QStringList &args);
	
private:
	template<void (DebuggerMain::*runFunc)(), void (DebuggerMain::*stepIntoFunc)()>
	void doStepOver();
	
private:
	QStringListModel		*m_ListModel;
	RecentFileManager		*m_RecentFileManager;
	CommentServerInterface	*m_CommentServer;
	DebugEventServer		*m_DebugEventServer;
	QProcess				*m_TTYProc;
	QToolButton				*m_AddTab;
	QToolButton				*m_DelTab;
	DebugEvent				m_LastEvent;
	GUI_STATE				m_GUIState;
	QString					m_Arguments;
	QString					m_LastOpenDirectory;
	QString					m_TTYFile;
	QString					m_WorkingDirectory;
	bool					m_StackViewLocked;
	bool					m_StepWasRun;
	bool					m_UpdateGUI;
	edb::address_t			m_ReenableBreakpointAddress;
	SessionFileManager		m_SessionFileManager;
	QString					m_ProgramExecutable;
	
public:
	QVector<DataViewInfo *>	m_DataRegions;
	DataViewInfo			m_StackViewInfo;
	
public:
	Ui::DebuggerUI ui;
};

#endif
