/**
* @file perspective_def.h
* @brief Macros préprocesseur de l'API.
* @author Florian Joncour
* @date 2013-2018
* @copyright Ces sources font partie de l'interface de programmation pour la bibliothèque Perspective3D,
un outils de modélisation 3D à partir de vues orthographiques 2D.
Ces sources sont fournies à titre gracieux, l'utilisateur a la liberté de les exploiter
pour créer des applications dérivées à condition de respecter les conditions suivantes:

    1) Ces sources ne peuvent être modifiées.
    2) Le dérivé ne peut être exploité à des fins commerciaux.
    3) Aucune licence commerciale de la bibliothèque dont est tiré ce fichier ne peut être rendue publique.
    4) Toute application exploitant la présente bibliothèque doit explicitement faire mention de l'usage et du rôle de Perspective3D.

Ces conditions peuvent toutefois être modifiées avec l'accord de l'auteur.
*/

#ifndef PERSPECTIVE_DEF_H
#define PERSPECTIVE_DEF_H

#include <string>

//#include <limits>
#include <new> /* bad_alloc */
#include <cfloat>
#include <cmath>
#include <type_traits> // std::underlying_type
#include "perspective_sys.h"

#ifdef PSYS_CXX_ICC /* INTEL_COMPILER */
#include <mathimf.h>
#endif // PSYS_CXX_ICC

#ifndef PI
#define PI 3.141592653589793238462643
#endif // PI

#ifndef PIi // (1. / PI)
#define PIi 0.3183098861837907
#endif // PIi

#ifndef RAD // (180. / PI) : 360/RAD = PI2
#define RAD 57.29577951308232
#endif // RAD

#ifndef DTOR // (1. / RAD) : 360*DTOR = PI2
#define DTOR 0.017453292519943295
#endif // DTOR

#define DEG_RAD(x) (x*DTOR)
#define RAD_DEG(x) (x*RAD)

#ifndef pfloat // Type pour les nombres flottants

#if !defined(TYPE_PFLOAT_DOUBLE) && !defined(TYPE_PFLOAT_FLOAT)
#define TYPE_PFLOAT_DOUBLE
//#define TYPE_PFLOAT_FLOAT
#endif // TYPE_PFLOAT_DOUBLE || TYPE_PFLOAT_FLOAT

#ifdef TYPE_PFLOAT_DOUBLE
typedef double pfloat;
#ifndef EPSILON_FIXE
#define EPSILON_FIXE 0.0001
#endif // EPSILON_FIXE
#ifndef EPSILON_FIXE_LARGE
#define EPSILON_FIXE_LARGE 0.001
#endif // EPSILON_FIXE_LARGE
#ifndef MODULOF
#define MODULOF fmod
#endif // MODULOF
#ifndef RCARREF
#define RCARREF sqrt
#endif // RCARREF
#ifndef RCUBIQUE
#define RCUBIQUE(X) (pow(X, 1.0/3.0))
#endif // RCUBIQUE
#ifndef HYPOTF
#define HYPOTF hypot
#endif // HYPOTF
#ifndef POSITIFD
#define POSITIFD fabs
#endif // POSITIFD
#ifndef SINF
#define SINF sin
#endif // SINF
#ifndef ASINF
#define ASINF sinf
#endif // ASINF
#ifndef COSF
#define COSF cos
#endif // COSF
#ifndef ACOSF
#define ACOSF acos
#endif // ACOSF
#ifndef POWF
#define POWF pow
#endif // POWF
#ifndef TANF
#define TANF tan
#endif // TANF
#ifndef ATANF
#define ATANF atan
#endif // ATANF
#ifndef ATAN2F
#define ATAN2F atan2
#endif // ATAN2F
#ifndef FLOORF
#define FLOORF floor
#endif // FLOORF
#ifndef CEILF
#define CEILF ceil
#endif // CEILF
#ifndef PFLOAT_MAX
#define PFLOAT_MAX DBL_MAX
#endif // PFLOAT_MAX
#ifndef PFLOAT_MIN
#define PFLOAT_MIN -DBL_MAX
#endif // PFLOAT_MIN
#else
#ifdef TYPE_PFLOAT_FLOAT
typedef float pfloat;
#ifndef EPSILON_FIXE
#define EPSILON_FIXE 0.001
#endif // EPSILON_FIXE
#ifndef EPSILON_FIXE_LARGE
#define EPSILON_FIXE_LARGE 0.005
#endif // EPSILON_FIXE_LARGE
#ifndef MODULOF
#define MODULOF fmodf
#endif // MODULOF
#ifndef RCARREF
#define RCARREF sqrtf
#endif // RCARREF
#ifndef RCUBIQUE
#define RCUBIQUE(X) (powf(X, 1.0f/3.0f))
#endif // RCUBIQUE
#ifndef HYPOTF
#define HYPOTF hypotf
#endif // HYPOTF
#ifndef POSITIFD
#define POSITIFD fabsf
#endif // POSITIFD
#ifndef SINF
#define SINF sinf
#endif // SINF
#ifndef ASINF
#define ASINF asinf
#endif // ASINF
#ifndef COSF
#define COSF cosf
#endif // COSF
#ifndef ACOSF
#define ACOSF acosf
#endif // ACOSF
#ifndef POWF
#define POWF powf
#endif // POWF
#ifndef TANF
#define TANF tanf
#endif // TANF
#ifndef ATANF
#define ATANF atanf
#endif // ATANF
#ifndef ATAN2F
#define ATAN2F atan2f
#endif // ATAN2F
#ifndef FLOORF
#define FLOORF floorf
#endif // FLOORF
#ifndef CEILF
#define CEILF ceilf
#endif // CEILF
#ifndef PFLOAT_MAX
#define PFLOAT_MAX FLT_MAX
#endif // PFLOAT_MAX
#ifndef PFLOAT_MIN
#define PFLOAT_MIN -FLT_MAX
#endif // PFLOAT_MIN
#else
#error "Impossible de définir un type pour les nombres flottants !"
#endif // TYPE_PFLOAT_FLOAT
#endif // TYPE_PFLOAT_DOUBLE

