//===-- RISCVInstPrinter.cpp - Convert RISCV MCInst to asm syntax ---------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class prints an RISCV MCInst to a .s file.
//
//===----------------------------------------------------------------------===//

#ifdef CAPSTONE_HAS_RISCV

#include <stdio.h>		// DEBUG
#include <stdlib.h>
#include <string.h>
#include <capstone/platform.h>

#include "RISCVInstPrinter.h"
#include "RISCVBaseInfo.h"
#include "../../MCInst.h"
#include "../../SStream.h"
#include "../../MCRegisterInfo.h"
#include "../../utils.h"
#include "RISCVMapping.h"

//#include "RISCVDisassembler.h"

#define GET_REGINFO_ENUM
#define GET_REGINFO_MC_DESC
#include "RISCVGenRegisterInfo.inc"
#define GET_INSTRINFO_ENUM
#include "RISCVGenInstrInfo.inc"

// Autogenerated by tblgen.
static void printInstruction(MCInst *MI, SStream *O, MCRegisterInfo *MRI);
static bool printAliasInstr(MCInst *MI, SStream *OS, void *info);
static void printOperand(MCInst *MI, unsigned OpNo, SStream *O);
static void printFenceArg(MCInst *MI, unsigned OpNo, SStream *O);
static void printCSRSystemRegister(const MCInst*, unsigned, SStream *);
static void printFRMArg(MCInst *MI, unsigned OpNo, SStream *O);
static void printCustomAliasOperand( MCInst *, unsigned, unsigned, SStream *);
/// getRegisterName - This method is automatically generated by tblgen
/// from the register set description.  This returns the assembler name
/// for the specified register.
static const char *getRegisterName(unsigned RegNo, unsigned AltIdx);

// Include the auto-generated portion of the assembly writer.
#define PRINT_ALIAS_INSTR
#include "RISCVGenAsmWriter.inc"


static void fixDetailOfEffectiveAddr(MCInst *MI)
{
	unsigned reg = 0;
	int64_t imm = 0;

	CS_ASSERT(3 == MI->flat_insn->detail->riscv.op_count);
	CS_ASSERT(RISCV_OP_REG == MI->flat_insn->detail->riscv.operands[0].type);

	if (RISCV_OP_IMM == MI->flat_insn->detail->riscv.operands[1].type) {
		CS_ASSERT(RISCV_OP_REG == MI->flat_insn->detail->riscv.operands[2].type);
		imm = MI->flat_insn->detail->riscv.operands[1].imm;
		reg = MI->flat_insn->detail->riscv.operands[2].reg;
	} else if (RISCV_OP_REG == MI->flat_insn->detail->riscv.operands[1].type) {
		CS_ASSERT(RISCV_OP_IMM == MI->flat_insn->detail->riscv.operands[2].type);
		reg = MI->flat_insn->detail->riscv.operands[1].reg;
		imm = MI->flat_insn->detail->riscv.operands[2].imm;
	}

	// set up effective address.
	MI->flat_insn->detail->riscv.operands[1].type = RISCV_OP_MEM;
	MI->flat_insn->detail->riscv.op_count--;
     	MI->flat_insn->detail->riscv.operands[1].mem.base = reg;
     	MI->flat_insn->detail->riscv.operands[1].mem.disp = imm;

	return;
}


//void RISCVInstPrinter::printInst(const MCInst *MI, raw_ostream &O,
//                                 StringRef Annot, const MCSubtargetInfo &STI) 
void RISCV_printInst(MCInst *MI, SStream *O, void *info) 
{
  	MCRegisterInfo *MRI = (MCRegisterInfo *) info;
  	//bool Res = false;
  	//MCInst *NewMI = MI;
  	// TODO: RISCV compressd instructions.
  	//MCInst UncompressedMI;
  	//if (!NoAliases)
    	//Res = uncompressInst(UncompressedMI, *MI, MRI, STI);
  	//if (Res)
    	//NewMI = const_cast<MCInst *>(&UncompressedMI);
  	if (/*NoAliases ||*/ !printAliasInstr(MI, O, info))
    		printInstruction(MI, O, MRI);
  		//printAnnotation(O, Annot);
	// fix load/store type insttuction
    	if (MI->csh->detail && 
	    MI->flat_insn->detail->riscv.need_effective_addr)
		fixDetailOfEffectiveAddr(MI);
	
	return;
}

