#ifndef OBJECT_3D_HPP
#define OBJECT_3D_HPP

// Détection du système cible...
#include "usgeDetectSystem.h"

// Les types "USpeed Game Engine"
#include "Core/CoreTypes.hpp"
#include "Material/Texture.hpp"
#include "Physic/PhysicState.hpp"

// Les vecteurs
#include <vector>

namespace USpeedGameEngine
{

class Object3D;

/**
 * Structure contenant les contraintes sur l'attachement d'un objet 3D à un éventuel parent
 */
typedef struct StructObject3DParentAttach
{
    Object3D    *parent;    //!< Parent auquel est attaché l'objet courant
    USGE_Float3 point;      //!< Point d'ancrage
} Object3DParentAttach;


class Object3D
{
    /****************
    * Les attributs *
    ****************/
    public :
        Object3DParentAttach parentAttach;  //!< La contrainte sur l'attachement de l'objet courant à un éventuel parent
        USGE_Vertex *vertices;              //!< Liste des points 3D du modèle
        unsigned long int verticesCount;    //!< Le nombre de points 3D dans le modèle

        Material *material;                 //!< Le materiau à appliquer
        bool smooth;                        //!< Affichage doux des polygones
        USGE_Float3 translation;            //!< Translation à effectuer avant le rendu
        USGE_Float3 rotation;               //!< Rotation à effectuer avant le rendu
        USGE_Float3 scale;                  //!< Mise à l'échelle à effectuer avant le rendu
        Physic::PhysicState *physicState;   //!< État physique de l'objet
        //Skeleton *skeleton;                 //!< Le squelette de l'objet en vue d'une animation

        /** La boîte d'encadrement du modèle */
        struct StructBoundinBox
        {
            USGE_Float3 min;    //!< Plus petits
            USGE_Float3 max;    //!< Plus grands
        } boundingBox;          //!< La boîte d'encadrement du modèle

        /** La sphère d'encadrement du modèle */
        struct StructBoundinSphere
        {
            USGE_Float3 center; //!< Le centre de la sphère
            float radius;       //!< Le rayon de la sphère
        } boundingSphere;       //!< La sphère d'encadrement du modèle

    protected :
    private :
        /** Structure pour les plans de coupe pour aider aux calculs des ombres projetées. */
        typedef struct PlaneStruct
        {
            float a;
            float b;
            float c;
            float d;
            bool visible;
        } USGE_Plane;
        USGE_Plane *planes;                 //!< Les plans pour la génération des ombres
        unsigned long int planesCount;      //!< Le ombre de plans pour la génération des ombres
    /***************
    * Les méthodes *
    ***************/
    public :
        // Destructeur par défaut
        ~Object3D();
        // Constructeur par défaut
        Object3D();

        // Affectation d'une liste de Vertex (par copie ou non)
        bool setVertices(USGE_Vertex *vList, unsigned long int vCount);
        bool setVertices(USGE_Vertex *vList, unsigned long int vCount, bool copy);
        // Affectation d'une texture à appliquer au modèle 3D
        void setMaterial(Material *pMat);
        // Remise à zéro de l'instance de la classe
        void clear();
        // Effectue le rendu de l'objet
        void render();
        void render(bool noCamera);
        // Effectue le rendu de l'ombre de l'objet en fonction de la position de la lumière spécifiée.
        void renderShadow(USGE_Float3 const &lightPosition);
        // Retourne le nombre de "Vertex" que contient l'objet 3D.
        unsigned long int getVerticesCount();
        // Retourne le "Vertex" demandé.
        USGE_Vertex *getVertex(unsigned long int num);
        // Modifie la géométrie du modèle actuellement en mémoire qu'il tienne dans une boîte de "1 x 1 x 1".
        void scaleToUnit();
        // Modifie la géométrie du modèle actuellement en mémoire qu'il tienne dans une boîte de "A x A x A".
        void scaleTo(float scale);
        // Modifie la géométrie du modèle actuellement en mémoire qu'il tienne dans une boîte de "X x Y x Z".
        void scaleTo(USGE_Float3 const &scale);

        /////////////////
        // Chargements //
        /////////////////
        // Chargement de la géométrie depuis un fichier.
        bool load3D(const char *filename);
        // Chargement de la géométrie depuis un tampon mémoire.
        bool load3DFromBuffer(const char *data, unsigned long int dataSize);
        // Chargement d'un cube
        bool loadCube();
        // Chargement d'un parallèlépipède rectangle
        bool loadBox(USGE_Float3 const &min, USGE_Float3 const &max, std::vector<float*> *uvMap);
        bool loadBox(USGE_Float3 const &min, USGE_Float3 const &max);
        bool loadBox(USGE_Float3 const &dim);
        // Calcul de la Bounding Box (boîte d'encadrement).
        void computeBB();

        //////////////////
        // Rendu doux ? //
        //////////////////
        // Choix d'un rendu doux ou brut par défaut.
        static void setDefaultSmooth(bool activate);
        // État du choix d'un rendu doux ou brut par défaut.
        static bool getDefaultSmooth();
        // Choix d'un rendu doux ou brut.
        void setSmooth(bool activate);
        // État du choix d'un rendu doux ou brut.
        bool getSmooth();

        ////////////////
        // Les ombres //
        ////////////////
        // Permet d'activer/désactiver la génération de l'ombre de ce modèle lors du rendu par rapport à la lumière concernée
        //void setShadowState(unsigned long int num, bool active);
        // Permet de d'activer/désactiver toutes les ombres
        //void setShadowState(bool active);

        ////////////////
        // Dépendance //
        ////////////////
        // Définition de la dépendance à un parent
        void attachTo(Object3D *parent, USGE_Float3 const &translate);
        // Suppression du lien d'ancrage à un parent
        void detach();

        //////////////////////////////////////
        // Informations sur l'état physique //
        //////////////////////////////////////
        // Permet de savoir si l'objet a un état physique défini
        bool hasPhysicState();
        // Affectation d'un état physique à l'objet.
        void setPhysicState(Physic::PhysicState *phyState);
        // Accès à l'état physique de l'objet
        Physic::PhysicState *getPhysicState();

        /////////////////////
        // Transformations //
        /////////////////////
        // Permet de définir la translation à effectuer sur modèle avant le rendu
        void setTranslation(USGE_Float3 const &trans, bool add);
        void setTranslation(USGE_Float3 const &trans);
        // Permet de définir la rotation à effectuer sur modèle avant le rendu
        void setRotation(USGE_Float3 const &rot, bool add);
        void setRotation(USGE_Float3 const &rot);
        // Définit l'échelle d'affichage du modèle
        void setScale(float scale);
        void setScale(USGE_Float3 const &scale);

        ////////////////
        // Accesseurs //
        ////////////////
        // Accès à la translation actuelle du modèle
        void getTranslation(USGE_Float3 &pTranslation);
        USGE_Float3 getTranslation();
        // Accès à la rotation actuelle du modèle
        void getRotation(USGE_Float3 &pRotation);
        USGE_Float3 getRotation();
        // Accès à l'échelle dynamique du modèle
        void getScale(USGE_Float3 &pScale);
        USGE_Float3 getScale();

    protected :
    private :
        // Effectue les transformations directes de l'objet courant en tant que parent lors du rendu d'un enfant
        void processParentTransform(Object3DParentAttach *anchor);
};

}        // Fin "namespace USpeedGameEngine"

#endif