#ifndef pfloatc
    #define pfloatc const pfloat
#endif // pfloatc

//#ifndef PFLOAT_MAX
//#define PFLOAT_MAX std::numeric_limits<pfloat>::max()
//#endif // PFLOAT_MAX
//#ifndef PFLOAT_MIN
//#define PFLOAT_MIN std::numeric_limits<pfloat>::min()
//#endif // PFLOAT_MIN

#ifndef POW2
    #define POW2(X) ((X)*(X))
#endif // POW2

#ifdef PSYS_WIN_MSVC // Compatibilitée MSVC
    #ifndef NAN
        static const unsigned long __nan[2] = {0xffffffff, 0x7fffffff};
        #define NAN (*(const float *) __nan)
    #endif // NAN
#endif // PSYS_WIN_MSVC

#define PNAN NAN
#endif // pfloat

#ifdef NAN
    #if defined(PSYS_CXX_ICC)
        #define PISNAN(x) (isnan(x))
    #elif defined(PSYS_CXX_MSVC)
        #define PISNAN(x) (_isnan(x))
    #else
        #define PISNAN(x) (std::isnan(x))
    #endif // PSYS_CXX_ICC
#else
    #define PISNAN(x) (x!=x)
#endif // NAN

#ifdef INFINITY
    #if defined(PSYS_CXX_ICC)
        #define PISINF(x) isinf(x)
    #elif defined(PSYS_CXX_MSVC)
        #define PISINF(x) (!_finite(x))
    #else
        //#define PISINF (isinf(x) != 0)
        #define PISINF(x) std::isinf(x)
    #endif // PSYS_CXX_ICC
#else
    #define PISINF(x) (fabs(x) > PFLOAT_MAX)
#endif // INFINITY

#define FPU_ERROR(x) (PISINF(x) || PISNAN(x))

#define IDNUL pint(-1)
#define IDERR pint(INT32_MAX) // 2147483647

#define COORD0 0.
#define COORDNUL PFLOAT_MIN
#define COORDNULP PFLOAT_MAX

#define VUE_EXTRUSION Perspective3D::vues2D_t::VUEHAUT
#define VUE_REVOLUTION Perspective3D::vues2D_t::VUECOTE

/* Couleurs pour l'affichage 3D (rvb) */
#define COULEUR_POINTS 125, 180, 220
#define COULEUR_POINTS_F 0.488f, 0.703f, 0.859f

#define COULEUR_SELECT_POINTS 28, 115, 176
#define COULEUR_SELECT_POINTS_F 0.101f, 0.449f, 0.687f

#define COULEUR_SELECT_LIGNES 70, 70, 70
#define COULEUR_SELECT_LIGNES_F 0.274f, 0.274f, 0.274f

#define COULEUR_LIGNES 130, 130, 130 /* Lignes de contour des surfaces. */
#define COULEUR_LIGNES_F 0.508f, 0.508f, 0.508f

//#define COULEUR_LIGNES 50, 50, 50 /* Lignes de contour des surfaces. */
//#define COULEUR_LIGNES_F 0.196f, 0.196f, 0.196f

#define COULEUR_ARRETES 120, 120, 120 /* Couleur des arrêtes des polygones (triangles) */
#define COULEUR_ARRETES_F 0.469f, 0.469f, 0.469f

#define COULEUR_REP_SURFACES 130, 40, 200
#define COULEUR_REP_SURFACES_F 0.508f, 0.156f, 0.781f

