#include <string>
#include <sstream>
#include <iomanip>


#include "cmd_gpsim.h"
#include "sim_context.h"
#include "symbol.h"
#include "cmd_manager.h"


const char * s_psEnglishMessages[] = {
  "",                                                     // Place holder so we don't have a zero
  "break reading register %s\n",                          // IDS_BREAK_READING_REG
  "break reading register %s with value %u\n",            // IDS_BREAK_READING_REG_VALUE
  "break reading register %s %s %u\n",                    // IDS_BREAK_READING_REG_OP_VALUE
  "break writing register %s\n",                          // IDS_BREAK_WRITING_REG
  "break writing register %s with value %u\n",            // IDS_BREAK_WRITING_REG_VALUE
  "break writing register %s %s %u\n",                    // IDS_BREAK_WRITING_REG_OP_VALUE
  "execution break at address %s\n",                      // IDS_BREAK_ON_EXEC_ADDRESS
  "unrecognized processor in the program file\n",         // IDS_PROGRAM_FILE_PROCESSOR_NOT_KNOWN
  "file name '%s' is too long\n",                         // IDS_FILE_NAME_TOO_LONG
  "file %s not found\n",                                  // IDS_FILE_NOT_FOUND
  "file %s is not formatted properly\n",                  // IDS_FILE_BAD_FORMAT
  "no processor has been specified\n",                    // IDS_NO_PROCESSOR_SPECIFIED
  "processor %s initialization failed\n",                 // IDS_PROCESSOR_INIT_FAILED
  "the program file type does not contain processor\n"    // first part of IDS_FILE_NEED_PROCESSOR_SPECIFIED
  "you need to specify processor with the processor command\n", // IDS_FILE_NEED_PROCESSOR_SPECIFIED
  "an appropriate list file for %s was not found\n",      // IDS_LIST_FILE_NOT_FOUND
  "Hit breakpoint %d\n",                                  // IDS_HIT_BREAK 
  NULL,     // IDS_
};

class CGpsimUserInterface : public IUserInterface {
public:
  CGpsimUserInterface(const char *paStrings[]);
  virtual ~CGpsimUserInterface() {}

  void         SetStreams(FILE *in, FILE *out);
  virtual ISimConsole &GetConsole();
  virtual void SetConsole(ISimConsole *pConsole);
  virtual void DisplayMessage(unsigned int uStringID, ...);
  virtual void DisplayMessage(FILE * pOut, unsigned int uStringID, ...);
  virtual void DisplayMessage(const char *fmt, ...);
  virtual void DisplayMessage(FILE * pOut, const char *fmt, ...);

  virtual const char * FormatProgramAddress(unsigned int uAddress,
    unsigned int uMask);
  virtual const char * FormatProgramAddress(unsigned int uAddress,
    unsigned int uMask, int iRadix);
  virtual const char * FormatRegisterAddress(Register *);
  virtual const char * FormatRegisterAddress(unsigned int uAddress,
    unsigned int uMask);
  virtual const char * FormatLabeledValue(const char * pLabel,
    unsigned int uValue);
  virtual const char * FormatLabeledValue(const char * pLabel,
    unsigned int uValue, unsigned int uMask, int iRadix,
    const char *pHexPrefix);
  virtual const char * FormatValue(unsigned int uValue);
  virtual const char * FormatValue(gint64 uValue);
  virtual const char * FormatValue(gint64 uValue, guint64 uMask);
  virtual const char * FormatValue(gint64 uValue, guint64 uMask,
    int iRadix);
  virtual const char * FormatValue(gint64 uValue,
    guint64 uMask, int iRadix, const char * pHexPrefix);

  virtual char *       FormatValue(char *str, int len,
    int iRegisterSize, RegisterValue value);
//  virtual char *       FormatValueAsBinary(char *str, int len,
//    int iRegisterSize, RegisterValue value);

  virtual void SetProgramAddressRadix(int iRadix);
  virtual void SetRegisterAddressRadix(int iRadix);
  virtual void SetValueRadix(int iRadix);

  virtual void SetProgramAddressMask(unsigned int uMask);
  virtual void SetRegisterAddressMask(unsigned int uMask);
  virtual void SetValueMask(unsigned int uMask);

  virtual void SetExitOnBreak(FNNOTIFYEXITONBREAK);
  virtual void NotifyExitOnBreak(int iExitCode);

