/* Blinker
   Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
   a blinkenarea.org project */

#include <math.h>
#include <sstream>
#include <string>

#include "Float.h"
#include "StringParser.h"

namespace Blinker {

/// constructor
Float::Float():
  m_float(0.0f)
{
}

/**
 * @brief parse from string format
 * @param[in] str string format
 * @return if parsing was successful
 */
bool Float::fromStr(const std::string &str)
{
  StringParser parser(str);
  float fl;

  if (!parser.floatVal(fl) ||
      !parser.isDone())
    return false;
  m_float = fl;
  return true;
}

/**
 * @brief convert to string format
 * @return string format
 */
std::string Float::toStr() const
{
  std::stringstream strm;
  /* output float manually to get floats with dot as decimal separator for
   * all locales */
  float val = m_float;
  int exp, dot_pos, e_exp, total_digits, e_scale;
  // output sign
  if (val < 0.0f) {
    strm << '-';
    val = -val;
  }
  // get exponent
  exp = floorf(log10f(val)) + 1;
  val *= pow(0.1f, exp);
  // fix val into range (1.0,0.1] in case of numerical instabilities
  if (val >= 1.0f) {
    val *= 0.1f;
    exp++;
  }
  if (val < 0.1f) {
    val *= 10.0f;
    exp--;
  }
  // scientific notation
  if (exp < -3 || exp > 5) {
    dot_pos = 1;
    e_exp = exp - 1;
  }
  // plain notation
  else {
    dot_pos = exp;
    e_exp = 0;
  }
  // digits before dot
  total_digits = 0;
  if (dot_pos > 0) {
    while (dot_pos > 0) {
      val *= 10.0f;
      unsigned int digit = (unsigned int)val;
      val -= digit;
      strm << (char)('0' + digit);
      dot_pos--;
      total_digits++;
    }
  } else {
    strm << '0';
  }
  // decimal dot
  strm << '.';
  // zeros after dot
  while (dot_pos < 0) {
    strm << '0';
    dot_pos++;
  }
  // digits after dot
  while (total_digits < 7) {
    val *= 10.0f;
    unsigned int digit = (unsigned int)val;
    val -= digit;
    strm << (char)('0' + digit);
    total_digits++;
  }
  // exponent
  if (e_exp != 0) {
    strm << 'e';
    // exponent sign
    if (e_exp < 0) {
      strm << '-';
      e_exp = -e_exp;
    }
    // exponent value
    e_scale = 1;
    while (e_scale * 10 <= e_exp)
      e_scale *= 10;
    do {
      unsigned int digit = e_exp / e_scale;
      strm << (char)('0' + digit);
      e_exp -= digit * e_scale;
      e_scale /= 10;
    } while (e_scale > 0);
  }
  return strm.str();
}

} // namespace Blinker

