#ifndef _INTERPRETER_HPP
#define _INTERPRETER_HPP

class IntprCallback : public DisasCallback {
public:
/* Mov with Immediate value */
void MoviI64(unsigned int reg_idx, uint64_t imm, bool bit64);

/* Deposit (i.e. distination register won't be changed) with Immediate value */
void DepositI64(unsigned int reg_idx, uint64_t imm, unsigned int pos, unsigned int len, bool bit64);
void DepositReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64);
void DepositZeroI64(unsigned int reg_idx, uint64_t imm, unsigned int pos, unsigned int len, bool bit64);
void DepositZeroReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64);

/* Mov between registers */
void MovReg(unsigned int rd_idx, unsigned int rn_idx, bool bit64);

/* Conditional mov between registers */
void CondMovReg(unsigned int cond, unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool bit64);

/* Add/Sub with Immediate value */
void AddI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64);
void SubI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t imm, bool setflags, bool bit64);

/* Add/Sub/Mul/Div between registers */
void AddReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool setflags, bool bit64);
void SubReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool setflags, bool bit64);
void MulReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool sign, bool dst64, bool src64);
void Mul2Reg(unsigned int rh_idx, unsigned int rl_idx, unsigned int rn_idx, unsigned int rm_idx, bool sign); //64bit * 64bit
void DivReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool sign, bool bit64);
void ShiftReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, unsigned int shift_type, bool bit64) ;

/* Add/Sub with carry flag between registers */
void AddcReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool setflags, bool bit64);
void SubcReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool setflags, bool bit64);

/* AND/OR/EOR/Shift ... with Immediate value */
void AndI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool setflags, bool bit64);
void OrrI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64);
void EorI64(unsigned int rd_idx, unsigned int rn_idx, uint64_t wmask, bool bit64);
void ShiftI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int shift_type, unsigned int shift_amount, bool bit64);

/* AND/OR/EOR/BIC/NOT ... between registers */
void AndReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool setflags, bool bit64);
void OrrReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool bit64);
void EorReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool bit64);
void BicReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx, bool setflags, bool bit64);
void NotReg(unsigned int rd_idx, unsigned int rm_idx, bool bit64);
void ExtendReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int extend_type, bool bit64);

/* Load/Store */
void LoadReg(unsigned int rd_idx, unsigned int base_idx, unsigned int rm_idx, int size, bool is_sign, bool extend, bool post, bool bit64);
void LoadRegI64(unsigned int rd_idx, unsigned int ad_idx, int size, bool is_sign, bool extend);
void StoreReg(unsigned int rd_idx, unsigned int base_idx, unsigned int rm_idx, int size, bool is_sign, bool extend, bool post, bool bit64);
void StoreRegI64(unsigned int rd_idx, unsigned int ad_idx, int size, bool is_sign, bool extend);
void _LoadReg(unsigned int rd_idx, uint64_t addr, int size, bool is_sign, bool extend);
void _StoreReg(unsigned int rd_idx, uint64_t addr, int size, bool is_sign, bool extend);

/* Bitfield Signed/Unsigned Extract... with Immediate value */
void SExtractI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64);
void UExtractI64(unsigned int rd_idx, unsigned int rn_idx, unsigned int pos, unsigned int len, bool bit64);

/* Signed Extend from 32bit */
void SExt32(unsigned int rd_idx, unsigned int rn_idx);

/* Reverse bit order */
void RevBit(unsigned int rd_idx, unsigned int rn_idx, bool bit64);
/* Reverse byte order per 16bit */
void RevByte16(unsigned int rd_idx, unsigned int rn_idx, bool bit64);
/* Reverse byte order per 32bit */
void RevByte32(unsigned int rd_idx, unsigned int rn_idx, bool bit64);
/* Reverse byte order per 64bit */
void RevByte64(unsigned int rd_idx, unsigned int rn_idx, bool bit64);
/* Count Leading Zeros */
void CntLeadZero(unsigned int rd_idx, unsigned int rn_idx, bool bit64);
/* Count Leading Signed bits */
void CntLeadSign(unsigned int rd_idx, unsigned int rn_idx, bool bit64);

/* Conditional compare... with Immediate value */
void CondCmpI64(unsigned int rn_idx, unsigned int imm, unsigned int nzcv, unsigned int cond, unsigned int op, bool bit64);
/* Conditional compare... between registers */
void CondCmpReg(unsigned int rn_idx, unsigned int rm_idx, unsigned int nzcv, unsigned int cond, unsigned int op, bool bit64);

