#include <api/api_main.h>
#include <extension.h>
#include <vm.h>

extern struct DISASM_TGT target;
extern struct DISASM_ENV disasm_env;
extern struct DISASM_PREFS disasm_prefs;

/* API sections:
 *     6. Code                  :: Creating/manipulating CODE objects
 * /


/* ---------------------------------------------------------------Code */
/* TODO : rewrite these to use curr_rec like api_search.c does */
   int DefineCode(long rva, struct code *c){
      struct address a;
      
      if (! DBIndexFind(ADDRESS_RVA, &rva, &a)) {
         return(SetLastError(4020));
      }
      if (! a.flags & ADDR_CODE){
         a.flags ^= ADDR_DATA;
         a.flags &= ADDR_CODE;
         SetFlags(rva, a.flags);
      }
      /* TODO :cehck if exists? Maybe just add DB restraint */
      DBRecordInsert(CODE, c);
      return(1);
   }

   int UndefineCode(long rva){
      int *state, flags;

      state = DBSaveState();
      flags = GetFlags( rva );
      DBRestoreState(state);

      if (flags & ADDR_CODE) {
         /* remove CODE flag from ADDRESS */
         flags ^= ADDR_CODE;
         flags &= ADDR_DATA;
      } else return(1);

      /* TODO: clean up any xrefs and such here !!! */
      return(DBRecordDelete(CODE_RVA, &rva));
   }

   long GetNextCode(long rva){
      struct address a;
   
      d_keyfind(ADDRESS_RVA, &rva);
      while (db_status == S_OKAY){
         d_keynext(ADDRESS_RVA);
         d_recread(&a);
         if (a.flags & ADDR_CODE) 
            return(a.rva);
      }
      return(0);
   }

   long GetNextData(long rva){
      struct address a;
   
      d_keyfind(ADDRESS_RVA, &rva);
      while (db_status == S_OKAY){
         d_keynext(ADDRESS_RVA);
         d_recread(&a);
         if (a.flags & ADDR_DATA) 
            return(a.rva);
      }
      return(0);
   }

   int GetCodeRep(char *buf, char *fmt, long rva) {
      struct address a;
   
      if (d_keyfind(CODE_RVA, &rva) != S_OKAY) 
         return(SetLastError(4110));
      d_recread(&a);
      return(asmsprintf(buf, fmt, &a));
   }


/* ---------------------------------------------------------ADD_EXPR */
/* these routines deal with address expressions. If an operand is of type
 * OP_ADDREXP, then its operand field references an ID in the ADDR_EXP
 * table.
 */

int DefineAddrExp(int scale, int index, int base, int disp, int flags){
   struct addr_exp a = {0};

   //if (! flags) return(0); /* flags should never be 0 */
   //if (! flags) printf("flags are 0!\n");; /* flags should never be 0 */
   a.scale = scale;
   a.index = index;
   a.base = base;
   a.disp = disp;
   a.flags = flags;
   a.id = ++seq.addr_exp;
   if (DBRecordInsert(ADDR_EXP, &a)) return(a.id);
   return(0);
}

int GetAddrExpr( int id, struct addr_exp *exp ){
   return(DBIndexFind(ADDR_EXP, &id, exp));
}

int FormatAddrExpOp( int operand, int flags, char *buf, int len){
   /* TODO : fix sign */
   if ( ! operand  && flags != ADDREXP_REG ) {
      buf[0] = '\0';
      return(0);
   }
   switch (flags) {
      case ADDREXP_REG:
         strncpy(buf, GetRegMnemonic(operand), len);
         break;
      case ADDREXP_WORD:
         if (operand) snprintf(buf, len, "%04X", operand);
         break;
      case ADDREXP_DWORD:
         if (operand) snprintf(buf, len, "%08X", operand);
         break;
      case ADDREXP_QWORD:
         if (operand) snprintf(buf, len, "%012X", operand);
         break;
      case ADDREXP_BYTE:
      default:
         if (operand) snprintf(buf, len, "%02X", operand);
   }

   return(strlen(buf));
}
int GetAddrExprString( int id, char *string, int len){
   struct addr_exp exp;
   char ops[32], opi[32], opb[32], opd[32];
   int sign = 0;
   
   if (DBIndexFind(ADDR_EXP_ID, &id, &exp)) {
      /* add NULLs for empty values */
      /* note that all register IDs are >= 0 */
      if (exp.scale < 0) sign &= 0x1000; // probably not ever negative
      if (exp.index < 0) sign &= 0x0100;
      if (exp.base  < 0) sign &= 0x0010; // ditto
      if (exp.disp  < 0) sign &= 0x0001;

      /* do scale */
      FormatAddrExpOp( exp.scale, AddrExp_ScaleType(exp.flags), ops, 32);
      /* do index */
      FormatAddrExpOp( exp.index, AddrExp_IndexType(exp.flags), opi, 32);
      /* do byte */
      FormatAddrExpOp( exp.base, AddrExp_BaseType(exp.flags), opb, 32);
      /* do disp */
      FormatAddrExpOp( exp.disp, AddrExp_DispType(exp.flags), opd, 32);
      /* call assembler for address expression format */
      ext_sprint_addrexp(string, len, ops, opi, opb, opd, sign);
      return(strlen(string));
   }
   else printf("not found!\n");
   return(0);
}

int opcmp( long op1, int flag1, long op2, int flag2 ) {
   struct addr_exp *e1, *e2;
   
   if ( flag1 & 0xFFFF00 != flag2 & 0xFFFF00 ) /* re don't care about perm */
      return(0);
   if ( flag1 & OP_TYPE_MASK != OP_EXPR )
      return( (op1 == op2) ? 1 : 0 );
   /* OK, the operand is an ADDR_EXPR, better compare it */
   e1 = GetAddrExpObject(op1);
   e2 = GetAddrExpObject(op2);

   if (e1 && e2 ) {
      if (e1->flags != e2->flags) return(0);
      if (e1->scale != e2->scale) return(0);
      if (e1->index != e2->index) return(0);
      if (e1->base != e2->base) return(0);
      if (e1->disp != e2->disp) return(0);
      return(1);
   }

   if (e1) free(e1);
   if (e2) free(e2);
   return(0);
}

int DefineCodeEffect(long rva, int reg, int change){
   struct code_effect e;

   e.rva = rva;
   e.reg = reg;
   e.change = change;
   e.id = ++seq.code_effect;
   return(DBRecordInsert(CODE_EFFECT, &e));
}
