#ifndef SMPINSTR_H
#define SMPINSTR_H 1

// SMPInstr.h
//
// This header defines the interfaces needed for analyzing instructions.

#include <cstddef>

#include <pro.h>
#include <ida.hpp>
#include <ua.hpp>

#include "SMPDataFlowAnalysis.h"

using namespace std;

class SMPInstr {
public: 
	// Constructors
	SMPInstr(ea_t addr);
	// Operators
	int operator==(const SMPInstr &rhs) const;
	int operator<(const SMPInstr &rhs) const;
	int operator<=(const SMPInstr &rhs) const;
	int operator!=(const SMPInstr &rhs) const;
    // Get methods
	inline ea_t GetAddr(void) const { return address; };
	inline char *GetDisasm(void) const { return (char *) disasm; };
	inline DefOrUse GetUse(size_t index) const { return Uses.GetRef(index); };
	inline DefOrUse GetDef(size_t index) const { return Defs.GetRef(index); };
	inline size_t NumUses(void) const { return Uses.GetSize(); };
	inline size_t NumDefs(void) const { return Defs.GetSize(); };
	inline insn_t GetCmd(void) const { return SMPcmd; };
	inline int GetOptType(void) const { return OptType; };
	inline SMPitype GetDataFlowType(void) const { return type; };
	// Set methods
	inline void SetCmd(insn_t cmd) { SMPcmd = cmd; return; };
	inline void SetTerminatesBlock(void) { BlockTerm = true; };
	inline void SetUseSSA(size_t index, int SSASub) { Uses.SetSSANum(index, SSASub); return; };
	inline void SetDefSSA(size_t index, int SSASub) { Defs.SetSSANum(index, SSASub); return; };
	inline void SetDeadRegs(char RegsString[]) { qstrncpy(DeadRegsString, RegsString, MAXSTR - 1); return; };
	// Query methods
	bool HasDestMemoryOperand(void) const; // Does instruction write to memory?
	bool HasSourceMemoryOperand(void) const; // Does instruction read from memory?
	bool IsSecondSrcOperandNumeric(flags_t F) const;
	bool IsBasicBlockTerminator(void) const;  // kind of inst that always terminates a block
	inline bool IsLastInBlock(void) const { return BlockTerm; };
	inline bool IsJumpTarget(void) const { return JumpTarget; };
	bool IsBranchToFarChunk(void) const;  // instr jumps outside current chunk
	bool MDIsNop(void) const; // instruction is simple or complex no-op
	bool MDIsPushInstr(void) const;
	bool MDIsPopInstr(void) const;
	bool MDIsReturnInstr(void) const;
	bool MDIsEnterInstr(void) const;
	bool MDIsLeaveInstr(void) const;
	bool MDIsStackPointerCopy(bool UseFP) const;  // copies ESP or EBP to register
	bool MDIsFrameAllocInstr(void) const;
	bool MDIsFrameDeallocInstr(bool UseFP, asize_t LocSize) const;
	bool MDUsesCalleeSavedReg(void) const;
	inline bool HasFlagsDef(void) const { return DefsFlags; };
	inline bool HasFlagsUse(void) const { return UsesFlags; };
	// Printing methods
	void PrintOperands(void) const;
	char *DestString(int OptType);
	void Dump(void) const; // Complete debug print, with DEF/USE list, SSA #s
	// Analysis methods
	void Analyze(void); // Fill in basic data for instruction.
	void AnnotateStackConstants(bool UseFP, FILE *AnnotFile);
	void EmitAnnotations(bool UseFP, bool AllocSeen, FILE *AnnotFile);
private:
	// Data
	insn_t SMPcmd; // copy of 'cmd' for this instruction
	ulong features; // Canonical features for SMPcmd
	char disasm[MAXSTR]; // Disassembly text of instruction
	DefOrUseList Defs; // Definitions list
	DefOrUseList Uses; // Uses list
	SMPitype  type; // Data flow analysis category
	int OptType;   // Optimization category (see OptCategory[])
	ea_t address;  // Code address for 1st byte of instruction
	bool analyzed; // Has instr been analyzed yet, setting type
	               //  and DEF and USE lists?
	bool JumpTarget; // Is Instr the target of any jumps or branches?
	bool BlockTerm;  // This instruction terminates a basic block.
	char DeadRegsString[MAXSTR]; // Registers that are dead at this instruction
	bool DefsFlags;  // Instr DEFs the flags
	bool UsesFlags;  // Instr USEs the flags
	// Methods
	void BuildSMPDefUseLists(void); // Build DEF and USE lists for instruction
	void MDFixupDefUseLists(void); // Machine-dependent ad hoc fixes
	void MDAddRegDef(ushort, bool); // Add DEF of register if not already a DEF
	void MDAddRegUse(ushort, bool); // Add USE of register if not already a USE
	void MDAnnotateSIBStackConstants(FILE *, op_t, ea_t, bool); // Handle x86 opcode SIB byte
	void MDAnalyzeDefType(void); // Set types of DEFs if possible
	void MDAnalyzeUseType(void); // Set types of USEs if possible
};  // end class SMPInstr

#endif