  static Integer  s_iValueRadix;
  static String   s_sValueHexPrefix;
  static Integer  s_iProgAddrRadix;
  static String   s_sProgAddrHexPrefix;
  static Integer  s_iRAMAddrRadix;
  static String   s_sRAMAddrHexPrefix;

  static Integer  s_iValueMask;
  static Integer  s_iProgAddrMask;
  static Integer  s_iRAMAddrMask;

protected:
  string        m_sLabeledAddr;
  string        m_sFormatValueGint64;

  const char ** m_paStrings;
//  CGpsimConsole m_Console;

};

Integer CGpsimUserInterface::s_iValueRadix(       "UIValueRadix",             IUserInterface::eHex);
String  CGpsimUserInterface::s_sValueHexPrefix(   "UIValueHexPrefix",         "$");
Integer CGpsimUserInterface::s_iProgAddrRadix(    "UIProgamAddressRadix",     IUserInterface::eHex);
String  CGpsimUserInterface::s_sProgAddrHexPrefix("UIProgamAddressHexPrefix", "$");
Integer CGpsimUserInterface::s_iRAMAddrRadix(     "UIRAMAddressRadix",        IUserInterface::eHex);
String  CGpsimUserInterface::s_sRAMAddrHexPrefix( "UIRAMAddressHexPrefix",    "$");

Integer CGpsimUserInterface::s_iValueMask(        "UIValueMask",             0xff);
Integer CGpsimUserInterface::s_iProgAddrMask(     "UIProgamAddressMask",     0xff);
Integer CGpsimUserInterface::s_iRAMAddrMask(      "UIRAMAddressMask",        0xff);


class NullConsole : public ISimConsole {
public:
  virtual void Printf(const char *fmt, ...) {}
  virtual void VPrintf(const char *fmt, va_list argptr) {}
  virtual void Puts(const char*) {}
  virtual void Putc(const char) {}
  virtual char* Gets(char *, int) { return "";}
};

NullConsole g_NullConsole;
static ISimConsole    *g_pConsole = &g_NullConsole;

class NullUserInterface : public IUserInterface {
//  NullConsole m_Console;
public:
  virtual ISimConsole &GetConsole() { return *g_pConsole; }
  virtual void SetConsole(ISimConsole *pConsole) { g_pConsole = pConsole; }
  virtual void DisplayMessage(unsigned int uStringID, ...) {}
  virtual void DisplayMessage(FILE * pOut, unsigned int uStringID, ...) {}
  virtual void DisplayMessage(const char *fmt, ...) {}
  virtual void DisplayMessage(FILE * pOut, const char *fmt, ...) {}

  virtual const char * FormatProgramAddress(unsigned int uAddress,
    unsigned int uMask) {return "";}
  virtual const char * FormatProgramAddress(unsigned int uAddress,
    unsigned int uMask, int iRadix) {return "";}
  virtual const char * FormatRegisterAddress(Register *)
  { return ""; }
  virtual const char * FormatRegisterAddress(unsigned int uAddress,
    unsigned int uMask) {return "";}
  virtual const char * FormatLabeledValue(const char * pLabel,
    unsigned int uValue) {return "";}
  virtual const char * FormatValue(unsigned int uValue) {return "";}
  virtual const char * FormatValue(gint64 uValue) {return "";}
  virtual const char * FormatValue(gint64 uValue, guint64 uMask) {return "";}
  virtual const char * FormatValue(gint64 uValue, guint64 uMask,
    int iRadix) {return "";}

  virtual char *       FormatValue(char *str, int len,
    int iRegisterSize, RegisterValue value) {return "";}

  virtual void SetProgramAddressRadix(int iRadix) {}
  virtual void SetRegisterAddressRadix(int iRadix) {}
  virtual void SetValueRadix(int iRadix) {}

  virtual void SetProgramAddressMask(unsigned int uMask) {}
  virtual void SetRegisterAddressMask(unsigned int uMask) {}
  virtual void SetValueMask(unsigned int uMask) {}

  virtual void SetExitOnBreak(FNNOTIFYEXITONBREAK) {}
  virtual void NotifyExitOnBreak(int iExitCode) {}
};

CGpsimUserInterface g_DefaultUI(s_psEnglishMessages);
static IUserInterface *g_GpsimUI = &g_DefaultUI;

