/***********************************************************************************
 *                                                                                 *
 * Voreen - The Volume Rendering Engine                                            *
 *                                                                                 *
 * Copyright (C) 2005-2012 University of Muenster, Germany.                        *
 * Visualization and Computer Graphics Group <http://viscg.uni-muenster.de>        *
 * For a list of authors please refer to the file "CREDITS.txt".                   *
 *                                                                                 *
 * This file is part of the Voreen software package. Voreen is free software:      *
 * you can redistribute it and/or modify it under the terms of the GNU General     *
 * Public License version 2 as published by the Free Software Foundation.          *
 *                                                                                 *
 * Voreen is distributed in the hope that it will be useful, but WITHOUT ANY       *
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR   *
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.      *
 *                                                                                 *
 * You should have received a copy of the GNU General Public License in the file   *
 * "LICENSE.txt" along with this file. If not, see <http://www.gnu.org/licenses/>. *
 *                                                                                 *
 * For non-commercial academic use see the license exception specified in the file *
 * "LICENSE-academic.txt". To get information about commercial licensing please    *
 * contact the authors.                                                            *
 *                                                                                 *
 ***********************************************************************************/

#include "tgt/texturereader.h"

#include "tgt/gpucapabilities.h"
#include "tgt/logmanager.h"

namespace tgt {

const std::string TextureReader::loggerCat_("tgt.Texture.Reader");

TextureReader::TextureReader()
{}

GLsizei TextureReader::checkSize(GLsizei s) {
    int k = 1;
    while (k < s)
        k <<= 1;
    return k;
}

bool TextureReader::create1DTexture(Texture* t, Texture::Filter filter, bool compress, bool createOGLTex) {
    t->type_ = GL_TEXTURE_1D;

    switch (t->bpp_) {
    case 3:
        t->format_ = GL_RGB;
        compress ? t->internalformat_ = GL_COMPRESSED_RGB_ARB : t->internalformat_ = GL_RGB;
        break;

    case 4:
        t->format_ = GL_RGBA;
        compress ? t->internalformat_ = GL_COMPRESSED_RGBA_ARB : t->internalformat_ = GL_RGBA;
        break;

    default:
        LERROR(static_cast<int>(t->bpp_)<< " bytes per pixel...error!");
        return false;
    }

    if (createOGLTex) {
        glGenTextures(1, &t->id_);
        glBindTexture(GL_TEXTURE_1D, t->id_);

        if (!GpuCaps.isAnisotropicFilteringSupported() && filter == Texture::ANISOTROPIC)
            filter = Texture::MIPMAP;

        t->setFilter(filter);
        t->uploadTexture();
    }
    return true;
}

bool TextureReader::create2DTexture(Texture* t, Texture::Filter filter, bool compress, bool createOGLTex) {
    t->type_ = GL_TEXTURE_2D;

    // derived internal format from format and bit depth
    switch (t->format_) {
    case GL_LUMINANCE:
        if (t->bpp_ == 1)
            t->internalformat_ = GL_LUMINANCE;
        else if (t->bpp_ == 2)
            t->internalformat_ = GL_LUMINANCE16;
        else {
            LERROR(t->bpp_ << " bytes per pixel not supported for texture format GL_LUMINANCE");
            return false;
        }
        break;

    case GL_LUMINANCE_ALPHA:
        if (t->bpp_ == 2)
            t->internalformat_ = GL_LUMINANCE_ALPHA;
        else if (t->bpp_ == 4)
            t->internalformat_ = GL_LUMINANCE16_ALPHA16;
        else {
            LERROR(t->bpp_ << " bytes per pixel not supported for texture format GL_LUMINANCE_ALPHA");
            return false;
        }
        break;

    case GL_RGB:
        if (t->bpp_ == 3)
            compress ? t->internalformat_ = GL_COMPRESSED_RGB_ARB : t->internalformat_ = GL_RGB;
        else if (t->bpp_ == 6)
            t->internalformat_ = GL_RGB16;
        else {
            LERROR(t->bpp_ << " bytes per pixel not supported for texture format GL_RGB");
            return false;
        }
        break;

    case GL_RGBA:
        if (t->bpp_ == 4)
            compress ? t->internalformat_ = GL_COMPRESSED_RGBA_ARB : t->internalformat_ = GL_RGBA;
        else if (t->bpp_ == 8)
            t->internalformat_ = GL_RGBA16;
        else {
            LERROR(t->bpp_ << " bytes per pixel not supported for texture format GL_RGBA");
            return false;
        }
        break;

    default:
        // unspecified or unknown format, try to derive from bpp
        LWARNING("Unspecified or unknown texture format, trying to derive format from bbp ...");
        switch (t->bpp_) {
        case 1:
            t->format_ = GL_LUMINANCE;
            t->internalformat_ = GL_LUMINANCE;
            break;

        case 2:
            t->format_ = GL_LUMINANCE;
            t->internalformat_ = GL_LUMINANCE16;
            break;

        case 3:
            t->format_ = GL_RGB;
            compress ? t->internalformat_ = GL_COMPRESSED_RGB_ARB : t->internalformat_ = GL_RGB;
            break;

        case 4:
            t->format_ = GL_RGBA;
            compress ? t->internalformat_ = GL_COMPRESSED_RGBA_ARB : t->internalformat_ = GL_RGBA;
            break;

        case 8: // 16-bit-per-channel RGBA
            t->format_ = GL_RGBA;
            t->internalformat_ = GL_RGBA16;
            break;

        case 12: //HDR-RGB, cut down to one byte per channel (until proper hdr-handling is implemented)
            t->format_ = GL_RGB;
            t->internalformat_ = GL_RGB;
            t->bpp_ = 3;
            break;

        default:
            LERROR(static_cast<int>(t->bpp_)<< " bytes per pixel not supported!");
            return false;
        }
    }

    if (createOGLTex) {
        glGenTextures(1, &t->id_);
        glBindTexture(GL_TEXTURE_2D, t->id_);

        if (!GpuCaps.isAnisotropicFilteringSupported() && filter == Texture::ANISOTROPIC)
            filter = Texture::MIPMAP;

        t->setFilter(filter);
        t->uploadTexture();
    }
    return true;
}

bool TextureReader::createRectangleTexture(Texture* t, Texture::Filter filter, bool compress, bool createOGLTex) {
    switch (t->bpp_) {
    case 3:
        t->format_ = GL_RGB;
        compress ? t->internalformat_ = GL_COMPRESSED_RGB_ARB : t->internalformat_ = GL_RGB;
        break;

    case 4:
        t->format_ = GL_RGBA;
        compress ? t->internalformat_ = GL_COMPRESSED_RGBA_ARB : t->internalformat_ = GL_RGBA;
        break;

    default:
        LERROR(static_cast<int>(t->bpp_) << " bytes per pixel...error!");
        return false;
    }

    if (createOGLTex) {
        glGenTextures(1, &t->id_);
#ifdef GL_TEXTURE_RECTANGLE_ARB
        glBindTexture(GL_TEXTURE_RECTANGLE_ARB, t->id_);
#endif
        if (!GpuCaps.isAnisotropicFilteringSupported() && filter == Texture::ANISOTROPIC)
            filter = Texture::MIPMAP;

        t->setFilter(filter);
        t->uploadTexture();
    }
    return true;
}

bool TextureReader::create3DTexture(Texture* t, Texture::Filter filter, bool compress, bool createOGLTex) {
    t->type_ = GL_TEXTURE_3D;

    switch (t->bpp_) {
    case 1:
        t->internalformat_ = GL_INTENSITY;
        break;
    case 2:
        t->internalformat_ = GL_INTENSITY16;
        break;
    case 3:
        t->format_ = GL_RGB;
        compress ? t->internalformat_ = GL_COMPRESSED_RGB_ARB : t->internalformat_ = GL_RGB;
        break;
    case 4:
        t->format_ = GL_RGBA;
        compress ? t->internalformat_ = GL_COMPRESSED_RGBA_ARB : t->internalformat_ = GL_RGBA;
        break;
    default:
        LERROR(t->bpp_<< " bytes per pixel...error!");
        return false;
    }

    if (createOGLTex) {
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glGenTextures(1, &t->id_);
        glBindTexture(GL_TEXTURE_3D, t->id_);

        if (!GpuCaps.isAnisotropicFilteringSupported() && filter == Texture::ANISOTROPIC)
            filter = Texture::MIPMAP;

        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP);
        t->setFilter(filter);
        t->uploadTexture();
    }
    return true;
}

} // namespace tgt
