###########################################################################
# cgkit - Python Computer Graphics Kit
# Copyright (C) 2004 Matthias Baas (baas@ira.uka.de)
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# cgkit homepage: http://cgkit.sourceforge.net
###########################################################################
# $Id: glmaterial.py,v 1.3 2005/08/28 19:42:43 mbaas Exp $

## \file glmaterial.py
## Contains the GLMaterial class.

import _core
from cgtypes import *
from _OpenGL.GL import *
from _OpenGL.GLU import *
import math
import os, os.path
import _Image as Image

# GLTexture
class GLTexture(_core.GLTexture):
    def __init__(self,
                 imagename = "",
                 image = None,
                 mode = GL_DECAL,
                 mipmap = True,
                 mag_filter = GL_LINEAR,
                 min_filter = GL_LINEAR,
                 wrap_s = GL_REPEAT,
                 wrap_t = GL_REPEAT,
                 internalformat = GL_RGB,
                 texenvcolor = vec4(1),
                 transform = mat4(1),
                 size = None,
                 environment_map = False):
        """Constructor.
        """
        
        _core.GLTexture.__init__(self)
        self.imagename = imagename
        self.mode = mode
        self.mag_filter = mag_filter
        if mipmap and min_filter==GL_LINEAR:
            min_filter = GL_LINEAR_MIPMAP_LINEAR
        self.min_filter = min_filter
        self.wrap_s = wrap_s
        self.wrap_t = wrap_t
        self.texenvcolor = vec4(texenvcolor)
        self.transform = transform
        self.environment_map = environment_map
        self.mipmap = mipmap
        self.internalformat = internalformat
        self.size = size

        self._image = image
        
        # Remember the current working directory
        self._path = os.getcwd()

    # loadTexData
    def loadTexData(self):
        """Load and apply texture file.
        """
        # No image data set? Then use the file name and load the image...
        if self.image==None:
            if self.imagename=="":
                return
            print 'Loading "%s"...'%self.imagename,
            fullname = os.path.join(self._path, self.imagename)
            try:
                img = Image.open(fullname)
                self._passPILImage(img)
            except IOError, e:
                print "failed"
                print e
                return
        else:
            data = self.image
            if type(data)==str:
                # Raw image data as string
                w,h = self.size
                format = self.internalformat
                self.texData(w, h, format, GL_UNSIGNED_BYTE, data)
            else:
                # PIL image
                self._passPILImage(data)


    ## protected:

    def _passPILImage(self, img):
        """Pass a PIL image back to OpenGL.

        img must be a PIL image.
        The image is scaled to a power of 2 if necessary.
        """
        img = self._fitPILImage(img)
        
        w,h = img.size
        
        if img.mode=="RGB":
            format = GL_RGB
        elif img.mode=="RGBA":
            format = GL_RGBA
        else:
            raise RuntimeError, "Unsupported texture format (%s)"%img.mode
        
        self.texData(w, h, format, GL_UNSIGNED_BYTE, img.tostring())
        

    def _fitPILImage(self, img):
        """Scale the image to a power of 2 resolution.

        The image is scale to the user specified resolution. If no
        resolution was set, the next higher power of 2 resolution is used.
        
        If the image resolution is already a power of 2 then
        the input image is returned.
        """
        w,h = img.size
        if self.size==None:
            w2 = 2**int(math.ceil(math.log(w)/math.log(2)))
            h2 = 2**int(math.ceil(math.log(h)/math.log(2)))
        else:
            w2,h2 = self.size
            
        if w2!=w or h2!=h:
            img = img.resize((w2,h2), Image.BICUBIC)

        return img
        
        
    # "image" property...
    
    def _getImage(self):
        """Return the current image.

        This method is used for retrieving the \a image property.

        \return PIL Image (or None)
        """
        return self._image

    def _setImage(self, img):
        """Set a new texture image.

        This method is used for setting the \a image property.

        \param image PIL image or None
        """
        self._image = img
        # Assign the name again so that the image data will be reloaded
        self.imagename = self.imagename

    image = property(_getImage, _setImage, None, "Texture image")

        
# GLMaterial
class GLMaterial(_core.GLMaterial):

    def __init__(self,
                 name = "GLMaterial",
                 ambient = (0.2, 0.2, 0.2, 1),
                 diffuse = (0.7, 0.7, 0.7, 1),
                 specular = (0,0,0,1),
                 shininess = 0.0,
                 emission = (0,0,0,1),
                 blend_factors = None,
                 texture = None,
                 density=1.0):
        _core.GLMaterial.__init__(self, name, density)

        self.ambient = vec4(ambient)
        self.diffuse = vec4(diffuse)
        self.specular = vec4(specular)
        self.shininess = shininess
        self.emission = vec4(emission)
        self.texture = texture
        if blend_factors!=None:
            self.blend_sfactor = blend_factors[0]
            self.blend_dfactor = blend_factors[1]
