/* -*- 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_TOOLS_HH
#define __MIA_TOOLS_HH 1

#include <limits>
#include <string>
#include <sstream>
#include <iostream>
#include <vector>
#include <cmath>
#include <stdexcept>
#include <mia/core/defines.hh>


NS_MIA_BEGIN

/** 
    \ingroup misc
    \brief A Scope based helper class to save and restore the current working directory 
    
    A helper class that stores the current working directory on construction
    and goes back to the it when the class intance is destroyed. 
*/
class EXPORT_CORE CCWDSaver {
	char *cwd;
public:
	CCWDSaver();
	~CCWDSaver();
};

#if 0
/**
   \ingroup misc
   \brief a functor to search  for files 
*/
class  FSearchFiles  {
	std::list<std::string>& result;
	const std::string pattern;
public:
	/** constructor of functor
	    \param __result takes an (empty) list to which the found files will be stored
	    \param __pattern holds the search pattern
	*/
	FSearchFiles(std::list<std::string>& __result, const std::string& __pattern);

	/** search the given \a path with the stored search pattern and add the found files to result */
	void operator()(const std::string& path);
};
#endif


#ifndef _GNU_SOURCE
/**
   \ingroup misc
   Provide sincosf conveniance functions for sin and cos if the GNU GCC extension is not available. 
 */
void EXPORT_CORE sincosf(float x, float *sin, float *cos); 

/**
   \ingroup misc
   Provide sincosf conveniance functions for sin and cos if the GNU GCC extension is not available. 
 */
void EXPORT_CORE sincos(double x, double *sin, double *cos); 
#endif

/**
   @cond INTERNAL 
   \ingroup traits 
   \brief trait to handle rounding of a double to different output types 
*/ 

template <typename T, bool is_float> 
struct __round {

	static T apply(double x) {
		return x; 
	}
}; 

template <typename T> 
struct __round<T, false> {
	static T apply(double x) {
		return static_cast<T>(rint(x));
	}
};

/// @endcond 

/**
   \ingroup misc
   
   A simple class to round floating point numbers onyl if necessary. 
   If the target is a floating point values then the result is just passed through, 
   otherwise rint is used for rounding 
   \param x 
   \returns rounded value or x
*/
template <typename T> 
T mia_round(double x) 
{
	const bool is_floating_point = std::is_floating_point<T>::value; 
	return __round<T, is_floating_point>::apply(x); 
}

/**
   @cond INTERNAL 
   \ingroup traits 
   \brief trait to handle rounding of a double to different output types with clamping to the output range 
*/ 

template <typename T, bool is_float> 
struct __round_clamped {

	static T apply(double x) {
		return x; 
	}
}; 

template <> 
struct __round_clamped<float, true> {

	static float apply(double x) {
		double y =  x < std::numeric_limits<float>::max() ? 
			( x > -std::numeric_limits<float>::max() ? x : -std::numeric_limits<float>::max()) : 
			std::numeric_limits<float>::max(); 
		return static_cast<float>(y); 
	}
}; 

template <> 
struct __round_clamped<bool, false> {
	static float apply(double x) {
		return x > 0.5; 
	}
}; 


template <typename T> 
struct __round_clamped<T, false> {
	static T apply(double x) {
		const double y = rint(x); 
		const double yy = y < std::numeric_limits<T>::max() ? 
			( y > std::numeric_limits<T>::min() ? y : std::numeric_limits<T>::min()) :
			std::numeric_limits<T>::max(); 
		return static_cast<T>(yy);
	}
};

/// @endcond 

/**
   \ingroup misc
   
   A simple class to round floating point numbers onyl if necessary. 
   If the target is a floating point values then the result is just passed through, 
   otherwise rint is used for rounding 
   \param x 
   \returns rounded value or x
*/
template <typename T> 
T mia_round_clamped(double x) 
{
	const bool is_floating_point = std::is_floating_point<T>::value; 
	return __round_clamped<T, is_floating_point>::apply(x); 
}


inline void eat_char( std::istream& is, char expect_val, const char *message) 
{
	char c; 
	is >> c; 
	if ( c != expect_val)
		throw std::runtime_error(message); 
}

NS_MIA_END

#endif
