#ifdef SUPERMODEL_DEBUGGER
#ifndef INCLUDED_CODEANALYSER_H
#define INCLUDED_CODEANALYSER_H

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

#include "Types.h"

#include "AddressTable.h"

namespace Debugger
{
	class CCPUDebug;
	class CRegion;
	class CLabel;

	enum ELabelFlags
	{
		LFNone         = 0,
		LFEntryPoint   = 1, 
		LFExcepHandler = 2, 
		LFInterHandler = 4,
		LFJumpTarget   = 8, 
		LFLoopPoint    = 16,
		LFSubroutine   = 32,
		LFUnseenCode   = 64,
		LFAll          = (LFEntryPoint | LFExcepHandler | LFInterHandler | LFJumpTarget | LFSubroutine | LFLoopPoint | LFUnseenCode)
	};

	/*
	 * Class that represents an entry point into a program's code in memory, eg a CPU's reset address or exception handler's address etc.
	 */
	class CEntryPoint
	{
	private:
		char autoLabelStr[255];

	public:
		UINT32 addr;
		ELabelFlags autoFlag;
		const char *autoLabel;

		CEntryPoint(const CEntryPoint &other);

		CEntryPoint(UINT32 eAddr, ELabelFlags eAutoFlag, const char *eAutoLabel);

		CEntryPoint &operator=(const CEntryPoint &other);

		bool operator==(const CEntryPoint &other);
		
		bool operator!=(const CEntryPoint &other);
	};

	/*
	 * Class that represents an auto-generated label that resulted from code analysis, eg a known entry point or the destination of a subroutine call.
	 */
	class CAutoLabel : public CAddressRef
	{
	private:
		static const char *s_defaultLabelFmts[];

		const char *m_subLabels[7];
		
		unsigned m_acquired;

		const char *CreateDefaultSubLabel(ELabelFlags flag);

	public:
		static const unsigned numLabelFlags;

		static ELabelFlags GetLabelFlag(int index);

		static int GetFlagIndex(ELabelFlags flag);

		static const char *GetFlagString(ELabelFlags flag);

		ELabelFlags flags;
		
		CAutoLabel(CCPUDebug *lCPU, UINT32 lAddr);

		~CAutoLabel();

		void Acquire();

		void Release();

		void AddFlag(ELabelFlags flag, const char *subLabel);

		bool GetLabel(char *label, ELabelFlags flag = LFAll);

		bool ContainsSubLabel(const char *subLabel);
	};

	class CCodeAnalyser;

	/*
     * Class that holds the results of having analysed the program code.
	 */
	class CCodeAnalysis
	{
	friend class CCodeAnalyser;

	private:
		vector<CEntryPoint> m_entryPoints;
		vector<UINT32> m_unseenEntryAddrs;
		vector<bool> m_seenIndices;
		vector<bool> m_validIndices;
		map<UINT32,CAutoLabel*> m_autoLabelsMap;

		unsigned m_acquired;

		CCodeAnalysis(CCodeAnalyser *aAnalyser);

		CCodeAnalysis(CCodeAnalyser *aAnalyser, unsigned aTotalIndices, vector<CEntryPoint> &entryPoints, vector<UINT32> &m_unseenEntryAddrs);

		CCodeAnalysis(CCodeAnalysis *oldAnalysis, vector<CEntryPoint> &entryPoints, vector<UINT32> &m_unseenEntryAddrs);

		void FinishAnalysis();

	public:
		CCodeAnalyser *analyser;
		set<unsigned> validIndexSet;
		vector<CAutoLabel*> autoLabels;
		
		~CCodeAnalysis();

		void Acquire();

		void Release();

		bool IsAddrValid(UINT32 addr);

		bool GetNextValidAddr(UINT32 &addr);

		bool IsIndexValid(unsigned index);

		bool GetNextValidIndex(unsigned &index);

		bool HasSeenAddr(UINT32 addr);

		bool HaveSeenIndex(unsigned index);

		CAutoLabel *GetAutoLabel(UINT32 addr);

		CAutoLabel *GetAutoLabel(const char *subLabel);

		vector<CAutoLabel*> GetAutoLabels(ELabelFlags flag);
	};

	/*
	 * Class that analyses a program's code to work out all the reachable program locations from a given set of entry points into the code.
	 * During the analysis, it keeps track of valid memory locations (to help with the disassembly of code for CPUs with variable-length 
	 * instruction sets) and generates a set of auto-labels that identify places of interest such as subroutines, jump destinations and 
	 * exception handlers etc.
	 * This sort of analysis works well for static addressing modes but not so well for dynamic address referencing or self-modifying code.
	 * To allow for the latter cases the analyser updates its analysis whenever it encounters an unseen memory location or it sees 
	 * that code has changed from a previous inspection.
	 */
	class CCodeAnalyser
	{	
	private:
		vector<CRegion*> m_codeRegions;
		vector<unsigned> m_indexBounds;

		vector<UINT32> m_customEntryAddrs;

		bool m_abortAnalysis;

		void CheckEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, vector<CEntryPoint> &prevPoints,
			bool &needsAnalysis, bool &reanalyse);
		
		void GatherEntryPoints(vector<CEntryPoint> &entryPoints, vector<UINT32> &unseenEntryAddrs, bool &reanalyse);

		void AddEntryPoint(vector<CEntryPoint> &entryPoints, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel);

		void AnalyseCode(vector<bool> &seenIndices, vector<bool> &validIndices, set<unsigned> &validIndexSet, map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr);

		void AddFlagToAddr(map<UINT32,CAutoLabel*> &autoLabelsMap, UINT32 addr, ELabelFlags autoFlag, const char *autoLabel);

	public:
		CCPUDebug *cpu;

		CCodeAnalysis emptyAnalysis;

		CCodeAnalysis *analysis;
		
		unsigned instrAlign;
		unsigned totalIndices;

		CCodeAnalyser(CCPUDebug *aCPU);

		~CCodeAnalyser();

		void Reset();

		bool GetAddrOfIndex(unsigned index, UINT32 &addr);

		bool GetIndexOfAddr(UINT32 addr, unsigned &index);

		bool NeedsAnalysis();

		bool AnalyseCode();

		void AbortAnalysis();

		void AddCustomEntryAddr(UINT32 entryAddr);

		bool RemoveCustomEntryAddr(UINT32 entryAddr);
	};
}

#endif	// INCLUDED_CODEANALYSER_H
#endif  // SUPERMODEL_DEBUGGER