#include <stdio.h>
#include <malloc.h>
#include <GL/glu.h>
#include "dms/FileImage.h"
#include "dms/Texture2D.h"

namespace dms
{

Texture2D::Texture2D(char *filename, GLint wrap, GLint min, GLint mag) :
                Texture(wrap,min,mag)
    {
    id_ = 0;
    image_ = 0;
    rescaledImageData_ = 0;
    defined_ = false;
    createdImage_ = false;
    if (filename)
        {
        image_ = new FileImage(filename);
        createdImage_ = true;
        }
    }


Texture2D::Texture2D(Image& image, GLint wrap, GLint min, GLint mag) :
                Texture(wrap,min,mag)
    {
    id_ = 0;
    rescaledImageData_ = 0;
    defined_ = false;
    createdImage_ = false;
    setImage(image);
    }


Texture2D::~Texture2D(void)
    {
    if (rescaledImageData_)
        ::free(rescaledImageData_);
    if (createdImage_)
        delete image_;
    }


void Texture2D::define(void)
    {
    GLsizei xdim2,ydim2;
    void *imgp;
    defined_ = true;
    if ((!image_) || (!image_->data()))
        return;
    xdim2 = 1;
    while (xdim2 <= image_->width())
        xdim2 *= 2;
    xdim2 /= 2;
    ydim2 = 1;
    while (ydim2 <= image_->height())
        ydim2 *= 2;
    ydim2 /= 2;
    if ((image_->width() != xdim2) || (image_->height() != ydim2))
        {
        rescaledImageData_ = (unsigned long *) ::malloc(xdim2 * ydim2 *
                            sizeof(unsigned long));
        gluScaleImage(GL_RGBA, image_->width(), image_->height(),
                      GL_UNSIGNED_BYTE, image_->data(), xdim2, ydim2,
                      GL_UNSIGNED_BYTE, rescaledImageData_);
        imgp = rescaledImageData_;
        }
    else
        imgp = image_->data();
    if (id_ == 0)
        glGenTextures(1, &id_);
    glBindTexture(GL_TEXTURE_2D, id_);
    if ((minFilter() == GL_NEAREST_MIPMAP_NEAREST) ||
          (minFilter() == GL_NEAREST_MIPMAP_LINEAR) ||
          (minFilter() == GL_LINEAR_MIPMAP_NEAREST) ||
          (minFilter() == GL_LINEAR_MIPMAP_LINEAR))
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, xdim2, ydim2, GL_RGBA,
                          GL_UNSIGNED_BYTE, imgp);
    else
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xdim2, ydim2, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, imgp);
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,magFilter());
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,minFilter());
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,wrapS());
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,wrapT());
    glBindTexture(GL_TEXTURE_2D,0);
    }


void Texture2D::subload(GLint xoffset,GLint yoffset)
    {
    if (!defined_)
        return;
    if ((!image_) || (!image_->data()))
        return;
    glBindTexture(GL_TEXTURE_2D, id_);
    if ((minFilter() == GL_NEAREST_MIPMAP_NEAREST) ||
          (minFilter() == GL_NEAREST_MIPMAP_LINEAR) ||
          (minFilter() == GL_LINEAR_MIPMAP_NEAREST) ||
          (minFilter() == GL_LINEAR_MIPMAP_LINEAR))
        printf("ERROR: Texture2D:subload(): subloading a mipmapped texture"
               " is not supported\n");
    else
        glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset,
                        image_->width(), image_->height(),
                        GL_RGBA, GL_UNSIGNED_BYTE, image_->data());
    glBindTexture(GL_TEXTURE_2D,0);
    }


void Texture2D::apply(void)
    {
    if (!defined_)
        define();
    if (id_)
        {
        glBindTexture(GL_TEXTURE_2D,id_);
        glEnable(GL_TEXTURE_2D);
        }
    else
        {
        glBindTexture(GL_TEXTURE_2D,0);
        glDisable(GL_TEXTURE_2D);
        }
    }


void Texture2D::disable(void)
    {
    glBindTexture(GL_TEXTURE_2D,0);
    glDisable(GL_TEXTURE_2D);
    }


void Texture2D::setImage(Image& im)
    {
    image_ = &im;
    }

}
