# -*- coding: utf-8 -*-
import math
import os.path
from gettext import gettext as _

import cairo
from OpenGL.GL import *
from OpenGL.GLU import *

from glchess.defaults import *

import glchess.scene
import texture
import models

PIECE_MOVE_SPEED    = 50.0 # FIXME: Define units
BOARD_ROTATION_TIME = 0.8

SQUARE_WIDTH      = 10.0
BOARD_DEPTH       = 3.0
BOARD_BORDER      = 5.0
BOARD_CHAMFER     = 2.0
BOARD_INNER_WIDTH = (SQUARE_WIDTH * 8.0)
BOARD_OUTER_WIDTH = (BOARD_INNER_WIDTH + BOARD_BORDER * 2.0)
OFFSET            = (BOARD_OUTER_WIDTH * 0.5)

def parse_colour(colour):
    assert colour.startswith('#')
    assert len(colour) == 7
    r = int(colour[1:3], 16) / 255.
    g = int(colour[3:5], 16) / 255.
    b = int(colour[5:7], 16) / 255.
    return (r, g, b)

def blend_colour(colour_a, colour_b, alpha):
    a = parse_colour(colour_a)
    b = parse_colour(colour_b)
    r = a[0] * alpha + b[0] * (1 - alpha)
    g = a[1] * alpha + b[1] * (1 - alpha)
    b = a[2] * alpha + b[2] * (1 - alpha)
    return (r, g, b)

LIGHT_AMBIENT_COLOUR  = (0.4, 0.4, 0.4, 1.0)
LIGHT_DIFFUSE_COLOUR  = (0.7, 0.7, 0.7, 1.0)
LIGHT_SPECULAR_COLOUR = (1.0, 1.0, 1.0, 1.0)

BOARD_AMBIENT   = (0.2, 0.2, 0.2, 1.0)
BOARD_DIFFUSE   = (0.8, 0.8, 0.8, 1.0)
BOARD_SPECULAR  = (1.0, 1.0, 1.0, 1.0)
BOARD_SHININESS = 128.0

BORDER_COLOUR        = parse_colour('#2e3436')
NUMBERING_COLOUR     = parse_colour('#888a85')
BLACK_SQUARE_COLOURS = {None:                               parse_colour('#babdb6'),
                        glchess.scene.HIGHLIGHT_SELECTED:   parse_colour('#73d216'),
                        glchess.scene.HIGHLIGHT_CAN_MOVE:   parse_colour('#3465a4'),
                        glchess.scene.HIGHLIGHT_THREATENED: blend_colour('#af0000', '#babdb6', 0.2),
                        glchess.scene.HIGHLIGHT_CAN_TAKE:   blend_colour('#af0000', '#babdb6', 0.8)}
WHITE_SQUARE_COLOURS = {None:                               parse_colour('#eeeeec'),
                        glchess.scene.HIGHLIGHT_SELECTED:   parse_colour('#8ae234'),
                        glchess.scene.HIGHLIGHT_CAN_MOVE:   parse_colour('#204a87'),
                        glchess.scene.HIGHLIGHT_THREATENED: blend_colour('#cc0000', '#eeeeec', 0.2),
                        glchess.scene.HIGHLIGHT_CAN_TAKE:   blend_colour('#cc0000', '#eeeeec', 0.8)}

import math
def accFrustum(left, right, bottom, top,
               near, far,
               pixdx, pixdy,
               eyedx, eyedy, 
               focus):
    viewport = glGetIntegerv(GL_VIEWPORT)

    xwsize = right - left
    ywsize = top - bottom
    dx = -(pixdx*xwsize/viewport[2] + eyedx*near/focus)
    dy = -(pixdy*ywsize/viewport[3] + eyedy*near/focus)

    glFrustum(left + dx, right + dx, bottom + dy, top + dy, near, far)
    glTranslatef(-eyedx, -eyedy, 0.0)

