/**
 * \file Constants.hpp
 * \brief Header for GeographicLib::Constants class
 *
 * Copyright (c) Charles Karney (2008-2024) <karney@alum.mit.edu> and licensed
 * under the MIT/X11 License.  For more information, see
 * https://geographiclib.sourceforge.io/
 **********************************************************************/

#if !defined(GEOGRAPHICLIB_CONSTANTS_HPP)
#define GEOGRAPHICLIB_CONSTANTS_HPP 1

#include <GeographicLib/Config.h>

/**
 * @relates GeographicLib::Constants
 * Pack the version components into a single integer.  Users should not rely on
 * this particular packing of the components of the version number; see the
 * documentation for GEOGRAPHICLIB_VERSION, below.
 **********************************************************************/
#define GEOGRAPHICLIB_VERSION_NUM(a,b,c) ((((a) * 10000 + (b)) * 100) + (c))

/**
 * @relates GeographicLib::Constants
 * The version of GeographicLib as a single integer, packed as MMmmmmpp where
 * MM is the major version, mmmm is the minor version, and pp is the patch
 * level.  Users should not rely on this particular packing of the components
 * of the version number.  Instead they should use a test such as \code
   #if GEOGRAPHICLIB_VERSION >= GEOGRAPHICLIB_VERSION_NUM(1,37,0)
   ...
   #endif
 * \endcode
 **********************************************************************/
#define GEOGRAPHICLIB_VERSION \
 GEOGRAPHICLIB_VERSION_NUM(GEOGRAPHICLIB_VERSION_MAJOR, \
                           GEOGRAPHICLIB_VERSION_MINOR, \
                           GEOGRAPHICLIB_VERSION_PATCH)

// For reference, here is a table of Visual Studio and _MSC_VER
// correspondences:
//
// _MSC_VER  Visual Studio
//   1900      vc14  (2015) First version of VS to include enough C++11 support
//   191[0-6]  vc15  (2017) First version of VS to include enough C++17 support
//   192[0-9]  vc16  (2019)
//   1930-1944 vc17  (2022)
//             vc18  (2026)

#if defined(_MSC_VER) && defined(GEOGRAPHICLIB_SHARED_LIB) && \
  GEOGRAPHICLIB_SHARED_LIB
#  if GEOGRAPHICLIB_SHARED_LIB > 1
#    error GEOGRAPHICLIB_SHARED_LIB must be 0 or 1
#  elif defined(GeographicLib_SHARED_EXPORTS)
#    define GEOGRAPHICLIB_EXPORT __declspec(dllexport)
#  else
#    define GEOGRAPHICLIB_EXPORT __declspec(dllimport)
#  endif
#else
#  define GEOGRAPHICLIB_EXPORT
#endif

#include <stdexcept>
#include <string>
#include <GeographicLib/Math.hpp>

/**
 * \brief Namespace for %GeographicLib
 *
 * All of %GeographicLib is defined within the GeographicLib namespace.  In
 * addition all the header files are included via GeographicLib/Class.hpp.
 * This minimizes the likelihood of conflicts with other packages.
 **********************************************************************/
namespace GeographicLib {

  /**
   * \brief %Constants needed by %GeographicLib
   *
   * Define constants specifying the WGS84 ellipsoid, the UTM and UPS
   * projections, and various unit conversions.
   *
   * Example of use:
   * \include example-Constants.cpp
   **********************************************************************/
  class GEOGRAPHICLIB_EXPORT Constants {
  private:
    typedef Math::real real;
    Constants() = delete;       // Disable constructor

  public:
    /**
     * A synonym for Math::degree<real>().
     **********************************************************************/
    static Math::real degree() { return Math::degree(); }
    /**
     * @return the number of radians in an arcminute.
     **********************************************************************/
    static Math::real arcminute()
    { return Math::degree() / Math::dm; }
    /**
     * @return the number of radians in an arcsecond.
     **********************************************************************/
    static Math::real arcsecond()
    { return Math::degree() / Math::ds; }

