/* tempresult.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* internal holder for int/float/string                                      */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include <string.h>

#include "strutil.h"
#include "asmdef.h"
#include "asmsub.h"
#include "tempresult.h"

/*!------------------------------------------------------------------------
 * \fn     as_tempres_ini(TempResult *p_res)
 * \brief  initialize temp result buffer
 * \param  p_res buffer to initialize
 * ------------------------------------------------------------------------ */

void as_tempres_ini(TempResult *p_res)
{
  p_res->Typ = TempNone;
  p_res->Flags = eSymbolFlag_None;
  p_res->AddrSpaceMask = 0;
  p_res->DataSize = eSymbolSizeUnknown;
  p_res->Relocs = NULL;
  memset(&p_res->Contents, 0, sizeof(p_res->Contents));
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_dyn_ini(void)
 * \brief  create instance of temp result and initialize
 * \return * to result buffer or NULL
 * ------------------------------------------------------------------------ */

TempResult *as_tempres_dyn_ini(void)
{
  TempResult *p_res = (TempResult*)calloc(1, sizeof(*p_res));
  if (p_res)
    as_tempres_ini(p_res);
  return p_res;
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_free(TempResult *p_res)
 * \brief  deinit temp result buffer
 * \param  p_res buffer to deinit
 * ------------------------------------------------------------------------ */

void as_tempres_free(TempResult *p_res)
{
  if (p_res->Typ == TempString)
    as_nonz_dynstr_free(&p_res->Contents.str);
  p_res->Typ = TempNone;
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_dyn_free(TempResult *p_res)
 * \brief  deinit & free temp result buffer
 * \param  p_res buffer to deinit & free
 * ------------------------------------------------------------------------ */

void as_tempres_dyn_free(TempResult *p_res)
{
  as_tempres_free(p_res);
  free(p_res);
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_set_none(TempResult *p_res)
 * \brief  set temp result to none
 * \param  p_res result to fill
 * ------------------------------------------------------------------------ */

void as_tempres_set_none(TempResult *p_res)
{
  if (p_res->Typ == TempString)
    as_nonz_dynstr_free(&p_res->Contents.str);
  p_res->Typ = TempNone;
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_set_int(TempResult *p_res, LargeInt value)
 * \brief  set temp result to integer value
 * \param  p_res result to fill
 * \param  value integer value to set
 * ------------------------------------------------------------------------ */

void as_tempres_set_int(TempResult *p_res, LargeInt value)
{
  if (p_res->Typ == TempString)
    as_nonz_dynstr_free(&p_res->Contents.str);
  p_res->Typ = TempInt;
  p_res->Contents.Int = value;
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_set_float(TempResult *p_res, as_float_t value)
 * \brief  set temp result to float value
 * \param  p_res result to fill
 * \param  value float value to set
 * ------------------------------------------------------------------------ */

void as_tempres_set_float(TempResult *p_res, as_float_t value)
{
  if (p_res->Typ == TempString)
    as_nonz_dynstr_free(&p_res->Contents.str);
  p_res->Typ = TempFloat;
  p_res->Contents.Float = value;
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_set_str(TempResult *p_res, const as_nonz_dynstr_t *p_value)
 * \brief  set temp result to string value
 * \param  p_res result to fill
 * \param  p_value string value to set
 * ------------------------------------------------------------------------ */

void as_tempres_set_str(TempResult *p_res, const as_nonz_dynstr_t *p_value)
{
  if (p_res->Typ != TempString)
    as_nonz_dynstr_ini(&p_res->Contents.str, p_value->capacity);
  p_res->Typ = TempString;
  as_nonz_dynstr_copy(&p_res->Contents.str, p_value);
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_set_str_raw(TempResult *p_res, const char *p_src, size_t src_len)
 * \brief  set temp result to string value, with raw source
 * \param  p_res result to fill
 * \param  p_src string value to set
 * \param  src_len length of source
 * ------------------------------------------------------------------------ */

void as_tempres_set_str_raw(TempResult *p_res, const char *p_src, size_t src_len)
{
  if (p_res->Typ != TempString)
    as_nonz_dynstr_ini(&p_res->Contents.str, as_nonz_dynstr_roundup_len(src_len));
  p_res->Typ = TempString;
  as_nonz_dynstr_append_raw(&p_res->Contents.str, p_src, src_len);
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_set_c_str(TempResult *p_res, const char *p_src)
 * \brief  set temp result to string value, with C string source
 * \param  p_res result to fill
 * \param  p_src string value to set
 * ------------------------------------------------------------------------ */

void as_tempres_set_c_str(TempResult *p_res, const char *p_src)
{
  as_tempres_set_str_raw(p_res, p_src, strlen(p_src));
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_set_reg(TempResult *p_res, const tRegDescr *p_value)
 * \brief  set temp result to register value
 * \param  p_res result to fill
 * \param  p_value register value to set
 * ------------------------------------------------------------------------ */

void as_tempres_set_reg(TempResult *p_res, const tRegDescr *p_value)
{
  if (p_res->Typ == TempString)
    as_nonz_dynstr_free(&p_res->Contents.str);
  p_res->Typ = TempReg;
  p_res->Contents.RegDescr = *p_value;
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_copy(TempResult *p_dest, const TempResult *p_src)
 * \brief  copy temp result's value
 * \param  p_dest destination
 * \param  p_src source
 * ------------------------------------------------------------------------ */

void as_tempres_copy_value(TempResult *p_dest, const TempResult *p_src)
{
  switch (p_src->Typ)
  {
    case TempInt:
      as_tempres_set_int(p_dest, p_src->Contents.Int);
      break;
    case TempFloat:
      as_tempres_set_float(p_dest, p_src->Contents.Float);
      break;
    case TempString:
      as_tempres_set_str(p_dest, &p_src->Contents.str);
      break;
    case TempReg:
      as_tempres_set_reg(p_dest, &p_src->Contents.RegDescr);
      break;
    default:
      as_tempres_set_none(p_dest);
  }
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_copy(TempResult *p_dest, const TempResult *p_src)
 * \brief  copy temp result
 * \param  p_dest destination
 * \param  p_src source
 * ------------------------------------------------------------------------ */

void as_tempres_copy(TempResult *p_dest, const TempResult *p_src)
{
  as_tempres_copy_value(p_dest, p_src);
  p_dest->Flags = p_src->Flags;
  p_dest->AddrSpaceMask = p_src->AddrSpaceMask;
  p_dest->DataSize = p_src->DataSize;
  p_dest->Relocs = p_src->Relocs;
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_cmp(const TempResult *p_res1, const TempResult *p_res2)
 * \brief  compare two values
 * \param  p_res1, p_res2 values to compare
 * \return -1/0/+1 for p_res1 </=/> p_res2
 * ------------------------------------------------------------------------ */

int as_tempres_cmp(const TempResult *p_res1, const TempResult *p_res2)
{
  if (p_res1->Typ != p_res2->Typ)
    return -1;
  switch (p_res1->Typ)
  {
    case TempString:
      return as_nonz_dynstr_cmp(&p_res1->Contents.str, &p_res2->Contents.str);
    case TempFloat:
      if (p_res1->Contents.Float < p_res2->Contents.Float)
        return -1;
      else if (p_res1->Contents.Float > p_res2->Contents.Float)
        return 1;
      else
        return 0;
    case TempInt:
      if (p_res1->Contents.Int < p_res2->Contents.Int)
        return -1;
      else if (p_res1->Contents.Int > p_res2->Contents.Int)
        return 1;
      else
        return 0;
    case TempReg:
      if (p_res1->Contents.RegDescr.Reg < p_res2->Contents.RegDescr.Reg)
        return -1;
      else if (p_res1->Contents.RegDescr.Reg > p_res2->Contents.RegDescr.Reg)
        return 1;
      else
        return 0;
    default:
      return 0;
  }
}

/*!------------------------------------------------------------------------
 * \fn     TempResultToFloat(TempResult *pResult)
 * \brief  convert TempResult to float
 * \param  pResult tempresult to convert
 * \return 0 or error code
 * ------------------------------------------------------------------------ */

int TempResultToFloat(TempResult *pResult)
{
  switch (pResult->Typ)
  {
    case TempInt:
      pResult->Contents.Float =
#ifdef AS_HAS128
      /* Weird problems converting negative int128_t values
         to long double when Valgrind is active: */
                                (QuadInt)
#endif
                                pResult->Contents.Int;
      pResult->Typ = TempFloat;
      break;
    case TempFloat:
      break;
    default:
      as_tempres_set_none(pResult);
      return -1;
  }
  return 0;
}

/*!------------------------------------------------------------------------
 * \fn     as_tempres_append_dynstr(as_dynstr_t *p_dest, const TempResult *pResult, int int_radix)
 * \brief  convert result to readable form
 * \param  p_dest where to write ASCII representation
 * \param  pResult result to convert
 * \param  int_radix number system to use for integers
 * \return 0 or error code
 * ------------------------------------------------------------------------ */

int as_tempres_append_dynstr(as_dynstr_t *p_dest, const TempResult *pResult, int int_radix)
{
  switch (pResult->Typ)
  {
    case TempInt:
      /* TODO: use SystemStr */
      as_sdprcatf(p_dest, (int_radix == 16) ? "%lllx" : "%llld", pResult->Contents.Int);
      break;
    case TempFloat:
      as_sdprcatf(p_dest, "%0.16e", pResult->Contents.Float);
      KillBlanks(p_dest->p_str);
      break;
    case TempString:
    {
      char quote_chr = (pResult->Flags & eSymbolFlag_StringSingleQuoted) ? '\'' : '"';
      const char *p_run, *p_end;

      as_sdprcatf(p_dest, "%c", quote_chr);
      for (p_run = pResult->Contents.str.p_str, p_end = p_run + pResult->Contents.str.len;
           p_run < p_end; p_run++)
        if ((*p_run == '\\') || (*p_run == quote_chr))
          as_sdprcatf(p_dest, "\\%c", *p_run);
        else if (!isprint(*p_run))
          as_sdprcatf(p_dest, "\\%03d", *p_run);
        else
          as_sdprcatf(p_dest, "%c", *p_run);

      as_sdprcatf(p_dest, "%c", quote_chr);
      break;
    }
    default:
      return -1;
  }
  return 0;
}
