#include "Wt/WApplication"
#include "Wt/WLocale"

#include "WebUtils.h"
#include <boost/lexical_cast.hpp>

#include <cctype>

namespace Wt {

namespace {
const WLocale systemLocale;
}

WLocale::WLocale()
  : decimalPoint_("."),
    groupSeparator_(""),
    dateFormat_("yyyy-MM-dd")
{ }

WLocale::WLocale(const WLocale& other)
  : decimalPoint_(other.decimalPoint()),
    groupSeparator_(other.groupSeparator()),
    dateFormat_(other.dateFormat())
{ }

WLocale::WLocale(const std::string& name)
  : name_(name),
    decimalPoint_(systemLocale.decimalPoint()),
    groupSeparator_(systemLocale.groupSeparator()),
    dateFormat_(systemLocale.dateFormat())
{ }

WLocale::WLocale(const char *name)
  : name_(name),
    decimalPoint_(systemLocale.decimalPoint()),
    groupSeparator_(systemLocale.groupSeparator()),
    dateFormat_(systemLocale.dateFormat())
{ }

void WLocale::setDecimalPoint(WT_UCHAR c)
{
  decimalPoint_ = c;
}

void WLocale::setGroupSeparator(WT_UCHAR c)
{
  groupSeparator_ = c;
}

void WLocale::setDateFormat(const WT_USTRING& format)
{
  dateFormat_ = format;
}

const WLocale& WLocale::currentLocale()
{
  WApplication *app = WApplication::instance();

  if (app)
    return app->locale();
  else
    return systemLocale;
}

bool WLocale::isDefaultNumberLocale() const
{
  return decimalPoint_ == "." && groupSeparator_.empty();
}

double WLocale::toDouble(const WT_USTRING& value) const
{
  if (isDefaultNumberLocale())
    return boost::lexical_cast<double>(value);

  std::string v = value.toUTF8();

  if (!groupSeparator_.empty())
    Utils::replace(v, groupSeparator_, "");
  if (decimalPoint_ != ".")
    Utils::replace(v, decimalPoint_, ".");

  return boost::lexical_cast<double>(v);
}

int WLocale::toInt(const WT_USTRING& value) const
{
  if (groupSeparator_.empty())
    return boost::lexical_cast<int>(value);

  std::string v = value.toUTF8();

  Utils::replace(v, groupSeparator_, "");

  return boost::lexical_cast<int>(v);
}

WT_USTRING WLocale::toString(int value) const
{
  return integerToString(boost::lexical_cast<std::string>(value));
}

WT_USTRING WLocale::toString(unsigned value) const
{
  return integerToString(boost::lexical_cast<std::string>(value));
}

WT_USTRING WLocale::toString(::int64_t value) const
{
  return integerToString(boost::lexical_cast<std::string>(value));
}

WT_USTRING WLocale::toString(::uint64_t value) const
{
  return integerToString(boost::lexical_cast<std::string>(value));
}

WT_USTRING WLocale::integerToString(const std::string& v) const
{
  if (groupSeparator_.empty())
    return WT_USTRING::fromUTF8(v);
  else
    return WT_USTRING::fromUTF8(addGrouping(v, 0));
}

WT_USTRING WLocale::toString(double value) const
{
  std::string v = boost::lexical_cast<std::string>(value);

  if (isDefaultNumberLocale())
    return WT_USTRING::fromUTF8(v);

  std::size_t dotPos = v.find('.');

  if (dotPos == std::string::npos) {
    if (std::isdigit(v[v.length() - 1]))
      return WT_USTRING::fromUTF8(addGrouping(v, v.length()));
    else
      return WT_USTRING::fromUTF8(v); // Inf, etc...
  } else {
    v.replace(dotPos, 1, decimalPoint_); 
    return WT_USTRING::fromUTF8(addGrouping(v, dotPos));
  }
}

std::string WLocale::addGrouping(const std::string& v, unsigned decimalPoint)
  const 
{
  std::string result;
  result.reserve(v.length() * 2);

  for (unsigned i = 0; i < decimalPoint; ++i) {
    result.push_back(v[i]);
    if (i < decimalPoint - 1 && ((decimalPoint - i - 1) % 3 == 0))
      result += groupSeparator_;
  }

  result += v.substr(decimalPoint);

  return result;
}

}