/* Tolérances et epsilons dynamiques */
namespace Perspective3D
{
    namespace TolerancesP3D
    {
        /*! Epsilon utilisateur pour la comparaison des coordonnées. */
        extern PSYS_TLS pfloat EpsilonCoords;
        /*! Epsilon utilisateur large pour la comparaison des coordonnées. */
        extern PSYS_TLS pfloat ToleranceCoordsLarge;
        /*! Epsilon utilisateur pour la comparaison des angles. */
        extern PSYS_TLS pfloat EpsilonAngles;
        /*! Epsilon utilisateur pour la comparaison des vecteurs normaux. */
        extern PSYS_TLS pfloat EpsilonVecteurs;
        /*! Tolérance utilisateur pour la comparaison des coordonnées (général). */
        extern PSYS_TLS pfloat ToleranceCoords;
        /*! Tolérance utilisateur pour la comparaison des coordonnées (axe X). */
        extern PSYS_TLS pfloat ToleranceCoords_X;
        /*! Tolérance utilisateur pour la comparaison des coordonnées (axe Y). */
        extern PSYS_TLS pfloat ToleranceCoords_Y;
        /*! Tolérance utilisateur pour la comparaison des coordonnées (axe Z). */
        extern PSYS_TLS pfloat ToleranceCoords_Z;

        inline void Init(pfloat t_xyz)
        {
            TolerancesP3D::EpsilonCoords = 1e-4;
            TolerancesP3D::EpsilonAngles = 0.1;
            TolerancesP3D::EpsilonVecteurs = 0.02;
            TolerancesP3D::ToleranceCoords = TolerancesP3D::ToleranceCoords_X = TolerancesP3D::ToleranceCoords_Y = TolerancesP3D::ToleranceCoords_Z = t_xyz;
            TolerancesP3D::ToleranceCoordsLarge = t_xyz * 4;
        }
    }

    class DLL_API PBase
    /* Destiné à être hérité pour surcharger les opérations new/delete et inclure les objets dans le gestionnaire de mémoire. */
    {
        public:
            PBase();
            ~PBase();
            static void *operator new(std::size_t taille);
            static void *operator new[](std::size_t taille);
            static void  operator delete(void *p);
            static void  operator delete[](void *p);
    };
}


#ifndef CompareX
#define CompareX(a, b, c) (POSITIFD((a-b)) < c) /* Comparaison avec l'espilon donné en argument. */
//#define CompareX(a, b, c) ((a==b) || POSITIFD(a-b) < c) /* Comparaison avec l'espilon donné en argument. */
#endif // !CompareX

#ifndef CompareE
#define CompareE(a, b) (CompareX(a, b, EPSILON_FIXE))                   /* Comparaison avec Epsilon fixe */
#endif // !CompareE
#ifndef CompareED
#define CompareED(a, b) (CompareX(a, b, EPSILON_FIXE_LARGE))            /* Comparaison avec Epsilon fixe élargi */
#endif // !CompareED
#ifndef CompareN
#define CompareN(a, b) (CompareX(a, b, TolerancesP3D::ToleranceCoords)) /* Comparaison approximative générale (défini par l'utilisateur) */
#endif // !CompareN

/* Déclaration des opérateurs pour les types énumérés. */
#define PENUM_DECL_OPS(NOM) \
    F_INLINE NOM operator| (NOM c1, NOM c2) { return static_cast<NOM>(static_cast<std::underlying_type<NOM>::type>(c1) | static_cast<std::underlying_type<NOM>::type>(c2)); } \
    F_INLINE NOM operator|= (NOM &c1, NOM c2) { c1 = static_cast<NOM>(static_cast<std::underlying_type<NOM>::type>(c1) | static_cast<std::underlying_type<NOM>::type>(c2)); return c1; } \
    F_INLINE NOM operator&= (NOM &c1, NOM c2) { c1 = static_cast<NOM>(static_cast<std::underlying_type<NOM>::type>(c1) & static_cast<std::underlying_type<NOM>::type>(c2)); return c1; } \
    F_INLINE unsigned int operator& (NOM c1, NOM c2) { return (static_cast<unsigned int>(c1) & static_cast<unsigned int>(c2)); } \
    F_INLINE bool operator== (NOM c1, NOM c2) { return (static_cast<std::underlying_type<NOM>::type>(c1) == static_cast<std::underlying_type<NOM>::type>(c2)); } \
    F_INLINE NOM operator^ (NOM c1, NOM c2) { return static_cast<NOM>(static_cast<std::underlying_type<NOM>::type>(c1) ^ static_cast<std::underlying_type<NOM>::type>(c2)); } \
    F_INLINE NOM operator^= (NOM &c1, NOM c2) { c1 = static_cast<NOM>(static_cast<std::underlying_type<NOM>::type>(c1) ^ static_cast<std::underlying_type<NOM>::type>(c2)); return c1; }

/* Conversion type énuméré->int */
#define PENUM_CAST_INT(VAL) static_cast<puint>(VAL)

#endif // PERSPECTIVE_DEF_H
