#include "Texture.h"

// Constructeurs et Destructeur
Texture::Texture()
: m_id(0), m_fichierImage(""), m_largeur(0), m_hauteur(0), m_format(0), m_formatInterne(0), m_textureVide(true)
{

}

Texture::Texture(std::string fichierImage)
: m_id(0), m_fichierImage(fichierImage), m_largeur(0), m_hauteur(0), m_format(0), m_formatInterne(0), m_textureVide(false)
{
    createTexture();
}

Texture::Texture(int largeur, int hauteur, GLenum format, GLenum formatInterne, bool textureVide)
: m_id(0), m_fichierImage(""), m_largeur(largeur), m_hauteur(hauteur),
m_format(format), m_formatInterne(formatInterne), m_textureVide(textureVide)
{
    if ( largeur > 0 && hauteur > 0 )
        create();
}

Texture::Texture(Texture const &autre)
{
    *this = autre;
}

Texture::~Texture()
{
    // Destruction de la texture
    if(glIsTexture(m_id) == GL_TRUE)
        glDeleteTextures(1, &m_id);
}

// Méthodes
Texture& Texture::operator=(Texture const &autre)
{
    // Copie des attributs
    m_fichierImage = autre.m_fichierImage;

    m_largeur = autre.m_largeur;
    m_hauteur = autre.m_hauteur;
    m_format = autre.m_format;
    m_formatInterne = autre.m_formatInterne;
    m_textureVide = autre.m_textureVide;

    // Si la texture est vide, alors on appelle la méthode create()
    if(m_textureVide && glIsTexture(autre.m_id) == GL_TRUE)
        create();

    // Sinon, on appelle la méthode createTexture() par défaut
    else if(glIsTexture(autre.m_id) == GL_TRUE)
        createTexture();

    // Retour du pointeur *this
    return *this;
}


bool Texture::createTexture()
{
    // Chargement de l'image dans une surface SDL
    SDL_Surface *imageSDL = SDL_LoadBMP(m_fichierImage.c_str());

    if( !imageSDL )
    {
        std::cout << "Erreur : " << SDL_GetError() << std::endl;
        return false;
    }

    // Inversion de l'image
    SDL_Surface *imageInversee = inverserPixels(imageSDL);
    SDL_FreeSurface(imageSDL);

    // Détermination du format et du format interne pour les images à 3 composantes
    if(imageInversee->format->BytesPerPixel == 3)
    {
        // Format interne
        m_formatInterne = GL_RGB;

        // Format
        if(imageInversee->format->Rmask == 0xff)
            m_format = GL_RGB;
        else
            m_format = GL_RG;
    }

    // Détermination du format et du format interne pour les images à 4 composantes
    else if(imageInversee->format->BytesPerPixel == 4)
    {
        // Format interne
        m_formatInterne = GL_RGBA;

        // Format
		m_format = GL_RGBA;
    }

    // Dans les autres cas, on arrête le chargement
    else
    {
        std::cout << "Erreur, format interne de l'image inconnu" << std::endl;
        SDL_FreeSurface(imageInversee);

        return false;
    }

    // On affecte les dimensions de notre image à celle de la texture
    m_hauteur = imageInversee->h;
    m_largeur = imageInversee->w;

    // Création OpenGL
    create( imageInversee->pixels );

    // Fin de la méthode
    SDL_FreeSurface(imageInversee);
    return true;
}


void Texture::create( void* sampler )
{
    // Destruction d'une éventuelle ancienne texture
    if(glIsTexture(m_id) == GL_TRUE)
        glDeleteTextures(1, &m_id);

    // Génération de l'ID
    glGenTextures(1, &m_id);

    // Verrouillage
    glBindTexture(GL_TEXTURE_2D, m_id);

        // Définition des caractéristiques de la texture
        glTexImage2D(GL_TEXTURE_2D, 0, m_formatInterne, m_largeur, m_hauteur, 0, m_format, GL_UNSIGNED_BYTE, sampler);

        // Application des filtres
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    // Déverrouillage
    glBindTexture(GL_TEXTURE_2D, 0);
}


SDL_Surface* Texture::inverserPixels(SDL_Surface *imageSource) const
{
    // Copie conforme de l'image source sans les pixels
    SDL_Surface *imageInversee = SDL_CreateRGBSurface(0, imageSource->w, imageSource->h, imageSource->format->BitsPerPixel, imageSource->format->Rmask,
                                                         imageSource->format->Gmask, imageSource->format->Bmask, imageSource->format->Amask);


    // Tableau intermédiaires permettant de manipuler les pixels
    unsigned char* pixelsSources = (unsigned char*) imageSource->pixels;
    unsigned char* pixelsInverses = (unsigned char*) imageInversee->pixels;

    // Inversion des pixels
    for(int i = 0; i < imageSource->h; i++)
    {
        for(int j = 0; j < imageSource->w * imageSource->format->BytesPerPixel; j++)
            pixelsInverses[(imageSource->w * imageSource->format->BytesPerPixel * (imageSource->h - 1 - i)) + j] = pixelsSources[(imageSource->w * imageSource->format->BytesPerPixel * i) + j];
    }

    // Retour de l'image inversée
    return imageInversee;
}




GLuint Texture::getID() const
{
    return m_id;
}


void Texture::setFichierImage(const std::string &fichierImage)
{
    m_fichierImage = fichierImage;
    m_textureVide = false;
}

