
#include <WFX/SDL/SDLTextureManager.h>
#include <SDL.h>
#include <SDL/SDL_image.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include <WFX/lang/WFXException.h>

using namespace WFX;
using namespace WFX::SDL;
using namespace WFX::scene;
using namespace WFX::lang;
using namespace std;

SDLTextureManager::SDLTextureManager() {
    glEnable(GL_TEXTURE_2D);
}

SDLTextureManager::~SDLTextureManager() {
    glDisable(GL_TEXTURE_2D);
}

bool SDLTextureManager::loadTexture(const Texture& t) {

    //TODO: auto-detect number of channels, currently hardcoded to 3

    //texture file
    string f = t.getFile().toString();
    //extension
    string ext;
    try {
        ext = t.getFile().getExt();
    } catch (WFXException& e) {
        RETHROW(e, "unable to load Texture " << f);
    }

    //load texture
    SDL_Surface* pBitmap[1];
    pBitmap[0] = IMG_Load(f.data());

    if (pBitmap[0] == NULL) {
        THROW(" Failed loading " << f << " : " << SDL_GetError());
        return false;
    }

    //bind Texture
    glBindTexture(GL_TEXTURE_2D, t.getHandle());

    Int width = pBitmap[0]->w;                      //img widht
    Int height = pBitmap[0]->h;                     //img height
    UChar* data = (UChar*)(pBitmap[0]->pixels);     //pointer to the pixels
    UChar* newData = new UChar[width*height*3];     //new pixels
    Int channels = 3;                               //R,G,B
    Int BytesPerPixel = pBitmap[0]->format->BytesPerPixel;
    
    //swap pixels
    for(Int i=0; i<(height/2); ++i)
        for(Int j=0; j<width*BytesPerPixel; j+=BytesPerPixel)
            for(Int k=0; k<BytesPerPixel; ++k)
                std::swap(data[(i*width*BytesPerPixel)+j+k], data[((height-i-1)*width*BytesPerPixel)+j+k]);

    //extract R,G,B values from bitmap
    for (Int i=0; i<(width*height); i++) {
        UChar r,g,b;

        UInt pixel_value = 0;                                    // 32 bit unsigned int (as dictated by SDL)

        // the following loop extracts the pixel (however wide it is 8,16,24 or 32) and 
        // creates a long with all these bytes taken together.
        for (Int j=BytesPerPixel-1; j>=0; --j) {
            pixel_value = pixel_value << 8;                        // left shift pixel value by 8 bits
            pixel_value = pixel_value | data[(i*BytesPerPixel)+j]; // then make the last 8 bits of pixel value  =
        }                                                          // the byte that we extract from pBitmap's data

        SDL_GetRGB(pixel_value, pBitmap[0]->format, (Uint8 *)&r, (Uint8 *)&g, (Uint8 *)&b);     // here we get r,g,b from pixel_value which is stored in the form specified by pBitmap->format

        newData[(i * channels) + 0] = r;          // in our tImage classes we store r first
        newData[(i * channels) + 1] = g;          // then g
        newData[(i * channels) + 2] = b;          // and finally b (for bmps - three channels only)

        pixel_value = 0;                                           // reset pixel , else we get incorrect values of r,g,b
    }

    // Build Mipmaps (builds different versions of the picture for distances - looks better)
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pBitmap[0]->w, pBitmap[0]->h, GL_RGB, GL_UNSIGNED_BYTE, newData);

    // Lastly, we need to tell OpenGL the quality of our texture map.
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    // Now we need to free the bitmap data that we loaded since openGL stored it as a texture
    delete [] newData;

    SDL_FreeSurface(pBitmap[0]);                        // Free the texture data we dont need it anymore
    
    LOG("done loading Texture " << f); 

    return true; 
}

UInt SDLTextureManager::nextHandle() {
    UInt ret = 0;
    glGenTextures(1, &ret);
    return ret;
}

void SDLTextureManager::bindTexture(UInt th) {
    TextureManager::bindTexture(th);
    glBindTexture(GL_TEXTURE_2D, th);
}


