/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef mia_core_tools_hh
#define mia_core_tools_hh

#include <sstream>
#include <string>
#include <boost/call_traits.hpp>
#include <memory>
#include <cmath>
#include <mia/core/defines.hh>
#include <mia/core/errormacro.hh>

NS_MIA_BEGIN

/**
   \ingroup misc
   \brief A helper class to make it possible to store a non-pointer object or a pointer that must not be freed
   in a shared pointer
   \tparam the type type be stored
*/

template <typename T>
struct void_destructor {
       virtual void operator () (T *)
       {
       }
};


/**
   \brief functor to wrap statically allocated data a shared pointer representation

   This functor wraps a statically allocated data into a std::shared_ptr.
   The data must not be constant and it well not be freed when the shared_ptr is destoyed.
   The functor is designed to be usable by the std::transform function.

   \tparam Data the data type to be wrapped into the shared pointer;
*/
template <typename Data>
class FWrapStaticDataInSharedPointer
{
public:
       typedef std::shared_ptr<Data> PData;
       PData operator () (Data& d) const
       {
              return PData(&d, void_destructor<Data>());
       }
};


/**
   \ingroup misc
   Function to convert a streamable type from a string to a value.
   The string may contain whitespaces before and after the value but no other characters.
   \tparam T some type that supports the >> stream operator
   \param s the c-string holding the value
   \param [out] result value of T corresponding to s
   \returns true if s could be parsed sucessfully, and false if not
 */

template <typename T>
bool from_string(const char *s, T& result)
{
       std::istringstream sx(s);
       sx >> result;

       if (sx.fail())
              return false;

       if (sx.eof())
              return true;

       std::string remaining;
       sx >> remaining;
       bool retval = true;

       for (auto i = remaining.begin(); i != remaining.end(); ++i)
              retval &= isspace(remaining[0]);

       return retval;
}

/**
   \ingroup misc
   Function to convert a streamable type from a string to a value.
   The string may contain whitespaces before and after the value but no other characters.
   \tparam T some type that supports the >> stream operator
   \param s the c-string holding the value
   \param [out] result value of T corresponding to s
   \returns true if s could be parsed sucessfully, and false if not
 */

template <typename T>
bool from_string(const std::string& s, T& result)
{
       return from_string(s.c_str(), result);
}


/**
   \ingroup misc
   Function to convert a streamable type from to a string
   \tparam T some type that supports the << stream operator
   \param v the value to be converted
   \returns string that corresponds to v
 */

template <typename T>
const std::string  to_string(typename boost::call_traits<T>::param_type v)
{
       std::stringstream result;
       result << v;
       return result.str();
}


/*
  The special handling of floating point values is needed,
  because c++ writes out inf/-inf for infinity, but XML
  expects uppercase INF/-INF.
*/
template <typename T>
const std::string  to_string_fp(T v)
{
       std::stringstream result;
       int inf = std::isinf(v);

       if (!inf) {
              result << v;
       } else {
              if (inf < 0)
                     result << "-";

              result << "INF";
       }

       return result.str();
}

template <>
inline const std::string  to_string<float>(boost::call_traits<float>::param_type v)
{
       return to_string_fp(v);
}

template <>
inline const std::string  to_string<double>(boost::call_traits<double>::param_type v)
{
       return to_string_fp(v);
}


NS_MIA_END

#endif