LIBGPSIM_EXPORT  IUserInterface & GetUserInterface(void) {
  return *g_GpsimUI;
}

LIBGPSIM_EXPORT  void SetUserInterface(IUserInterface * pGpsimUI) {
  if(pGpsimUI) {
    g_GpsimUI = pGpsimUI;
  }
  else {
    g_GpsimUI = &g_DefaultUI;
  }
}


static streambuf *s_pSavedCout = 0;
LIBGPSIM_EXPORT  void SetUserInterface(std::streambuf * pOutStreamBuf) {
  if(pOutStreamBuf == NULL && s_pSavedCout != 0) {
    cout.rdbuf(s_pSavedCout);
    s_pSavedCout = 0;
  }
  else {
    s_pSavedCout = cout.rdbuf(pOutStreamBuf);;
  }
}

///
///   CGpsimUserInterface
///
CGpsimUserInterface::CGpsimUserInterface(const char *paStrings[]) {
  m_paStrings = paStrings;
  m_pfnNotifyOnExit = NULL;
}

void CGpsimUserInterface::SetStreams(FILE *in, FILE *out) {
//  m_Console.SetOut(out);
//  m_Console.SetIn(in);
}

ISimConsole &CGpsimUserInterface::GetConsole(void) {
  return *g_pConsole;
}

void CGpsimUserInterface::SetConsole(ISimConsole *pConsole) {
  g_pConsole = pConsole;
}

void CGpsimUserInterface::DisplayMessage(unsigned int uStringID, ...) {
  va_list ap;
  va_start(ap,uStringID);
  g_pConsole->VPrintf(m_paStrings[uStringID], ap);
  va_end(ap);
}

void CGpsimUserInterface::DisplayMessage(FILE * pOut, unsigned int uStringID, ...) {
  va_list ap;
  va_start(ap,uStringID);
  if (pOut == NULL || pOut == stdout) {
    g_pConsole->VPrintf(m_paStrings[uStringID], ap);
  }
  else {
    vfprintf(pOut, m_paStrings[uStringID], ap);
  }   
  va_end(ap);
}

void CGpsimUserInterface::DisplayMessage(const char *fmt, ...) {
  va_list ap;
  va_start(ap,fmt);
  g_pConsole->VPrintf(fmt, ap);
  va_end(ap);
}

void CGpsimUserInterface::DisplayMessage(FILE * pOut, const char *fmt, ...) {
  va_list ap;
  va_start(ap,fmt);
  if (pOut == NULL || pOut == stdout) {
    g_pConsole->VPrintf(fmt, ap);
  }
  else {
    vfprintf(pOut, fmt, ap);
  }   
  va_end(ap);
}

void CGpsimUserInterface::SetProgramAddressRadix(int iRadix) {
  s_iProgAddrRadix = iRadix;
}

void CGpsimUserInterface::SetRegisterAddressRadix(int iRadix) {
  s_iRAMAddrRadix = iRadix;
}

void CGpsimUserInterface::SetValueRadix(int iRadix) {
  s_iValueRadix = iRadix;
}

void CGpsimUserInterface::SetProgramAddressMask(unsigned int uMask) {
  s_iProgAddrMask = uMask;
}

void CGpsimUserInterface::SetRegisterAddressMask(unsigned int uMask) {
  s_iRAMAddrMask = uMask;
}

void CGpsimUserInterface::SetValueMask(unsigned int uMask) {
  s_iValueMask = uMask;
}

void CGpsimUserInterface::SetExitOnBreak(FNNOTIFYEXITONBREAK pFunc) {
  m_pfnNotifyOnExit = pFunc;
}

void CGpsimUserInterface::NotifyExitOnBreak(int iExitCode) {
  if(m_pfnNotifyOnExit != NULL) {
    m_pfnNotifyOnExit(iExitCode);
  }
}


const char * CGpsimUserInterface::FormatProgramAddress(unsigned int uAddress,
    unsigned int uMask) {
  const char * pLabel = get_symbol_table().findProgramAddressLabel(uAddress);
  return FormatLabeledValue(pLabel, uAddress, uMask,
    s_iProgAddrRadix, s_sProgAddrHexPrefix);
}

