/*
 * NodePixelTexture.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include "stdafx.h"

#include "swt.h"
#include "DuneApp.h"
#include "resource.h"

#include "NodePixelTexture.h"
#include "Proto.h"
#include "FieldValue.h"
#include "SFImage.h"
#include "SFBool.h"
#include "Texture.h"

ProtoPixelTexture::ProtoPixelTexture(Scene *scene)
  : Proto(scene, "PixelTexture")
{
    image.set(
          addExposedField(SFIMAGE, "image", new SFImage(0, 0, 0, NULL)));
    repeatS.set(
          addField(SFBOOL, "repeatS", new SFBool(true)));
    repeatT.set(
          addField(SFBOOL, "repeatT", new SFBool(true)));
    if (TheApp->getCoverMode()) {
        // non standard Covise/COVER extensions
        blendMode.set(
          addField(SFINT32, "blendMode", new SFInt32(0), 
                   new SFInt32(0), new SFInt32(5)));
        setFieldFlags(blendMode, FF_COVER_ONLY);
    }

    alphaChannel.set(
          addField(SFSTRING, "alphaChannel", new SFString("AUTO")));
    setFieldFlags(alphaChannel, FF_KAMBI_ONLY);

    textureProperties.set(
        addField(SFNODE, "textureProperties", new SFNode(), 
                 X3D_TEXTURE_PROPERTIES));
    setFieldFlags(textureProperties, FF_X3D_ONLY);
}

Node *
ProtoPixelTexture::create(Scene *scene)
{ 
    return new NodePixelTexture(scene, this); 
}

NodePixelTexture::NodePixelTexture(Scene *scene, Proto *def)
  : Node(scene, def)
{
    _image = NULL;
    _textureWidth = 0;
    _textureHeight = 0;
    _imageStatus = IMG_STATUS_UNLOADED;
    _textureName = 0;
    _components = 0;
    _glColorMode = GL_RGB;          // default: no transparency

    _scaleRequired = false;

    _isTransparent = false;
    _isAlphaNot0Not1 = true;
}

NodePixelTexture::NodePixelTexture(const NodePixelTexture &node)
  : Node(node)
{
    _image = NULL;
    _textureName = 0;  // must load its own texture though
    _textureWidth = node._textureWidth;
    _textureHeight = node._textureHeight;
    _components=node._components;
    _glColorMode = node._glColorMode ;
    _imageStatus = node._imageStatus;
    _scaleRequired = node._scaleRequired;
    _isTransparent = node._isTransparent;

    int size = _textureWidth * _textureHeight * _components;
    // copy image data, if any
    if (node._image)
        if ((size > 0) && 
            (((NodePixelTexture)node).image()->getNumPixels() > 0)) {
            _image = new unsigned char[size];
            memcpy(_image, node._image, size);
        } 
}

NodePixelTexture::~NodePixelTexture()
{
    delete [] _image;
}

void
NodePixelTexture::setField(int field, FieldValue *value)
{
    if (field == image_Field()) {
        _imageStatus = IMG_STATUS_UNLOADED;
    }
    Node::setField(field, value);
}

void
NodePixelTexture::load()
{
    SFImage *sfImage = image();
    int width = 0;
    int height = 0;
    unsigned char *data = NULL;

    _imageStatus = IMG_STATUS_ERROR;

    if (sfImage != NULL) {
        width = sfImage->getWidth();
        height = sfImage->getHeight();
        _components = sfImage->getComponents();
        if (_components==4)
            _glColorMode=GL_RGBA;
        else if (_components==3)
            _glColorMode = GL_RGB;
        else if (_components==2)
            _glColorMode = GL_LUMINANCE_ALPHA;
        else if (_components==1)
            _glColorMode = GL_LUMINANCE;
        else 
            return;
        if ((width == 0) || (height == 0))
            return;
        data = new unsigned char [width * height * _components];
        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++)
                for (int k = 0; k < _components; k++) {
                    int numPixel = i + j * width;
                    int numData = numPixel * _components + k;
                    data[numData] = 0;
                    if (sfImage->getNumPixels() > numPixel) {
                        unsigned int bytes = sfImage->getPixels()[numPixel];
                        int numShifts = (_components - k - 1) * 8;
                        data[numData] = (bytes >> numShifts) & 0xFF;
                    } 
                }
    } else
        return;

    _textureWidth = 1;
    _textureHeight = 1;

    while (_textureWidth < width)
       _textureWidth <<= 1;

    while (_textureHeight < height)
       _textureHeight <<= 1;

    delete [] _image;

    if (width == _textureWidth && height == _textureHeight) {
        _scaleRequired = false;
        _image = data;
    } else {
        _scaleRequired = true;
        _image = new unsigned char[_textureWidth * _textureHeight * 
                                   _components];
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glPixelStorei(GL_PACK_ALIGNMENT, 1);
        gluScaleImage(_glColorMode, width, height, GL_UNSIGNED_BYTE, data,
                      _textureWidth, _textureHeight, GL_UNSIGNED_BYTE, _image);
        delete [] data;
    }
    _imageStatus = IMG_STATUS_LOADED;
    if ((_components == 2) || (_components == 4)) {
        int size = _textureWidth * _textureHeight * _components;
        int i;
        for (i = 0; i < size; i += _components)
            if (_image[i + _components - 1] < 0xff) {
                _isTransparent = true;
                break;
            }
        for (i = 0; i < size; i += _components)
            if (_image[i + _components - 1] != 0xff)
                if (_image[i + _components - 1] != 0x0) {
                    _isAlphaNot0Not1 = true;
                    break;
                }
    }
    if (TheApp->isAnaglyphStereo()) {
        // change colors into grayscale values
        if ((_components == 3) || (_components == 4)) {
            int size = _textureWidth * _textureHeight * _components;
            for (int i = 0; i < size; i += _components) {
                int gray = (_image[i + 0] + _image[i + 1] + _image[i + 2]) / 3;
                _image[i + 0] = gray;
                _image[i + 1] = gray;
                _image[i + 2] = gray;
            }
        }
    }
}

void
NodePixelTexture::bind()
{
    if (_imageStatus == IMG_STATUS_UNLOADED) {
        load();
        if (_textureName != 0) glDeleteTextures(1, &_textureName);
        _textureName = 0;
    }
    if (_imageStatus == IMG_STATUS_LOADED) {
        if (_textureName == 0) {
            glGenTextures(1, &_textureName);
            glBindTexture(GL_TEXTURE_2D, _textureName);
//            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            glTexImage2D(GL_TEXTURE_2D, 0, _components, 
                         _textureWidth, _textureHeight, 0,
                         _glColorMode, GL_UNSIGNED_BYTE, _image);
            GLenum error=glGetError();
            if (error!=0)
               {
               /* what's wrong here with "invalid value" ? */
               fprintf(stderr,"GL Error: %s\n",gluErrorString(error));
               }

        } else {
            glBindTexture(GL_TEXTURE_2D, _textureName);
        }
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 
                        repeatS()->getValue() ? GL_REPEAT : GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 
                        repeatT()->getValue() ? GL_REPEAT : GL_CLAMP);
        glEnable(GL_TEXTURE_2D);