    /** \name Ellipsoid parameters
     **********************************************************************/
    ///@{
    /**
     * @tparam T the type of the returned value.
     * @return the equatorial radius of WGS84 ellipsoid (6378137 m).
     **********************************************************************/
    template<typename T = real> static T WGS84_a()
    { return 6378137 * meter<T>(); }
    /**
     * @tparam T the type of the returned value.
     * @return the flattening of WGS84 ellipsoid (1/298.257223563).
     **********************************************************************/
    template<typename T = real> static T WGS84_f() {
      // Evaluating this as 1000000000 / T(298257223563LL) reduces the
      // round-off error by about 10%.  However, expressing the flattening as
      // 1/298.257223563 is well ingrained.
      return 1 / ( T(298257223563LL) / 1000000000 );
    }
    /**
     * @tparam T the type of the returned value.
     * @return the gravitational constant of the WGS84 ellipsoid, \e GM, in
     *   m<sup>3</sup> s<sup>&minus;2</sup>.
     **********************************************************************/
    template<typename T = real> static T WGS84_GM()
    { return T(3986004) * 100000000 + 41800000; }
    /**
     * @tparam T the type of the returned value.
     * @return the angular velocity of the WGS84 ellipsoid, &omega;, in rad
     *   s<sup>&minus;1</sup>.
     **********************************************************************/
    template<typename T = real> static T WGS84_omega()
    { return 7292115 / (T(1000000) * 100000); }
    /**
     * @tparam T the type of the returned value.
     * @return the equatorial radius of GRS80 ellipsoid, \e a, in m.
     **********************************************************************/
    template<typename T = real> static T GRS80_a()
    { return 6378137 * meter<T>(); }
    /**
     * @tparam T the type of the returned value.
     * @return the gravitational constant of the GRS80 ellipsoid, \e GM, in
     *   m<sup>3</sup> s<sup>&minus;2</sup>.
     **********************************************************************/
    template<typename T = real> static T GRS80_GM()
    { return T(3986005) * 100000000; }
    /**
     * @tparam T the type of the returned value.
     * @return the angular velocity of the GRS80 ellipsoid, &omega;, in rad
     *   s<sup>&minus;1</sup>.
     *
     * This is about 2 &pi; 366.25 / (365.25 &times; 24 &times; 3600) rad
     * s<sup>&minus;1</sup>.  365.25 is the number of days in a Julian year and
     * 365.35/366.25 converts from solar days to sidereal days.  Using the
     * number of days in a Gregorian year (365.2425) results in a worse
     * approximation (because the Gregorian year includes the precession of the
     * earth's axis).
     **********************************************************************/
    template<typename T = real> static T GRS80_omega()
    { return 7292115 / (T(1000000) * 100000); }
    /**
     * @tparam T the type of the returned value.
     * @return the dynamical form factor of the GRS80 ellipsoid,
     *   <i>J</i><sub>2</sub>.
     **********************************************************************/
    template<typename T = real> static T GRS80_J2()
    { return T(108263) / 100000000; }
    /**
     * @tparam T the type of the returned value.
     * @return the central scale factor for UTM (0.9996).
     **********************************************************************/
    template<typename T = real> static T UTM_k0()
    {return T(9996) / 10000; }
    /**
     * @tparam T the type of the returned value.
     * @return the central scale factor for UPS (0.994).
     **********************************************************************/
    template<typename T = real> static T UPS_k0()
    { return T(994) / 1000; }
    ///@}

    /** \name Triaxial ellipsoid parameters
     *
     * These parameters are close to the values given by Milan Bursa, Vladimira
     * Fialova, "Parameters of the Earth's tri-axial level ellipsoid", Studia
     * Geophysica et Geodaetica 37(1), 1-13 (1993).
     * - longitude of major axis = &minus;14.93&deg; &plusmn; 0.05&deg;
     * - \e a = 6378171.36 m &plusmn; 0.30 m
     * - \e a / (\e a &minus; \e c) = 297.7738 &plusmn; 0.0003
     * - \e a / (\e a &minus; \e b) = 91449 &plusmn; 60
     * .
     * which gives: \e a = 6378171.36 m, \e b = 6378101.61 m, \e c = 6356751.84
     * m.  Here take the semiaxes to be whole numbers of meters, with (\e a +
     * \e b)/2 = WGS84_a(), \e a &minus; \e b = 70 m, \e c = round(WGS84_a() *
     * (1 - WGS84_f())).  This gives
     * - \e a = 6378172 m
     * - \e b = 6378102 m
     * - \e c = 6356752 m
     * - \e lon0 = &minus;14.93&deg;
     **********************************************************************/
    ///@{
    /**
     * @tparam T the type of the returned value.
     * @return the major semiaxis of a triaxial approximation to the Earth, \e
     *   a, in m (= 6378172).
     **********************************************************************/
    template<typename T = real> static T Triaxial_Earth_a()
    { return WGS84_a<T>() + 70/real(2); }
    /**
     * @tparam T the type of the returned value.
     * @return the median semiaxis of a triaxial approximation to the Earth, \e
     *   b, in m (= 6378102).
     **********************************************************************/
    template<typename T = real> static T Triaxial_Earth_b()
    { return WGS84_a<T>() - 70/real(2); }
    /**
     * @tparam T the type of the returned value.
     * @return the minor semiaxis of a triaxial approximation to the Earth, \e
     *   c, in m (= 6356752).
     **********************************************************************/
    template<typename T = real> static T Triaxial_Earth_c()
    { using std::round; return round(WGS84_a<T>() * (1 - WGS84_f())); }
    /**
     * @tparam T the type of the returned value.
     * @return the longitude, with respect to Greenwich, of the major semiaxis
     *   of of a triaxial approximation to the Earth, \e lon0, in degrees (=
     *   &minus;14.93).
     **********************************************************************/
    template<typename T = real> static T Triaxial_Earth_lon0()
    { return T(1493) / 100; }
    ///@}