const char * CGpsimUserInterface::FormatProgramAddress(unsigned int uAddress,
    unsigned int uMask, int iRadix) {
  return FormatValue((gint64)uAddress, uMask, iRadix, s_sProgAddrHexPrefix);
}

const char * CGpsimUserInterface::FormatRegisterAddress(Register *pReg)
{
  if (!pReg)
    return "";

  return FormatLabeledValue(pReg->name().c_str(),
			    pReg->address,
			    s_iRAMAddrMask, s_iRAMAddrRadix, s_sRAMAddrHexPrefix);
}

const char * CGpsimUserInterface::FormatRegisterAddress(unsigned int uAddress,
                                                        unsigned int uMask) {
  register_symbol * pRegSym = get_symbol_table().findRegisterSymbol(uAddress, uMask);
  const char * pLabel = pRegSym == NULL ? "" : pRegSym->name().c_str();
  return FormatLabeledValue(pLabel, uAddress, s_iRAMAddrMask, s_iRAMAddrRadix, s_sRAMAddrHexPrefix);
}

const char * CGpsimUserInterface::FormatLabeledValue(const char * pLabel,
                                                     unsigned int uValue) {
  return FormatLabeledValue(pLabel, uValue, s_iValueMask, s_iValueRadix, s_sValueHexPrefix);
}

const char * CGpsimUserInterface::FormatLabeledValue(const char * pLabel,
                                                     unsigned int uValue,
                                                     unsigned int uMask,
                                                     int          iRadix,
                                                     const char * pHexPrefix) {
  m_sLabeledAddr.clear();
  const char *pValue = FormatValue(uValue, uMask, iRadix, pHexPrefix);
  if(pLabel != NULL && *pLabel != 0) {
    m_sLabeledAddr.append(pLabel);
    m_sLabeledAddr.append("(");
    m_sLabeledAddr.append(pValue);
    m_sLabeledAddr.append(")");
  }
  else {
    m_sLabeledAddr = pValue;
  }
  return m_sLabeledAddr.c_str();
}

const char * CGpsimUserInterface::FormatValue(unsigned int uValue) {
  return FormatLabeledValue(NULL, uValue, s_iValueMask, s_iValueRadix, s_sValueHexPrefix);
}

const char * CGpsimUserInterface::FormatValue(gint64 uValue) {
  return FormatValue(uValue, s_iValueMask, s_iValueRadix);
}

const char * CGpsimUserInterface::FormatValue(gint64 uValue,
    guint64 uMask) {
  return FormatValue(uValue, uMask, s_iValueRadix, s_sValueHexPrefix);
}

const char * CGpsimUserInterface::FormatValue(gint64 uValue,
    guint64 uMask, int iRadix) {
  return FormatValue(uValue, uMask, iRadix, s_sValueHexPrefix);
}

const char * CGpsimUserInterface::FormatValue(gint64 uValue,
    guint64 uMask, int iRadix, const char * pHexPrefix) {

  ostringstream osValue;
  string sPrefix;
  int iBytes = 0;
  guint64 l_uMask = uMask;
  int iDigits;
  while(l_uMask) {
    iBytes++;
    l_uMask >>= 8;
  }
  switch(iRadix) {
  case eHex:
    iDigits = iBytes * 2;
    osValue << pHexPrefix;
    osValue << hex << setw(iDigits) << setfill('0');
    break;
  case eDec:
    osValue << dec;
    break;
  case eOct:
    iDigits = iBytes * 3;
    osValue << "0";
    osValue << oct << setw(iDigits) << setfill('0');
    break;
  }
  osValue << (uValue & uMask);
  m_sFormatValueGint64 = osValue.str();
  return m_sFormatValueGint64.c_str();
}

char * CGpsimUserInterface::FormatValue(char *str, int len,
                                        int iRegisterSize,
                                        RegisterValue value)
{

  if(!str || !len)
    return 0;

  char hex2ascii[] = "0123456789ABCDEF";
  int i;
  int min = (len < iRegisterSize*2) ? len : iRegisterSize*2;

  if(value.data == INVALID_VALUE)
    value.init = 0xfffffff;

  for(i=0; i < min; i++) {
    if(value.init & 0x0f)
      str[min-i-1] = '?';
    else
      str[min-i-1] = hex2ascii[value.data & 0x0f];
    value >>= 4;
  }
  str[min] = 0;

  return str;

}



syntax highlighted by Code2HTML, v. 0.9.1