def accPerspective(fovy, aspect,
                   near, far,
                   pixdx, pixdy,
                   eyedx, eyedy, 
                   focus):
    fov2 = ((fovy*math.pi) / 180.0) / 2.0
    top = near / (math.cos(fov2) / math.sin(fov2))
    bottom = -top
    right = top * aspect
    left = -right
    accFrustum(left, right, bottom, top, near, far, pixdx, pixdy, eyedx, eyedy, focus)

class ChessPiece(glchess.scene.ChessPiece):
    """
    """    
    
    def __init__(self, scene, chessSet, name, location, feedback):
        """
        """
        self.scene = scene
        self.feedback = feedback
        self.chessSet = chessSet
        self.name = name
        self.location = location
        self.pos = self.scene._coordToLocation(location)
        self.targetPos = None  # Position moving to
        self.moving    = False # Is the piece moving?
        self.delete    = False # Delete once moved?

    def move(self, coord, delete, animate = True):
        """Extends glchess.scene.ChessPiece"""
        if not coord:
            self.scene.pieces.remove(self)
            self.feedback.onDeleted()
            return

        self.delete = delete
        self.location = coord
        self.targetPos = self.scene._coordToLocation(coord)
        
        # If already there then delete
        if self.pos == self.targetPos:
            self.targetPos = None
            if delete:
                self.scene.pieces.remove(self)
                self.feedback.onDeleted()
                self.scene.feedback.onRedraw()
            return
            
        # If not currently moving then start
        if not self.moving:
            self.scene._animationQueue.append(self)
            self.moving = True
            
            # Start animation
            if self.scene.animating is False:
                self.scene.animating = True
                self.scene.feedback.startAnimation()
    
    def draw(self, state = 'default'):
        """
        """
        self.chessSet.drawPiece(self.name, state, self.scene)
        
    def animate(self, timeStep):
        """
        
        Return True if the piece has moved otherwise False.
        """
        if self.targetPos is None:
            return False
        
        if self.pos == self.targetPos:
            self.targetPos = None
            if self.delete:
                self.scene.pieces.remove(self)
                self.feedback.onDeleted()
            return False
        
        # Get distance to target
        dx = self.targetPos[0] - self.pos[0]
        dy = self.targetPos[1] - self.pos[1]
        dz = self.targetPos[2] - self.pos[2]
        
        # Get movement step in each direction
        xStep = timeStep * PIECE_MOVE_SPEED
        if xStep > abs(dx):
            xStep = dx
        else:
            xStep *= cmp(dx, 0.0)
        yStep = timeStep * PIECE_MOVE_SPEED
        if yStep > abs(dy):
            yStep = dy
        else:
            yStep *= cmp(dy, 0.0)
        zStep = timeStep * PIECE_MOVE_SPEED
        if zStep > abs(dz):
            zStep = dz
        else:
            zStep *= cmp(dz, 0.0)
            
        # Move the piece
        self.pos = (self.pos[0] + xStep, self.pos[1] + yStep, self.pos[2] + zStep)
        return True