/* Go to Immediate address */
void BranchI64(uint64_t imm);

/* Conditional Branch with Immediate value and jump to Immediate address */
void BranchCondiI64(unsigned int cond, unsigned int rt_idx, uint64_t imm, uint64_t addr, bool bit64);

/* Conditional Branch with NZCV flags */
void BranchFlag(unsigned int cond, uint64_t addr);

/* Set PC with reg */
void SetPCReg(unsigned int rt_idx);

/* Super Visor Call */
void SVC(unsigned int svc_num);
/* Breakpoint exception */
void BRK(unsigned int memo);

/* Read/Write Sysreg */
void ReadWriteSysReg(unsigned int rd_idx, int offset, bool read);
/* Read/Write NZCV */
void ReadWriteNZCV(unsigned int rd_idx, bool read);

/* Fp Mov between registers */
void FMovReg(unsigned int fd_idx, unsigned int fn_idx, int type);
/* Fp Mov between registers (float <-> int)*/
void FMovConv(unsigned int rd_idx, unsigned int rn_idx, int type, bool itof);

/* #######  Vector ####### */

/* Load/Store for vector */
void LoadVecReg(unsigned int vd_idx, int element, unsigned int rn_idx, int size);
void StoreVecReg(unsigned int rd_idx, int element, unsigned int vn_idx, int size);

/* Load/Store for FP */
void LoadFpReg(unsigned int rd_idx, unsigned int base_idx, unsigned int rm_idx, int size, bool post, bool bit64);
void StoreFpReg(unsigned int rd_idx, unsigned int base_idx, unsigned int rm_idx, int size, bool post, bool bit64);

/* Load/Store for FP */
void LoadFpRegI64(unsigned int fd_idx, unsigned int ad_idx, int size);
void StoreFpRegI64(unsigned int fd_idx, unsigned int ad_idx, int size);

/* AND/OR/EOR/BIC/NOT ... between vector registers */
void AndVecReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx);
void OrrVecReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx);
void EorVecReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx);
void BicVecReg(unsigned int rd_idx, unsigned int rn_idx, unsigned int rm_idx);
void NotVecReg(unsigned int rd_idx, unsigned int rm_idx);

/* Read Vector register to FP register */
void ReadVecReg(unsigned int fd_idx, unsigned int vn_idx, unsigned int index, int size);
/* Read Vector register to general register */
void ReadVecElem(unsigned int rd_idx, unsigned int vn_idx, unsigned int index, int size);
/* Write general register value tot Vector register */
void WriteVecElem(unsigned int vd_idx, unsigned int rn_idx, unsigned int index, int size);

/* Duplicate an immediate value to vector register */
void DupVecImmI32(unsigned int vd_idx, uint32_t imm, int size, int dstsize);
void DupVecImmI64(unsigned int vd_idx, uint64_t imm, int size, int dstsize);
/* Duplicate an element of vector register to new one */
void DupVecReg(unsigned int vd_idx, unsigned int vn_idx, unsigned int index, int size, int dstsize);
/* Duplicate an general register into vector register */
void DupVecRegFromGen(unsigned int vd_idx, unsigned int rn_idx, int size, int dstsize);

/* Compare Bit wise equal */
void CompareEqualVec(unsigned int vd_idx, unsigned int vn_idx, unsigned int vm_idx, int index, int size);

/* Compare Bit wise test bits nonzero */
void CompareTestBitsVec(unsigned int vd_idx, unsigned int vn_idx, unsigned int vm_idx, int index, int size);

};

/* Global Interpreter singleton class .*/
class Interpreter {
private:
Interpreter() = default;
~Interpreter() = default;

static Interpreter *inst;
static IntprCallback *disas_cb;
public:
Interpreter(const Interpreter&) = delete;
Interpreter& operator=(const Interpreter&) = delete;
Interpreter(Interpreter&&) = delete;
Interpreter& operator=(Interpreter&&) = delete;

void Init();

static Interpreter *get_instance() {
	return inst;
}

static void create() {
	if (!inst) {
		inst = new Interpreter;
		inst->disas_cb = new IntprCallback;
	}
}

static void destroy() {
	if (inst) {
		delete inst->disas_cb;
		delete inst;
		inst = nullptr;
	}
}
void Run();
int SingleStep();
};
#endif