static void printRegName(SStream *OS, unsigned RegNo) 
{
  	SStream_concat0(OS, getRegisterName(RegNo, RISCV_ABIRegAltName));
}

/**
void RISCVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
                                    raw_ostream &O, const char *Modifier) 
*/
static void printOperand(MCInst *MI, unsigned OpNo, SStream *O) 
{
  	unsigned reg;
  	int64_t Imm = 0;

  	MCOperand *MO = MCInst_getOperand(MI, OpNo);
  
  	if (MCOperand_isReg(MO)) {
    		reg = MCOperand_getReg(MO);
    		printRegName(O, reg);
    		if (MI->csh->detail) {
      			MI->flat_insn->detail->riscv.operands[MI->flat_insn->detail->riscv.op_count].type = RISCV_OP_REG;
      			MI->flat_insn->detail->riscv.operands[MI->flat_insn->detail->riscv.op_count].reg = reg;
      			MI->flat_insn->detail->riscv.op_count++;
    		}
  	} else {
		CS_ASSERT(MCOperand_isImm(MO) && "Unknown operand kind in printOperand");
    		Imm = MCOperand_getImm(MO);
    		if (Imm >= 0) {
      			if (Imm > HEX_THRESHOLD)
        			SStream_concat(O, "0x%" PRIx64, Imm);
      			else
				SStream_concat(O, "%" PRIu64, Imm);
    		} else {
      			if (Imm < -HEX_THRESHOLD)
				SStream_concat(O, "-0x%" PRIx64, -Imm);
      			else
				SStream_concat(O, "-%" PRIu64, -Imm);
    		}

    		if (MI->csh->detail) {
      			MI->flat_insn->detail->riscv.operands[MI->flat_insn->detail->riscv.op_count].type = RISCV_OP_IMM;
      			MI->flat_insn->detail->riscv.operands[MI->flat_insn->detail->riscv.op_count].imm = Imm;
      			MI->flat_insn->detail->riscv.op_count++;
		}
    	}

  	//CS_ASSERT(MO.isExpr() && "Unknown operand kind in printOperand");
	
	return;
}

static void printCSRSystemRegister(const MCInst *MI, unsigned OpNo,
                                   //const MCSubtargetInfo &STI,
                                   SStream *O) 
{
	// TODO: Not yeat implementated.
	return;
  	//CS_ASSERT (0 && "CSR system register hav't support.");
#if 0
  unsigned Imm = MI->getOperand(OpNo).getImm();
  auto SysReg = RISCVSysReg::lookupSysRegByEncoding(Imm);
  if (SysReg && SysReg->haveRequiredFeatures(STI.getFeatureBits()))
    O << SysReg->Name;
  else
    O << Imm;
#endif
}

static void printFenceArg(MCInst *MI, unsigned OpNo, SStream *O) 
{
  	unsigned FenceArg = MCOperand_getImm(MCInst_getOperand(MI, OpNo));
  	//CS_ASSERT (((FenceArg >> 4) == 0) && "Invalid immediate in printFenceArg");

  	if ((FenceArg & RISCVFenceField_I) != 0)
    		SStream_concat0(O, "i");
  	if ((FenceArg & RISCVFenceField_O) != 0)
    		SStream_concat0(O, "o");
 	if ((FenceArg & RISCVFenceField_R) != 0)
    		SStream_concat0(O, "r");
  	if ((FenceArg & RISCVFenceField_W) != 0)
    		SStream_concat0(O, "w");
  	if (FenceArg == 0)
    		SStream_concat0(O, "unknown");
}

static void printFRMArg(MCInst *MI, unsigned OpNo, SStream *O) 
{
  	enum RoundingMode FRMArg = 
    	  (enum RoundingMode)MCOperand_getImm(MCInst_getOperand(MI, OpNo));
#if 0
  auto FRMArg =
      static_cast<RISCVFPRndMode::RoundingMode>(MI->getOperand(OpNo).getImm());
  O << RISCVFPRndMode::roundingModeToString(FRMArg);
#endif
  	SStream_concat0(O, roundingModeToString(FRMArg));
}
  
#endif				// CAPSTONE_HAS_RISCV
