/**
 ** Supermodel
 ** A Sega Model 3 Arcade Emulator.
 ** Copyright 2011 Bart Trzynadlowski, Nik Henson
 **
 ** This file is part of Supermodel.
 **
 ** Supermodel 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 3 of the License, or (at your option)
 ** any later version.
 **
 ** Supermodel 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 Supermodel.  If not, see <http://www.gnu.org/licenses/>.
 **/

/*
 * Debugger.h
 */

#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_DEBUGGER_H
#define INCLUDED_DEBUGGER_H

#include <stdio.h>
#include <stdarg.h>
#include <vector>
#include <algorithm>
using namespace std;

#include "Types.h"

//#if defined(SUPERMODEL_WIN32) || defined(SUPERMODEL_UNIX) || defined(SUPERMODEL_OSX)
#define DEBUGGER_HASBLOCKFILE
#define DEBUGGER_HASLOGGER
#define DEBUGGER_HASTHREAD
//#endif

#define DEBUGGER_STATEFILE_VERSION 0

#ifdef DEBUGGER_HASBLOCKFILE
#include "BlockFile.h"
#endif // DEBUGGER_HASBLOCKFILE

#ifdef DEBUGGER_HASLOGGER
#ifndef SUPERMODEL_VERSION
#define SUPERMODEL_VERSION ""
#endif // SUPERMODEL_VERSEION
#include "OSD/Logger.h"
#endif // DEBUGGER_HASLOGGER

#ifdef DEBUGGER_HASTHREAD
#include "OSD/Thread.h"
#endif // DEBUGGER_HASTHREAD

#ifndef stricmp
#ifdef _MSC_VER	// MS VisualC++
	#define stricmp	_stricmp
#else           // Assume GC
	#define stricmp	strcasecmp
#endif // _MSC_VER
#endif // stricmp

namespace Debugger
{
	class CCPUDebug;
	class CCodeAnalyser;
	class CException;
	class CInterrupt;
	class CIO;
	class CWatch;
	class CBreakpoint;
	class CRegMonitor;

	enum EHaltReason
	{
		HaltNone  = 0,
		HaltState = 1,
		HaltUser  = 2,
		HaltStep  = 4,
		HaltCount = 8,
		HaltUntil = 16
	};

	enum EFormat
	{
		Hex = 0,
		Hex0x,
		HexDollar,
		HexPostH,
		Decimal,
		Binary,
		ASCII
	};

	/*
	 * Base class for a debugger.
	 * Provides common methods for loading/saving debugger state, formatting/parsing data and general control of the debugger.
	 * Also holds references to the CCPUDebug objects, one for each debuggable CPU, and contains pure virtual methods for sub-classes to 
	 * implement which act as callbacks for the various debugging events that can occur.
	 */
#ifdef DEBUGGER_HASLOGGER
	class CDebugger : public CLogger
#else
	class CDebugger
#endif // DEBUGGER_HASLOGGER
	{
	friend class CCPUDebug;
	friend class CCodeAnalyser;

	private:
		bool m_exit;
		bool m_pause;
		bool m_break;
		bool m_breakUser;

#ifdef DEBUGGER_HASTHREAD
		CMutex *m_mutex;
		CCPUDebug *m_primaryCPU;
#endif

	protected:
		virtual void AddCPUs() = 0;
		
		virtual void DeleteCPUs();
	
#ifdef DEBUGGER_HASBLOCKFILE
		virtual bool LoadState(CBlockFile *state);

		virtual bool SaveState(CBlockFile *state);
#endif // DEBUGGER_HASBLOCKFILE

		//
		// Protected virtual methods for sub-classes to implement
		//

		virtual void AnalysisUpdated(CCodeAnalyser *analyser) = 0;

		virtual void ExceptionTrapped(CException *ex) = 0;

		virtual void InterruptTrapped(CInterrupt *in) = 0;

		virtual void MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead) = 0;

		virtual void IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput) = 0;

		virtual void BreakpointReached(CBreakpoint *bp) = 0;

		virtual void MonitorTriggered(CRegMonitor *regMon) = 0;

		virtual void ExecutionHalted(CCPUDebug *cpu, EHaltReason reason) = 0;

		virtual void WaitCommand(CCPUDebug *cpu) = 0;

		virtual void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) = 0;

	public:
		vector<CCPUDebug*> cpus;

		UINT64 frameCount;

#ifdef DEBUGGER_HASLOGGER
		bool logDebug;
		bool logInfo;
		bool logError;
#endif // DEBUGGER_HASLOGGER

		static unsigned GetDataSize(UINT64 data);

		static const char *GetSizeString(unsigned dataSize);

		static UINT64 MaskData(unsigned dataSize, UINT64 data);

		static UINT64 GetSlottedData(UINT32 addr, unsigned dataSize, UINT64 data, UINT32 slotAddr, unsigned slotDataSize);

		static bool ParseInt(const char *str, int *val);

		CDebugger();

		virtual ~CDebugger();

		void AddCPU(CCPUDebug *cpu);

		void RemoveCPU(CCPUDebug *cpu);

		CCPUDebug *GetCPU(const char *name);

		void SetExit();

		void SetPause(bool pause);

		void ForceBreak(bool user);

		void ClearBreak();

		bool CheckExit();

		bool CheckPause();

#ifdef DEBUGGER_HASTHREAD
		bool MakePrimary(CCPUDebug *cpu);

		void ReleasePrimary();
#endif // DEBUGGER_HASTHREAD

		//
		// Printing/logging
		//

		void PrintEvent(CCPUDebug *cpu, const char *fmt, ...);

#ifdef DEBUGGER_HASLOGGER
		// CLogger logging methods
		virtual void DebugLog(const char *fmt, va_list vl);

		virtual void InfoLog(const char *fmt, va_list vl);

		virtual void ErrorLog(const char *fmt, va_list vl);
#endif // DEBUGGER_HASLOGGER

		//
		// Formatting/parsing
		//

		// TODO - add in bufSize
		virtual bool ParseData(const char *str, EFormat format, unsigned dataSize, UINT64 *data);

		virtual void FormatData(char *str, EFormat format, unsigned dataSize, INT64 data);

		virtual void FormatData(char *str, EFormat format, unsigned dataSize, UINT64 data);

		//
		// State loading/saving
		//

		virtual bool HasState();

		virtual bool LoadState(const char *fileName);

		virtual bool SaveState(const char *fileName);

		//
		// Public virtual methods for sub-classes to implement
		//

		virtual void Attach();

		virtual void Detach();

		virtual void Reset();

		virtual void Poll();
	};

	//
	// Inlined methods
	//

	inline bool CDebugger::CheckExit()
	{
		return m_exit;
	}

	inline bool CDebugger::CheckPause()
	{
		return m_pause;
	}
}

#endif	// INCLUDED_DEBUGGER_H
#endif  // SUPERMODEL_DEBUGGER