// src/backend/string_utils.cc - Copyright 2005, 2006, University
//               of Padova, dept. of Pure and Applied
//               Mathematics
//
// This file is part of SGPEMv2.
//
// This is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// SGPEMv2 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with SGPEMv2; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#include "gettext.h"

#include <sgpemv2/string_utils.hh>

#include <sstream>

using namespace std;
//using namespace sgpem;
using Glib::ustring;

namespace sgpem
{

  /* Force template instantiation to allow visibility outside this DSO */
  template SG_DLLEXPORT int string_to<int>(const Glib::ustring&) throw(domain_error);
  template SG_DLLEXPORT float string_to<float>(const Glib::ustring&) throw(domain_error);
  template SG_DLLEXPORT double string_to<double>(const Glib::ustring&) throw(domain_error);

  template SG_DLLEXPORT void to_string<int>(const int val, Glib::ustring&);
  template SG_DLLEXPORT void to_string<float>(const float val, Glib::ustring&);
  template SG_DLLEXPORT void to_string<double>(const double val, Glib::ustring&);
  
  // Specialized further down in this file:
  // template SG_DLLEXPORT bool string_to<bool>(const Glib::ustring&);


  template <typename T>
  T
  string_to(const ustring& str) throw(domain_error)
  {
    Tokens tokens = tokenize(str);

    if (tokens.size() != 1)
      throw domain_error(_("too few or too many tokens"));

    // use the token, not the string, so whitespaces at the end of the string are
    // not considered errors by the last test
    istringstream iss(tokens[0]);
    T result;
    
    iss.exceptions(ios_base::failbit | ios_base::badbit);

    try
    {
      iss >> result;
    }
    catch (ios_base::failure e)
    {
      throw domain_error(e.what());
    }

    // disable exceptions, otherwise peek() will throw them on eof!
    iss.exceptions(ios_base::goodbit);

    // check if there is still stuff in the stream, there shouldn't
    if (iss.peek() != istringstream::traits_type::eof())
      throw domain_error(_("incorrect number format"));

    return result;
  }


  template <>
  SG_DLLEXPORT bool 
  string_to<bool>(const Glib::ustring& str) throw(domain_error)
  {
    Tokens tokens = tokenize(str);

    if (tokens.size() != 1)
      throw domain_error(_("too few or too many tokens"));

    // use the token, not the string, so whitespaces at the end of the string are
    // not considered errors by the last test
    istringstream iss(tokens[0]);
    bool value;
    
    iss.exceptions(ios_base::failbit | ios_base::badbit);

    try
    {
      iss >> boolalpha >> value;
    }
    catch (ios_base::failure e)
    {
      throw domain_error(e.what());
    }

    // disable exceptions, otherwise peek() will throw them on eof!
    iss.exceptions(ios_base::goodbit);

    // check if there is still stuff in the stream, there shouldn't
    if (iss.peek() != istringstream::traits_type::eof())
      throw domain_error(_("incorrect boolean"));

    return value;
  }

  /**
   \brief A function that converts an integer value to an Unicode string
   */
  template<typename T>
  void
  to_string(const T val, Glib::ustring& str)
  {
    stringstream ss;
    ss << val;
    ss >> str;
  }


  // helper function for tokenize()
  static void
  add_token(Tokens& tokens, const ustring& token)
  {
    if (token.size() > 0)
      tokens.push_back(token);
  }

  Tokens
  tokenize(const ustring& str)
  {
    istringstream iss(str);
    Tokens tokens;

    while (iss)
    {
      ustring token;
      iss >> token;

      ustring::size_type index;

      // for the SET command, parse the assigment symbol as
      // a separate token
      do
      {
        index = token.find('=');

        if (index != ustring::npos)
        {
          add_token(tokens, token.substr(0, index));
          add_token(tokens, "=");

          // the second argument can safely be out of range
          token = token.substr(index + 1, token.size());
        }
      }
      while (index != ustring::npos);

      add_token(tokens, token);
    }

    return tokens;
  }

}