#ifdef HAVE_TEXTUREIMAGE_MODE
        switch (mode()->getValue()) {
          case 1:
            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
            glEnable(GL_TEXTURE_GEN_S);
            glEnable(GL_TEXTURE_GEN_T);
            break;
          case 2:
            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
            glEnable(GL_TEXTURE_GEN_S);
            glEnable(GL_TEXTURE_GEN_T);
            break;
          case 3:
            glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
            glEnable(GL_TEXTURE_GEN_S);
            glEnable(GL_TEXTURE_GEN_T);
            break;
        }
#endif
    }
}

void
NodePixelTexture::unbind()
{
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_TEXTURE_GEN_S);
    glDisable(GL_TEXTURE_GEN_T);
}

int
NodePixelTexture::isLoaded()
{
    return _imageStatus == IMG_STATUS_LOADED;
}

int NodePixelTexture::getProfile(void) const
{ 
    return PROFILE_INTERCHANGE;
}

int
NodePixelTexture::getComponentLevel(void) const
{
    if (_textureWidth != 512)
        return 1;
    if (_textureHeight != 512)
        return 1;
    if (_isAlphaNot0Not1)
        return 1;
    return -1;
}

const char* 
NodePixelTexture::getComponentName(void) const
{
    static const char* name = "Texturing";
    return name;
}