    /** \name SI units
     **********************************************************************/
    ///@{
    /**
     * @tparam T the type of the returned value.
     * @return the number of meters in a meter.
     *
     * This is unity, but this lets the internal system of units be changed if
     * necessary.
     **********************************************************************/
    template<typename T = real> static T meter() { return T(1); }
    /**
     * @return the number of meters in a kilometer.
     **********************************************************************/
    static Math::real kilometer()
    { return 1000 * meter<real>(); }
    /**
     * @return the number of meters in a nautical mile (approximately 1 arc
     *   minute)
     **********************************************************************/
    static Math::real nauticalmile()
    { return 1852 * meter<real>(); }

    /**
     * @tparam T the type of the returned value.
     * @return the number of square meters in a square meter.
     *
     * This is unity, but this lets the internal system of units be changed if
     * necessary.
     **********************************************************************/
    template<typename T = real> static T square_meter()
    { return meter<T>() * meter<T>(); }
    /**
     * @return the number of square meters in a hectare.
     **********************************************************************/
    static Math::real hectare()
    { return 10000 * square_meter<real>(); }
    /**
     * @return the number of square meters in a square kilometer.
     **********************************************************************/
    static Math::real square_kilometer()
    { return kilometer() * kilometer(); }
    /**
     * @return the number of square meters in a square nautical mile.
     **********************************************************************/
    static Math::real square_nauticalmile()
    { return nauticalmile() * nauticalmile(); }
    ///@}

    /** \name Anachronistic British units
     **********************************************************************/
    ///@{
    /**
     * @return the number of meters in an international foot.
     **********************************************************************/
    static Math::real foot()
    { return real(254 * 12) / 10000 * meter<real>(); }
    /**
     * @return the number of meters in a yard.
     **********************************************************************/
    static Math::real yard() { return 3 * foot(); }
    /**
     * @return the number of meters in a fathom.
     **********************************************************************/
    static Math::real fathom() { return 2 * yard(); }
    /**
     * @return the number of meters in a chain.
     **********************************************************************/
    static Math::real chain() { return 22 * yard(); }
    /**
     * @return the number of meters in a furlong.
     **********************************************************************/
    static Math::real furlong() { return 10 * chain(); }
    /**
     * @return the number of meters in a statute mile.
     **********************************************************************/
    static Math::real mile() { return 8 * furlong(); }
    /**
     * @return the number of square meters in an acre.
     **********************************************************************/
    static Math::real acre() { return chain() * furlong(); }
    /**
     * @return the number of square meters in a square statute mile.
     **********************************************************************/
    static Math::real square_mile() { return mile() * mile(); }
    ///@}

    /** \name Anachronistic US units
     **********************************************************************/
    ///@{
    /**
     * @return the number of meters in a US survey foot.
     **********************************************************************/
    static Math::real surveyfoot()
    { return real(1200) / 3937 * meter<real>(); }
    ///@}
  };

  /**
   * \brief Exception handling for %GeographicLib
   *
   * A class to handle exceptions.  It's derived from std::runtime_error so it
   * can be caught by the usual catch clauses.
   *
   * Example of use:
   * \include example-GeographicErr.cpp
   **********************************************************************/
  class GeographicErr : public std::runtime_error {
  public:

    /**
     * Constructor
     *
     * @param[in] msg a string message, which is accessible in the catch
     *   clause via what().
     **********************************************************************/
    GeographicErr(const std::string& msg) : std::runtime_error(msg) {}
  };

} // namespace GeographicLib

#endif  // GEOGRAPHICLIB_CONSTANTS_HPP
