#include <Common.h>
#include <System/SysAll.h>
#include <UI/UIAll.h>
#include "MathLib.h"
#include "ConvDblToStr.h"

static char ThousSepIntPart  = '\'';
static char ThousSepFracPart = '\'';

/* prototypes for internal functions. */
static int  ConvDblToStr_f(double, char*, int);
static int  ConvDblToStr_e(double, char*, int);
static int  ConvDblToStr_i(double, char*);
static void AddThousandSeparator(char* s);


/* function definitions. */
void SetThausandSeparator(char s1, char s2)
{
  ThousSepIntPart  = s1;
  ThousSepFracPart = s2;
}

int ConvertDblToStr(double x, char* s, int format, int digit)
{
  int ret;
  char s1, s2;

  if (isnan(x)) {
    StrCopy(s, "[NaN]");
    return 1;
  }
  if (isinf(x) == +1) {
    StrCopy(s, "[+INF]");
    return 1;
  }
  if (isinf(x) == -1) {
    StrCopy(s, "[-INF]");
    return 1;
  }

  s1 = ThousSepIntPart;
  s2 = ThousSepFracPart;
  if (!(format & ADD_SEP_INTP))  ThousSepIntPart  = '\0';
  if (!(format & ADD_SEP_FRACP)) ThousSepFracPart = '\0';

  if (format & FIX_FORMAT) {
    ret = ConvDblToStr_f(x, s, digit);
    if (ret == 0) AddThousandSeparator(s);
  } else if (format & EXP_FORMAT) {
    ret = ConvDblToStr_e(x, s, digit);
  } else {
    ret = 1;
  }

  ThousSepIntPart  = s1;
  ThousSepFracPart = s2;
  return ret;
}
   

int ConvDblToStr_f(double x, char* s, int d)
{
  double integer, fraction;
  char* ptr = s;
  int iii;

  if (x < 0) {
    *(ptr++) = '-';
    x = fabs(x);
  }

  /* divide into int. & frac. */
  x += pow(10, -d) * 0.5;
  fraction = modf(x, &integer);

  /* integer part */
  ConvDblToStr_i(integer, ptr);
  ptr = s + StrLen(s);
  *(ptr++) = '.';

  /* fraction part */
  fraction = fraction * pow(10, d);
  iii = d - (fraction > 10 ? (int)log10(fraction) + 1 : 1);
  while (iii-- > 0) *(ptr++) = '0';

  ConvDblToStr_i(fraction, ptr);
  ptr = s + StrLen(s);
  *ptr = '\0';

  /* suppress trailing zeros. */
  while (*(ptr - 1) == '0') *(--ptr) = '\0';
  if (*(ptr - 1) == '.') *(--ptr) = '\0';

  return 0;
}


int ConvDblToStr_e(double x, char* s, int d)
{
  double mantissa, exponent;
  char* ptr = s;

  if (fabs(x) < 1e-308) {
    exponent = 0;
    mantissa = 0;
  } else if (x > 0) {
    exponent = floor(log10(x));
    mantissa = x / pow(10, exponent);
  } else {
    exponent = floor(log10(-x));
    mantissa = x / pow(10, exponent);
  }

  ConvDblToStr_f(mantissa, s, d);
  AddThousandSeparator(s);
  ptr += StrLen(s);
  *(ptr++) = 'e';

  if (exponent >= 0) *(ptr++) = '+';

  ConvDblToStr_f(exponent, ptr, 3);
  return 0;
}


int ConvDblToStr_i(double x, char* s)
{
  int lg10, i;
  unsigned long y;
  char* ptr = s;

  lg10 = (x > 9.) ? (int)log10(x) : 0;
  if (lg10 >= 8) {
    y = (long)(x / 1e8);
    ptr += StrPrintF(ptr, "%ld", y);
    x -= (double)y * 1e8;
    lg10 = ((x > 9.) ? (int)log10(x) : 0);
    if (lg10 < 7)
      for (i = 1; i <= (8 - lg10 - 1); i++) *(ptr++) = '0';
  }
  ptr += StrPrintF(ptr, "%ld", (long)x);
	
  *ptr = '\0';
  return 0;
}


static void AddThousandSeparator(char* s)
{
  char buf1[32], buf2[32];
  char* p0;
  char* p1;
  char* p2;
  int i;

  /* skip positive/negative sign. */
  if ((*s < '0') || (*s > '9')) s++;


  *buf1 = '\0';
  *buf2 = '\0';

  p0 = StrChr(s, '.');
  if (p0 == NULL) {
    p0 = s + StrLen(s);
  } else {
    /* fraction part */
    p1 = p0 + 1;
    p2 = buf2;
    i = 0;
    while (*p1 != '\0') {
      if ((i > 0) && ((i % 3) == 0) && (ThousSepFracPart != '\0'))
	*(p2++) = ThousSepFracPart;
      *(p2++) = *(p1++);
      i++;
    }
    *p2 = '\0';
  }

  /* integer part */
  p1 = p0 - 1;
  p2 = buf1;
  i = 0;
  while (p1 >= s) {
    if ((i > 0) && ((i % 3) == 0) && (ThousSepIntPart != '\0'))
      *(p2++) = ThousSepIntPart;
    *(p2++) = *(p1--);
    i++;
  }
  *p2 = '\0';

  p1 = s;
  p2 = buf1 + StrLen(buf1) - 1;
  while (p2 >= buf1) *(p1++) = *(p2--);
  *(p1++) = '.';
  p2 = buf2;
  while (*p2 != '\0') *(p1++) = *(p2++);
  *p1 = '\0';
}