class Scene(glchess.scene.Scene):
    """
    """

    def __init__(self, feedback):
        """Constructor for an OpenGL scene"""
        self.feedback = feedback
        self.lightPos = [100.0, 100.0, 100.0, 1.0] # The scene light position
        self.pieces = []
        self.highlights = {}
        self._animationQueue = []
        self.animating = False
        self.piecesMoving = False

        # The viewport dimensions
        self.viewportWidth    = 0
        self.viewportHeight   = 0
        self.viewportAspect   = 1.0
    
        # Loading screen properties
        self.throbberEnabled  = False
        self.throbberAngle    = 0.0
    
        # The board angle in degrees
        self.boardAngle       = 0.0
        self.oldBoardAngle    = 0.0
        self.targetBoardAngle = 0.0
    
        # OpenGL display list for the board and a flag to regenerate it
        self.boardList        = None
        self.regenerateBoard  = False
    
        self.jitters = ((0.0, 0.0),)

        self.showNumbering = False
        self.numberingTexture = None
        
        self.chessSets = {'white': models.WhiteBuiltinSet(), 'black': models.BlackBuiltinSet()}
        
        # Texture objects for the board
        self.whiteTexture = texture.Texture(os.path.join(TEXTURE_DIR, 'board.png'),
                                            ambient = BOARD_AMBIENT, diffuse = BOARD_DIFFUSE,
                                            specular = BOARD_SPECULAR, shininess = BOARD_SHININESS)
        self.blackTexture = texture.Texture(os.path.join(TEXTURE_DIR, 'board.png'),
                                            ambient = BOARD_AMBIENT, diffuse = BOARD_DIFFUSE,
                                            specular = BOARD_SPECULAR, shininess = BOARD_SHININESS)

    def onRedraw(self):
        """This method is called when the scene needs redrawing"""
        pass
    
    def addChessPiece(self, chessSet, name, coord, feedback):
        """Add a chess piece model into the scene.
        
        'chessSet' is the name of the chess set (string).
        'name' is the name of the piece (string).
        'coord' is the the chess board location of the piece in LAN format (string).
        'feedback' is the feedback object (extends scene.ChessPieceFeedback).

        Returns a reference to this chess piece or raises an exception.
        """
        chessSet = self.chessSets[chessSet]
        piece = ChessPiece(self, chessSet, name, coord, feedback)
        self.pieces.append(piece)
        
        # Redraw the scene
        self.feedback.onRedraw()
        
        return piece

    def setBoardHighlight(self, coords):
        """Highlight a square on the board.
        
        'coords' is a dictionary of highlight types keyed by square co-ordinates.
                 The co-ordinates are a tuple in the form (file,rank).
                 If None the highlight will be cleared.
        """
        if coords is None:
            self.highlights = {}
        else:
            self.highlights = coords.copy()
            
        # Regenerate the optimised board model
        self.regenerateBoard = True

        self.feedback.onRedraw()

    def showBoardNumbering(self, showNumbering):
        """Extends glchess.scene.Scene"""
        self.showNumbering = showNumbering
        self.feedback.onRedraw()

    def showSmooth(self, doSmooth):
        if doSmooth:
            #self.jitters = ((-0.25, 0.25), (0.25, -0.25))
            self.jitters = ((0.0033922635, 0.3317967229), (0.2806016275, -0.2495619123), (-0.273817106, -0.086844639))
            #self.jitters = ((-0.175, -0.25), (-0.375, 0.25), (0.375, -0.25), (0.125, 0.25))
        else:
            self.jitters = ((0.0, 0.0),)
        self.feedback.onRedraw()

    def reshape(self, width, height):
        """Resize the viewport into the scene.
        
        'width' is the width of the viewport in pixels.
        'height' is the width of the viewport in pixels.
        """
        self.viewportWidth = int(width)
        self.viewportHeight = int(height)
        self.viewportAspect = float(self.viewportWidth) / float(self.viewportHeight)
        glViewport(0, 0, self.viewportWidth, self.viewportHeight)
        self.feedback.onRedraw()

    def setBoardRotation(self, angle, faceToFace = False, animate = True):
        """Set the rotation on the board.
        
        'angle' is the angle the board should be drawn at in degress (float, [0.0, 360.0]).
        """
        self.targetBoardAngle = angle
        
        if not animate:
            self.oldBoardAngle = self.boardAngle = angle
            self.feedback.onRedraw()
            return
        
        if self.animating is False:
            self.animating = True
            self.feedback.startAnimation()

    def animate(self, timeStep):
        """Extends glchess.scene.Scene"""
        redraw1 = self.animateThrobber(timeStep)
        self.piecesMoving = self.animatePieces(timeStep)
        redraw2 = self.animateRotation(timeStep)
        if redraw1 or redraw2 or self.piecesMoving:
            self.animating = True
            self.feedback.onRedraw()
        else:
            self.animating = False
        return self.animating

    def render(self, background_colour):
        """Render the scene.
        
        This requires an OpenGL context.
        """
        glClearColor(background_colour[0], background_colour[1], background_colour[2], 1.0)
        if len(self.jitters) > 1:
            glClear(GL_ACCUM_BUFFER_BIT)

        glLightfv(GL_LIGHT0, GL_AMBIENT, LIGHT_AMBIENT_COLOUR)
        glLightfv(GL_LIGHT0, GL_DIFFUSE, LIGHT_DIFFUSE_COLOUR)
        glLightfv(GL_LIGHT0, GL_SPECULAR, LIGHT_SPECULAR_COLOUR)
        
        for jitter in self.jitters:
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
            
            # Set the projection for this scene
            glMatrixMode(GL_PROJECTION)
            glLoadIdentity()
            if len(self.jitters) > 1:
                accPerspective(60.0, self.viewportAspect, 0.1, 1000, jitter[0], jitter[1], 0, 0, 1)
            else:
                gluPerspective(60.0, self.viewportAspect, 0.1, 1000)

            # Do camera and board rotation/translation
            glMatrixMode(GL_MODELVIEW)
            glLoadIdentity()
            self.transformCamera()

            glLightfv(GL_LIGHT0, GL_POSITION, self.lightPos)            
            glEnable(GL_LIGHTING)
            glEnable(GL_LIGHT0)
            
            self.transformBoard()
            
            glEnable(GL_DEPTH_TEST)
            glEnable(GL_CULL_FACE)
        
            glEnable(GL_TEXTURE_2D)
            glEnable(GL_COLOR_MATERIAL)
            self.drawBoard()
            glDisable(GL_COLOR_MATERIAL)
            glDisable(GL_TEXTURE_2D)
        
            if self.showNumbering:
                self.drawNumbering()
        
            # WORKAROUND: Mesa is corrupting polygons on the bottom of the models
            # It could be because the depth buffer has a low bit depth?
            glClear(GL_DEPTH_BUFFER_BIT)
        
            if self.throbberEnabled:
                self.drawThrobber()
            else:
                self.drawPieces()
            
            if len(self.jitters) > 1:
                glAccum(GL_ACCUM, 1.0 / len(self.jitters))

        if len(self.jitters) > 1:
            glAccum(GL_RETURN, 1)

    def getSquare(self, x, y):
        """Find the chess square at a given 2D location.
        
        'x' is the number of pixels from the left of the scene to select.
        'y' is the number of pixels from the bottom of the scene to select.
        
        This requires an OpenGL context.
        
        Return the co-ordinate in LAN format (string) or None if no square at this point.
        """
        # FIXME: Don't rely on this? It seems to get corrupt when multiple games are started
        viewport = glGetIntegerv(GL_VIEWPORT)

        # Don't render to screen, just select
        # Selection buffer is large in case we select multiple squares at once (it generates an exception)
        glSelectBuffer(20)
        glRenderMode(GL_SELECT)

        glInitNames()
        
        # Create pixel picking region near cursor location
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPickMatrix(x, (float(viewport[3]) - y), 1.0, 1.0, viewport)
        gluPerspective(60.0, float(viewport[2]) / float(viewport[3]), 0, 1)

        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        self.transformCamera()

        # Draw board

        self.transformBoard()
        self.drawSquares()

        # Render and check for hits
        # Catch the exception in case we select more than we can fit in the selection buffer
        glFlush()
        try:
            records = glRenderMode(GL_RENDER)
        except GLerror:
            coord = None
        else:
            # Get the first record and use this as the selected square
            if len(records) > 0:
                (_, _, coord) = records[0]
            else:
                coord = None

        # Reset projection matrix
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(60.0, float(viewport[2]) / float(viewport[3]), 0.1, 1000)
        
        if coord is None:
            return None
        
        # Convert from co-ordinates to LAN format
        rank = chr(ord('a') + coord[0])
        file = chr(ord('1') + coord[1])

        return rank + file

    # Private methods
    
    def _coordToLocation(self, coord):
        """
        """
        rank = ord(coord[0]) - ord('a')
        file = ord(coord[1]) - ord('1')
        x = BOARD_BORDER + float(rank) * SQUARE_WIDTH + 0.5 * SQUARE_WIDTH
        z = -(BOARD_BORDER + float(file) * SQUARE_WIDTH + 0.5 * SQUARE_WIDTH)
        
        return (x, 0.0, z)
        
    def animateThrobber(self, timeStep):
        """
        """
        if self.throbberEnabled is False:
            return False
        
        self.throbberAngle += timeStep * (math.pi * 2.0) / 2.0
        while self.throbberAngle > (math.pi * 2.0):
            self.throbberAngle -= 2.0 * math.pi
        return True
        
    def animateRotation(self, timeStep):
        """
        """
        if self.boardAngle == self.targetBoardAngle:
            return False
        
        # Wait unti pieces have stopped moving
        if self.piecesMoving:
            return False

        # Rotate board to the chosen angle
        length = abs(self.targetBoardAngle - self.oldBoardAngle)
        self.boardAngle += timeStep * length / BOARD_ROTATION_TIME
        while self.boardAngle > 360.0:
            self.boardAngle -= 360.0
        travelled = self.targetBoardAngle - self.boardAngle
        while travelled < 0.0:
            travelled += 360.0
            
        # If have moved through the remaining angle then clip to the target
        if travelled >= length:
            self.oldBoardAngle = self.boardAngle = self.targetBoardAngle

        return True
    
    def animatePieces(self, timeStep):
        """
        """
        if len(self._animationQueue) == 0:
            return False
        
        redraw = False
        animationQueue = []
        for piece in self._animationQueue:
            if piece.animate(timeStep):
                piece.moving = True
                redraw = True
                assert(animationQueue.count(piece) == 0)
                animationQueue.append(piece)
            else:
                # Redraw static scene once pieces stop
                if piece.moving:
                    redraw = True
                    self.redrawStatic = True
                piece.moving = False

                # Notify higher classes
                piece.feedback.onMoved()

        self._animationQueue = animationQueue

        if redraw:
            self.feedback.onRedraw()

        return len(self._animationQueue) > 0
            
    def drawThrobber(self):
        """
        """
        glDisable(GL_LIGHTING)
        glDisable(GL_DEPTH_TEST)
            
        # Orthographic projection with even scaling
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
            
        if self.viewportWidth > self.viewportHeight:
            h = 1.0
            w = 1.0 * self.viewportWidth / self.viewportHeight
        else:
            h = 1.0 * self.viewportHeight / self.viewportWidth
            w = 1.0
        gluOrtho2D(0, w, 0, h)
            
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
            
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glColor4f(0.0, 0.0, 0.0, 0.75)
        glBegin(GL_QUADS)
        glVertex2f(-1.0, -1.0)
        glVertex2f(w + 1.0, -1.0)
        glVertex2f(w + 1.0, h + 1.0)
        glVertex2f(-1.0, h + 1.0)
        glEnd()
            
        NSECTIONS = 9
        RADIUS_OUT0 = 0.4
        RADIUS_OUT1 = 0.43
        RADIUS_OUT2 = 0.4
        RADIUS_IN0 = 0.25#0.1
        RADIUS_IN1 = 0.24#0.09
        RADIUS_IN2 = 0.25#0.1
        STEP_ANGLE = 2.0 * math.pi / float(NSECTIONS)
        HALF_WIDTH = 0.8 * (0.5 * STEP_ANGLE)

        glTranslatef(0.5 * w, 0.5 * h, 0.0)
        glBegin(GL_QUADS)
            
        for i in xrange(NSECTIONS):
            theta = 2.0 * math.pi * float(i) / float(NSECTIONS)
            leadTheta = theta + HALF_WIDTH
            lagTheta = theta - HALF_WIDTH
            x0 = math.sin(leadTheta)
            y0 = math.cos(leadTheta)
            x1 = math.sin(theta)
            y1 = math.cos(theta)
            x2 = math.sin(lagTheta)
            y2 = math.cos(lagTheta)
                
            angleDifference = self.throbberAngle - theta
            if angleDifference > math.pi:
                angleDifference -= 2.0 * math.pi
            elif angleDifference < -math.pi:
                angleDifference += 2.0 * math.pi

            stepDifference = angleDifference / STEP_ANGLE
            if stepDifference > -0.5 and stepDifference < 0.5:
                x = 2.0 * abs(stepDifference)
                glColor4f(1.0, x, x, 0.6)
            else:
                glColor4f(1.0, 1.0, 1.0, 0.6)
                
            glVertex2f(RADIUS_IN0 * x0, RADIUS_IN0 * y0)
            glVertex2f(RADIUS_OUT0 * x0, RADIUS_OUT0 * y0)
            glVertex2f(RADIUS_OUT1 * x1, RADIUS_OUT1 * y1)
            glVertex2f(RADIUS_IN1 * x1, RADIUS_IN1 * y1)
                
            glVertex2f(RADIUS_IN1 * x1, RADIUS_IN1 * y1)
            glVertex2f(RADIUS_OUT1 * x1, RADIUS_OUT1 * y1)
            glVertex2f(RADIUS_OUT2 * x2, RADIUS_OUT2 * y2)
            glVertex2f(RADIUS_IN2 * x2, RADIUS_IN2 * y2)

        glEnd()
            
        glDisable(GL_BLEND)

    def transformCamera(self):
        """Perform the camera matrix transformation"""
        gluLookAt(0.0, 80.0, 40.0,
                  0.0,  0.0, 5.0,
                  0.0,  1.0,  0.0)
                  
    def _makeNumberingTexture(self):
        WIDTH = 64
        HEIGHT = 64
        TEXTURE_WIDTH = WIDTH*16
        TEXTURE_HEIGHT = HEIGHT

        surface = cairo.ImageSurface(cairo.FORMAT_A8, TEXTURE_WIDTH, TEXTURE_HEIGHT)
        context = cairo.Context(surface)
        context.set_source_rgba(1.0, 1.0, 1.0, 1.0)
        context.select_font_face("sans-serif", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
        context.set_font_size(WIDTH)
        (ascent, descent, x, x, x) = context.font_extents()
        scale = WIDTH / (ascent + descent)

        def drawCenteredText(x, y, scale, text):
            (w2, h2, w, h, _, _) = context.text_extents(text)
            matrix = context.get_matrix()
            context.translate(x, y)
            context.move_to(-w*scale/2, h*scale/2)
            context.scale(scale, scale)
            context.show_text(text)
            context.set_matrix(matrix)

        yoffset = HEIGHT * 0.5
        xoffset = WIDTH * 0.5
        for i in xrange(8):
            f = 'abcdefgh'[i]
            r = '12345678'[i]
            drawCenteredText(xoffset, yoffset, scale, glchess.chess.translate_file(f))
            drawCenteredText(xoffset + (WIDTH * 8), yoffset, scale, glchess.chess.translate_rank(r))
            xoffset += WIDTH

        t = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, t)
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

        data = surface.get_data()        
        try:
            gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, TEXTURE_WIDTH, TEXTURE_HEIGHT,
                              GL_ALPHA, GL_UNSIGNED_BYTE, str(data))
        except GLUerror, e:
            glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_ALPHA, GL_UNSIGNED_BYTE, str(data))
            
        return t
    
    def drawNumbering(self):
        if self.numberingTexture is None:
            self.numberingTexture = self._makeNumberingTexture()
            
        TEXT_WIDTH = BOARD_BORDER * 0.8
        TEXT_OFFSET = (BOARD_BORDER + BOARD_CHAMFER) * 0.5
        offset = BOARD_BORDER + SQUARE_WIDTH * 0.5
        whiteZOffset = -TEXT_OFFSET
        blackZOffset = -BOARD_OUTER_WIDTH + TEXT_OFFSET
        leftOffset = TEXT_OFFSET
        rightOffset = BOARD_OUTER_WIDTH - TEXT_OFFSET

        def drawLabel(x, z, cell):
            w = 1.0 / 16
            l = cell / 16.0
            
            glPushMatrix()
            glTranslatef(x, 0.0, z)
            glRotatef(-self.boardAngle, 0.0, 1.0, 0.0)

            glBegin(GL_QUADS)            
            glTexCoord2f(l, 0.0)
            glVertex3f(-TEXT_WIDTH/2, 0.0, -TEXT_WIDTH/2)
            glTexCoord2f(l, 1.0)
            glVertex3f(-TEXT_WIDTH/2, 0.0, TEXT_WIDTH/2)
            glTexCoord2f(l + w, 1.0)
            glVertex3f(TEXT_WIDTH/2, 0.0, TEXT_WIDTH/2)
            glTexCoord2f(l + w, 0.0)
            glVertex3f(TEXT_WIDTH/2, 0.0, -TEXT_WIDTH/2)
            glEnd()
            
            glPopMatrix()

        glNormal3f(0.0, 1.0, 0.0)
        glDisable(GL_DEPTH_TEST)
        glEnable(GL_TEXTURE_2D)
        glEnable(GL_COLOR_MATERIAL)        
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glBindTexture(GL_TEXTURE_2D, self.numberingTexture)
        glColor3f(*NUMBERING_COLOUR)

        for i in xrange(8):
            drawLabel(leftOffset, -offset, i + 8)
            drawLabel(rightOffset, -offset, i + 8)
            drawLabel(offset, whiteZOffset, i)
            drawLabel(offset, blackZOffset, i)

            offset += SQUARE_WIDTH

        glEnable(GL_DEPTH_TEST)
        glDisable(GL_BLEND)
        glDisable(GL_COLOR_MATERIAL)
        glDisable(GL_TEXTURE_2D)

    def drawBoard(self):
        """Draw a chessboard"""
        # Use pre-rendered version if available
        if self.regenerateBoard is False and self.boardList is not None:
            glCallList(self.boardList)
            return
        
        # Attempt to store the board as a display list
        if self.boardList is None:
            list = glGenLists(1)
            if list != 0:
                self.boardList = list

        # If have a list store there
        if self.boardList is not None:
            glNewList(self.boardList, GL_COMPILE)
            
        # Board verticies
        # (lower 12-15 are under 8-11)
        #
        # a b c         d e f
        #
        # 8-----------------9  g
        # |\               /|
        # | 4-------------5 |  h
        # | |             | |
        # | | 0---------1 | |  i
        # | | |         | | |
        # | | |         | | |
        # | | 3---------2 | |  j
        # | |             | |
        # | 7-------------6 |  k
        # |/               \|
        # 11---------------10  l
        #
        #     |- board -|
        #        width

        # Draw the border
        glColor3f(*BORDER_COLOUR)

        # Top
        a = 0.0
        b = BOARD_CHAMFER
        c = BOARD_BORDER
        d = c + (SQUARE_WIDTH * 8.0)
        e = d + BOARD_BORDER - BOARD_CHAMFER
        f = d + BOARD_BORDER
        l = 0.0
        k = -BOARD_CHAMFER
        j = -BOARD_BORDER
        i = j - (SQUARE_WIDTH * 8.0)
        h = i - BOARD_BORDER + BOARD_CHAMFER
        g = i - BOARD_BORDER
        verticies = [(c, 0.0, i), (d, 0.0, i),
                     (d, 0.0, j), (c, 0.0, j),
                     (b, 0.0, h), (e, 0.0, h),
                     (e, 0.0, k), (b, 0.0, k),
                     (a, -BOARD_CHAMFER, g), (f, -BOARD_CHAMFER, g),
                     (f, -BOARD_CHAMFER, l), (a, -BOARD_CHAMFER, l),
                     (a, -BOARD_DEPTH, g), (f, -BOARD_DEPTH, g), (f, -BOARD_DEPTH, l), (a, -BOARD_DEPTH, l)]
        
        normals = [(0.0, 1.0, 0.0), (0.0, 0.0, -1.0), (1.0, 0.0, 0.0), (0.0, 0.0, 1.0), (-1.0, 0.0, 0.0),
                   (0.0, 0.707, -0.707), (0.707, 0.707, 0.0), (0.0, 0.707, 0.707), (-0.707, 0.707, 0.0)]
        
        #textureCoords = [(0.0, 0.0), (
        
        quads = [(0, 1, 5, 4, 0), (1, 2, 6, 5, 0), (2, 3, 7, 6, 0), (3, 0, 4, 7, 0),
                 (4, 5, 9, 8, 5), (5, 6, 10, 9, 6), (6, 7, 11, 10, 7), (7, 4, 8, 11, 8),
                 (8, 9, 13, 12, 1), (9, 10, 14, 13, 2), (10, 11, 15, 14, 3), (11, 8, 12, 15, 4)]
        
        glDisable(GL_TEXTURE_2D)
        glBegin(GL_QUADS)
        for q in quads:
            glNormal3fv(normals[q[4]])
            #glTexCoord2fv(textureCoords[q[0]])
            glVertex3fv(verticies[q[0]])
            #glTexCoord2fv(textureCoords[q[1]])
            glVertex3fv(verticies[q[1]])
            #glTexCoord2fv(textureCoords[q[2]])
            glVertex3fv(verticies[q[2]])
            #glTexCoord2fv(textureCoords[q[3]])
            glVertex3fv(verticies[q[3]])
        glEnd()

        # Draw the squares
        glEnable(GL_TEXTURE_2D)
        for x in [0, 1, 2, 3, 4, 5, 6, 7]:
            for y in [0, 1, 2, 3, 4, 5, 6, 7]:
                isBlack = (x + (y % 2) + 1) % 2

                # Get the highlight type
                coord = chr(ord('a') + x) + chr(ord('1') + y)
                try:
                    highlight = self.highlights[coord]
                except KeyError:
                    highlight = None
                
                if isBlack:
                    colour = BLACK_SQUARE_COLOURS[highlight]
                    self.whiteTexture.bind() #blackTexture
                else:
                    colour = WHITE_SQUARE_COLOURS[highlight]
                    self.whiteTexture.bind()

                x0 = BOARD_BORDER + (x * SQUARE_WIDTH)
                x1 = x0 + SQUARE_WIDTH
                z0 = BOARD_BORDER + (y * SQUARE_WIDTH)
                z1 = z0 + SQUARE_WIDTH

                glBegin(GL_QUADS)
                glNormal3f(0.0, 1.0, 0.0)
                glColor3fv(colour)
                glTexCoord2f(0.0, 0.0)
                glVertex3f(x0, 0.0, -z0)
                glTexCoord2f(1.0, 0.0)
                glVertex3f(x1, 0.0, -z0)
                glTexCoord2f(1.0, 1.0)
                glVertex3f(x1, 0.0, -z1)
                glTexCoord2f(0.0, 1.0)
                glVertex3f(x0, 0.0, -z1)
                glEnd()
        
        if self.boardList is not None:
            glEndList()
            glCallList(self.boardList)
        
    def drawSquares(self):
        """Draw the board squares for picking"""
        
        # draw the floor squares
        for u in [0, 1, 2, 3, 4, 5, 6, 7]:
            glPushName(u)

            for v in [0, 1, 2, 3, 4, 5, 6, 7]:
                glPushName(v)

                # Draw square
                glBegin(GL_QUADS)
                x0 = BOARD_BORDER + (u * SQUARE_WIDTH)
                x1 = x0 + SQUARE_WIDTH
                z0 = BOARD_BORDER + (v * SQUARE_WIDTH)
                z1 = z0 + SQUARE_WIDTH

                glVertex3f(x0, 0.0, -z0)
                glVertex3f(x1, 0.0, -z0)
                glVertex3f(x1, 0.0, -z1)
                glVertex3f(x0, 0.0, -z1)
                glEnd()

                glPopName()
            glPopName()
        
    def drawPieces(self):
        """Draw the pieces in the scene"""
        glEnable(GL_TEXTURE_2D)

        for piece in self.pieces:
            glPushMatrix()
            glTranslatef(piece.pos[0], piece.pos[1], piece.pos[2])

            # Draw the model
            piece.draw()

            glPopMatrix()
        
        glDisable(GL_TEXTURE_2D)

    def transformBoard(self):
        """Perform the board transform"""
        # Rotate the board
        glRotatef(self.boardAngle, 0.0, 1.0, 0.0)

        # Offset board so centre is (0.0,0.0)
        glTranslatef(-OFFSET, 0.0, OFFSET)
