
#include <SDL.h>
#include <SDL_image.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <iostream>
#include "TextureCache.h"
#include "TextureLoader/TextureLoader.h"
#include "FileLoader/FileLoader.h"
using namespace std;

//#define DEBUG_GL_ERROR
#ifdef DEBUG_GL_ERROR
#define checkGLError() checkGLErrorLine(__FUNCTION__, __LINE__)
static void checkGLErrorLine(const char * function, int line)
{
    GLenum err = glGetError();
    if (err != 0)
    {
        cerr << "gl error in " << function
            << ": " << err << " (0x" << hex << err << ") at line "
            << dec << line << endl;
    }
}
#else
#define checkGLError()
#endif

TextureCache::~TextureCache()
{
}

GLuint TextureCache::load(const FileLoader::Path & path,
        FileLoader & fileLoader, bool mipmaps,
        int mode, int quality)
{
    string filename = path.shortPath;
    if (filename == "")
        filename = path.fullPath;
    if (filename == "")
        return 0;
    map<string,GLuint>::iterator it = m_cache.find(filename);
    if (it != m_cache.end())
    {
        return it->second;
    }
    GLuint tex = loadTexture(path, fileLoader, mipmaps, mode, quality);
    if (tex > 0)
    {
        m_cache[filename] = tex;
    }
    return tex;
}

GLuint TextureCache::loadTexture(const FileLoader::Path & path,
        FileLoader & fileLoader, bool mipmaps,
        int mode, int quality)
{
    checkGLError();
    GLuint texture;
    FileLoader::Buffer buff = fileLoader.load(path);
    if (buff.size <= 0)
        return 0;
    SDL_RWops * ops = SDL_RWFromMem(buff.data, buff.size);
    SDL_Surface * temp = IMG_Load_RW(ops, true);
    if (!temp)
    {
        cerr << "Failed to load image ('" << path.fullPath << "', '"
            << path.shortPath << "'): " << IMG_GetError() << endl;
        return 0;
    }

    SDL_PixelFormat fmt;
    fmt.palette = NULL;
    fmt.BitsPerPixel = 32;
    fmt.BytesPerPixel = 4;
    fmt.Rmask = 0x000000FF;
    fmt.Gmask = 0x0000FF00;
    fmt.Bmask = 0x00FF0000;
    fmt.Amask = 0xFF000000;
    fmt.Rshift = 0;
    fmt.Gshift = 8;
    fmt.Bshift = 16;
    fmt.Ashift = 24;

    SDL_Surface * texsurf = SDL_ConvertSurface(temp, &fmt, SDL_SWSURFACE);
    SDL_FreeSurface(temp);
    if (!texsurf)
    {
        cerr << "Image was not converted properly!" << endl;
        return 0;
    }
    unsigned int * pixels = new unsigned int[texsurf->w * texsurf->h];
    int y;
    unsigned int dstOffset = texsurf->w * (texsurf->h - 1);
    unsigned int srcOffset = 0;
    for (y = 0; y < texsurf->h; y++)
    {
        memcpy(pixels + dstOffset,
            ((unsigned int *)texsurf->pixels) + srcOffset,
            texsurf->w << 2);
        dstOffset -= texsurf->w;
        srcOffset += texsurf->w;
    }
    glGenTextures(1, &texture);
    checkGLError();
    glBindTexture(GL_TEXTURE_2D, texture);
    checkGLError();
    glTexImage2D(GL_TEXTURE_2D, 0, 4, texsurf->w, texsurf->h, 0,
        GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    if (quality > 0)
    {
        checkGLError();
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
            mipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
        checkGLError();
    }
    else
    {
        checkGLError();
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
            mipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
        checkGLError();
    }

    checkGLError();

    if (mipmaps)
    {
        checkGLError();
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA,
            texsurf->w, texsurf->h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
        checkGLError();
    }

    SDL_FreeSurface(texsurf);
    delete[] pixels;
    checkGLError();
    return texture;
}
