#ifndef _DEBUG_H_
#define _DEBUG_H_

#include "conddebug.h"
#include "git.h"
#include "nsf.h"

//watchpoint stuffs
#define WP_E       0x01  //watchpoint, enable
#define WP_W       0x02  //watchpoint, write
#define WP_R       0x04  //watchpoint, read
#define WP_X       0x08  //watchpoint, execute
#define WP_F       0x10  //watchpoint, forbid

#define BT_C       0x00  //break type, cpu mem
#define BT_P       0x20  //break type, ppu mem
#define BT_S       0x40  //break type, sprite mem
#define BT_R       0x80  //break type, rom mem

#define BREAK_TYPE_STEP -1
#define BREAK_TYPE_BADOP -2
#define BREAK_TYPE_CYCLES_EXCEED -3
#define BREAK_TYPE_INSTRUCTIONS_EXCEED -4
#define BREAK_TYPE_LUA -5
#define BREAK_TYPE_UNLOGGED_CODE -6
#define BREAK_TYPE_UNLOGGED_DATA -7

//opbrktype is used to grab the breakpoint type that each instruction will cause.
//WP_X is not used because ALL opcodes will have the execute bit set.
static const uint8 opbrktype[256] = {
	      /*0,    1, 2, 3,    4,    5,         6, 7, 8,    9, A, B,    C,    D,         E, F*/
/*0x00*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0,    0, 0, 0,    0, WP_R, WP_R|WP_W, 0,
/*0x10*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0,
/*0x20*/	0, WP_R, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, 0,    0, 0, 0, WP_R, WP_R, WP_R|WP_W, 0,
/*0x30*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0,
/*0x40*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0,    0, 0, 0,    0, WP_R, WP_R|WP_W, 0,
/*0x50*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0,
/*0x60*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0,    0, 0, 0, WP_R, WP_R, WP_R|WP_W, 0,
/*0x70*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0,
/*0x80*/	0, WP_W, 0, 0, WP_W, WP_W,      WP_W, 0, 0,    0, 0, 0, WP_W, WP_W,      WP_W, 0,
/*0x90*/	0, WP_W, 0, 0, WP_W, WP_W,      WP_W, 0, 0, WP_W, 0, 0,    0, WP_W,         0, 0,
/*0xA0*/	0, WP_R, 0, 0, WP_R, WP_R,      WP_R, 0, 0,    0, 0, 0, WP_R, WP_R,      WP_R, 0,
/*0xB0*/	0, WP_R, 0, 0, WP_R, WP_R,      WP_R, 0, 0, WP_R, 0, 0, WP_R, WP_R,      WP_R, 0,
/*0xC0*/	0, WP_R, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, 0,    0, 0, 0, WP_R, WP_R, WP_R|WP_W, 0,
/*0xD0*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0,
/*0xE0*/	0, WP_R, 0, 0, WP_R, WP_R, WP_R|WP_W, 0, 0,    0, 0, 0, WP_R, WP_R, WP_R|WP_W, 0,
/*0xF0*/	0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0, 0, WP_R, 0, 0,    0, WP_R, WP_R|WP_W, 0
};


typedef struct {
	uint32 address;
	uint32 endaddress;
	uint16 flags;

	Condition* cond;
	char* condText;
	char* desc;

} watchpointinfo;

//mbg merge 7/18/06 had to make this extern
extern watchpointinfo watchpoint[65]; //64 watchpoints, + 1 reserved for step over

extern unsigned int debuggerPageSize;
int getBank(int offs);
int GetNesFileAddress(int A);
int GetPRGAddress(int A);
int GetRomAddress(int A);
//int GetEditHex(HWND hwndDlg, int id);
uint8 *GetNesPRGPointer(int A);
uint8 *GetNesCHRPointer(int A);
void KillDebugger();
uint8 GetMem(uint16 A);
uint8 GetPPUMem(uint8 A);

//---------CDLogger
void LogCDVectors(int which);
void LogCDData(uint8 *opcode, uint16 A, int size);
extern volatile int codecount, datacount, undefinedcount;
extern unsigned char *cdloggerdata;
extern unsigned int cdloggerdataSize;

extern int debug_loggingCD;
static INLINE void FCEUI_SetLoggingCD(int val) { debug_loggingCD = val; }
static INLINE int FCEUI_GetLoggingCD() { return debug_loggingCD; }
//-------

//-------tracing
//we're letting the win32 driver handle this ittself for now
//extern int debug_tracing;
//static INLINE void FCEUI_SetTracing(int val) { debug_tracing = val; }
//static INLINE int FCEUI_GetTracing() { return debug_tracing; }
//---------

//--------debugger
extern int iaPC;
extern uint32 iapoffset; //mbg merge 7/18/06 changed from int
void DebugCycle();
bool CondForbidTest(int bp_num);
void BreakHit(int bp_num);

extern bool break_asap;
extern bool break_on_unlogged_code;
extern bool break_on_unlogged_data;
extern uint64 total_cycles_base;
extern uint64 delta_cycles_base;
extern bool break_on_cycles;
extern uint64 break_cycles_limit;
extern uint64 total_instructions;
extern uint64 delta_instructions;
extern bool break_on_instructions;
extern uint64 break_instructions_limit;
extern void ResetDebugStatisticsCounters();
extern void ResetCyclesCounter();
extern void ResetInstructionsCounter();
extern void ResetDebugStatisticsDeltaCounters();
extern void IncrementInstructionsCounters();
//-------------

//internal variables that debuggers will want access to
extern uint8 *vnapage[4],*VPage[8];
extern uint8 PPU[4],PALRAM[0x20],UPALRAM[3],SPRAM[0x100],VRAMBuffer,PPUGenLatch,XOffset;
extern uint32 FCEUPPU_PeekAddress();
extern uint8 READPAL_MOTHEROFALL(uint32 A);
extern int numWPs;

///encapsulates the operational state of the debugger core
class DebuggerState {
public:
	///indicates whether the debugger is stepping through a single instruction
	bool step;
	///indicates whether the debugger is stepping out of a function call
	bool stepout;
	///indicates whether the debugger is running one line
	bool runline;
	///target timestamp for runline to stop at
	uint64 runline_end_time;
	///indicates whether the debugger should break on bad opcodes
	bool badopbreak;
	///counts the nest level of the call stack while stepping out
	int jsrcount;

	///resets the debugger state to an empty, non-debugging state
	void reset() {
		numWPs = 0;
		step = false;
		stepout = false;
		jsrcount = 0;
	}
};

extern NSF_HEADER NSFHeader;

extern uint8 PSG[0x10];
extern uint8 DMCFormat;
extern uint8 RawDALatch;
extern uint8 DMCAddressLatch;
extern uint8 DMCSizeLatch;
extern uint8 EnabledChannels;
extern uint8 SpriteDMA;
extern uint8 RawReg4016;
extern uint8 IRQFrameMode;

///retrieves the core's DebuggerState
DebuggerState &FCEUI_Debugger();

//#define CPU_BREAKPOINT 1
//#define PPU_BREAKPOINT 2
//#define SPRITE_BREAKPOINT 4
//#define READ_BREAKPOINT 8
//#define WRITE_BREAKPOINT 16
//#define EXECUTE_BREAKPOINT 32

int offsetStringToInt(unsigned int type, const char* offsetBuffer, bool *conversionOk = nullptr);
unsigned int NewBreak(const char* name, int start, int end, unsigned int type, const char* condition, unsigned int num, bool enable);

#endif
