#include "exports.h"
#include "program_files.h"
#include "pic-processor.h"
#include "cod.h"
#include "hexutils.h"
#include "cmd_gpsim.h"

/**
  * RegisterProgramFileType
  * Exported function for external modules to register their
  * file types.
  */

void LIBGPSIM_EXPORT RegisterProgramFileType(ProgramFileType * pPFT) {
  ProgramFileTypeList::GetList().push_back(pPFT);
}

void ProgramFileType::DisplayError(int err, const char *pProgFilename,
                                   const char *pLstFile)
{
  int iMessage;
  const char * pArg = "";
  switch(err) {
  case ERR_UNRECOGNIZED_PROCESSOR:
    iMessage = IDS_PROGRAM_FILE_PROCESSOR_NOT_KNOWN;
    break;
  case ERR_FILE_NOT_FOUND:
    iMessage = IDS_FILE_NOT_FOUND;
    pArg = pProgFilename;
    break;
  case ERR_FILE_NAME_TOO_LONG:
    iMessage = IDS_FILE_NAME_TOO_LONG;
    pArg = pProgFilename;
    break;
  case ERR_LST_FILE_NOT_FOUND:
    if(pLstFile == NULL) {
      iMessage = IDS_LIST_FILE_NOT_FOUND;
      pArg = pProgFilename;
    }
    else {
      iMessage = IDS_FILE_NOT_FOUND;
      pArg = pLstFile;
    }
    break;
  case ERR_BAD_FILE:
    iMessage = IDS_FILE_BAD_FORMAT;
    pArg = pProgFilename;
    break;
  case ERR_NO_PROCESSOR_SPECIFIED:
    iMessage = IDS_NO_PROCESSOR_SPECIFIED;
    break;
  case ERR_PROCESSOR_INIT_FAILED:
    iMessage = IDS_PROCESSOR_INIT_FAILED;
    break;
  case ERR_NEED_PROCESSOR_SPECIFIED:
    iMessage = IDS_FILE_NEED_PROCESSOR_SPECIFIED;
    break;
  default:
    iMessage = SUCCESS;
    break;
  }
  if(iMessage != SUCCESS)
    GetUserInterface().DisplayMessage(iMessage, pArg);
}

/**
  * ProgramFileTypeList
  * Singleton class to manage the many (as of now three) file types.
  */
ProgramFileTypeList * ProgramFileTypeList::s_ProgramFileTypeList =
  new ProgramFileTypeList();
// We will instantiate g_HexFileType and g_CodFileType here to be sure
// they are instantiated after s_ProgramFileTypeList. The objects will 
// move should the PIC code moved to its own external module.
static IntelHexProgramFileType g_HexFileType;
static PicCodProgramFileType g_CodFileType;

ProgramFileTypeList &ProgramFileTypeList::GetList() {
  return *s_ProgramFileTypeList;
}

ProgramFileTypeList::ProgramFileTypeList() {
  reserve(5);
}

ProgramFileTypeList::~ProgramFileTypeList() {
}

bool ProgramFileTypeList::LoadProgramFile(Processor **pProcessor,
                                          const char *pFilename,
                                          FILE *pFile, const char *pProcessorName) 
{
  iterator it;
  iterator itLast;
  iterator itEnd = end();
  int iReturn = ProgramFileType::SUCCESS;
  for(it = begin(); it != itEnd; it++) {
    itLast = it;
    fseek(pFile, 0, SEEK_SET);
    get_symbol_table().clear();
    if((iReturn = (*it)->LoadProgramFile(pProcessor, pFilename, pFile, pProcessorName))
      == ProgramFileType::SUCCESS) {
      return true;
    }
    if(IsErrorDisplayableInLoop(iReturn)) {
      (*it)->DisplayError(iReturn, pFilename, NULL);
    }
  }
  if(!IsErrorDisplayableInLoop(iReturn)) {
    (*itLast)->DisplayError(iReturn, pFilename, NULL);
  }
  return false;
}

bool ProgramFileTypeList::IsErrorDisplayableInLoop(int iError) {
  return iError != ProgramFileType::SUCCESS &&
         iError != ProgramFileType::ERR_BAD_FILE &&
         iError != ProgramFileType::ERR_NEED_PROCESSOR_SPECIFIED &&
         iError != ProgramFileType::ERR_LST_FILE_NOT_FOUND;
}

///
/// ProgramFileBuf
/// Used to wrap the FILE pointer to the program file
/// for libraries that use istreams.
/////////////////////////////////////////////////////

ProgramFileBuf::ProgramFileBuf(FILE *pFile) {
  m_pFile = pFile;
  setg(m_Buffer + 4, m_Buffer + 4, m_Buffer + 4);
}

ProgramFileBuf::int_type ProgramFileBuf::underflow( ) {
  if(gptr() < egptr()) {
    return *gptr();
  }
  int numPutback;
  numPutback = gptr() - eback();
  if (numPutback > 4) {
    numPutback = 4;
  }
  std::memcpy (m_Buffer+(4-numPutback), gptr() - numPutback, numPutback);
  int num;
  if((num = ::fread((void*)( m_Buffer + 4), 1, m_iBufferSize - 4, m_pFile)) <= 0) {
    if(errno != 0)
      printf("%s\n", strerror(errno));
    return (int_type )traits_type::eof();
  }
  setg(m_Buffer + (4 - numPutback),
       m_Buffer + 4, m_Buffer + 4 + num);
  return *gptr();
}

streamsize ProgramFileBuf::xsgetn(
  char_type *_Ptr, streamsize _Count) {
  return ::fread(_Ptr, _Count, 1, m_pFile);
}


syntax highlighted by Code2HTML, v. 0.9.1