
"Lighting."

from __future__ import with_statement

import weakref

import OpenGL.GL as gl
import OpenGL.GLU as glu

import glitch

class LightSwitch(glitch.Node):
    def __init__(self, lights=True, **kw):
        glitch.Node.__init__(self, **kw)
        self.lights = lights

    def render(self, ctx):
        with ctx.push('lighting', True):
            gl.glPushAttrib(gl.GL_ENABLE_BIT)

            if self.lights:
                gl.glEnable(gl.GL_LIGHTING)
            else:
                gl.glDisable(gl.GL_LIGHTING)

            glitch.Node.render(self, ctx)
            gl.glPopAttrib()

class Light(glitch.Node):
    def __init__(self, x=0, y=0, z=0, intensity=1, attenuation=None, **kw):
        glitch.Node.__init__(self, **kw)
        self.i = intensity
        self.attenuation = attenuation
        (self.x, self.y, self.z) = (x, y, z)

    def get_light(self, ctx):
        lights = ctx.setdefault('lights', weakref.WeakKeyDictionary())

        if self not in lights:
            n_lights = gl.glGetInteger(gl.GL_MAX_LIGHTS)
            v = lights.values()

            # Avoid using light 0 as it is a special case.
            for i in xrange(1, n_lights):
                if i not in v:
                    lights[self] = i
                    break

        return gl.GL_LIGHT0 + lights[self]

    def render(self, ctx):
        if not ctx.get('lighting', True):
            glitch.Node.render(self, ctx)
            return

        gl_light = self.get_light(ctx)
        # Treat as positional (rather than directional) by making w
        # nonzero. Confusing!
        gl.glLightfv(gl_light, gl.GL_POSITION, [self.x, self.y, self.z, 1.])

        if self.attenuation is not None:
            (constant, linear, quadratic) = self.attenuation
            gl.glLightf(gl_light, gl.GL_CONSTANT_ATTENUATION, constant)
            gl.glLightf(gl_light, gl.GL_LINEAR_ATTENUATION, linear)
            gl.glLightf(gl_light, gl.GL_QUADRATIC_ATTENUATION, quadratic)

        gl.glEnable(gl_light)
        glitch.Node.render(self, ctx)
        gl.glDisable(gl_light)

    def draw(self, ctx):
        if ctx.get('debug', False):
            gl.glPushAttrib(gl.GL_LIGHTING_BIT | gl.GL_CURRENT_BIT)
            gl.glDisable(gl.GL_LIGHTING)
            gl.glColor(1, 1, 0.3)
            gl.glMatrixMode(gl.GL_MODELVIEW)
            gl.glTranslate(self.x, self.y, self.z)
            quad = glu.gluNewQuadric()
            glu.gluSphere(quad, 0.1, 15, 15)
            gl.glTranslate(-self.x, -self.y, -self.z)
            glu.gluDeleteQuadric(quad)
            gl.glPopAttrib()

class AmbientLight(Light):
    def draw(self, ctx):
        Light.draw(self, ctx)
        gl_light = self.get_light(ctx)
        gl.glLightfv(gl_light, gl.GL_AMBIENT, [self.i, self.i, self.i, 1])
            #[self.r, self.g, self.b, self.a])

class DiffuseLight(Light):
    def draw(self, ctx):
        Light.draw(self, ctx)
        gl_light = self.get_light(ctx)
        gl.glLightfv(gl_light, gl.GL_DIFFUSE, [self.i, self.i, self.i, 1])

class ConeLight(Light):
    "Draw and enable a light at the origin directed towards negative-Z axis"

    def __init__(self, cutoff=45.0, dx=0, dy=0, dz=-1, exp=0, **kwargs):
        Light.__init__(self, **kwargs)
        self.cutoff = cutoff
        (self.dx, self.dy, self.dz) = (dx, dy, dz)
        self.exp = exp

    # def draw(self, ctx):
    #     if ctx.get('debug', False):
    #         # The GLUT cone starts at the circular base; to model a light we
    #         # need to start from the tip. Draw the tip at (0, 0, 0), with
    #         # light direction along the -z axis, which is where we point
    #         # GL_SPOT_DIRECTION.
    #         gl.glColor(1, 1, 0.3)
    #         q = glu.gluNewQuadric()
    #         gl.glMatrixMode(gl.GL_MODELVIEW)
    #         gl.glPushMatrix()
    #         gl.glTranslatef(self.x, self.y, self.z-1.)
    #         radius = tan(pi * (self.cutoff / 180.))
    #         glut.glutWireCone(radius, 0.3, 7, 7)
    #         glu.gluDeleteQuadric(q)
    #         gl.glPopMatrix()

    def render(self, ctx):
        gl_light = self.get_light(ctx)
        gl.glLightfv(gl_light, gl.GL_DIFFUSE, [self.i, self.i, self.i, 1.0])
        gl.glLightfv(gl_light, gl.GL_SPOT_CUTOFF, self.cutoff)
        gl.glLightfv(gl_light, gl.GL_SPOT_EXPONENT, self.exp)

        gl.glLightfv(gl_light, gl.GL_SPOT_DIRECTION,
            [self.dx, self.dy, self.dz])

        Light.render(self, ctx)

