#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import os;
import string;
from xml.dom import minidom, Node




######################################################################
#                                                                    #
#  Pool configuration                                                #
#                                                                    #
######################################################################
POOLS_FILES             = [ 'data/pools_bisson.txt'    ,
                            'data/pools_rene.txt'      ,
                            'data/pools_veillette.txt' ]
CATEGORIES_FORWARD_FILE = 'data/choices_forwards.txt'
CATEGORIES_GOALIE_FILE  = 'data/choices_goalies.txt'
POINTS_FILE             = 'data/content.xml'
POOL_PREFIX             = 'pool'
PLAYER_PREFIX           = 'player'
OUTPUT_DIRECTORY        = 'pool'
OUTPUT_FILES            = [ ('index'      , 'Accueil')    , \
                            ('Ranking'    , 'Meneurs')    , \
                            ('Popular'    , 'Populaires') , \
                            ('Bests'      , 'Payants')    , \
                            ('Categories' , 'Catégories') , \
                            ('Contact'    , 'Contact') ]




######################################################################
#                                                                    #
#  Web configuration                                                 #
#                                                                    #
######################################################################
WEB_DIRECTORY     = 'web'
CSS_FILE_NAME     = 'styles.css'
LOGO_FILE_NAME    = 'logo.gif'
STATS_FILE_NAME   = 'stats.xls'
FILES_TO_COPY     = [ CSS_FILE_NAME   ,
                      LOGO_FILE_NAME  ,
                      STATS_FILE_NAME ,
                      '.htaccess' ];
HOME_PAGE_FILE    = WEB_DIRECTORY + '/welcome.html'
CONTACT_PAGE_FILE = WEB_DIRECTORY + '/contact.html'
POOLS_NB_COLUMNS  = 3
RESULTS_FILE_NAME = 'Results.html'





######################################################################
#                                                                    #
#  Exceptions                                                        #
#                                                                    #
######################################################################

class SpecificException(Exception):
    """This is the base exception for this module"""

class OpenException(SpecificException):
    """This exception is raised when a file is not found.

    Attributes:
        fileName  : The name of the file
    """

    def __init__(self, fileName):
        self.fileName = fileName

    def __str__(self):
        return 'Could not open "%s"' % self.fileName

class BadFormatException(SpecificException):
    """This exception is raised when a file is not formatted
    correctly.

    Attributes:
        fileName  : The name of the file
        lineNumber: The line Number
    """

    def __init__(self, fileName, lineNumber):
        self.fileName   = fileName
        self.lineNumber = lineNumber

    def __str__(self):
        return '%s:%d: Error in format' % (self.fileName , self.lineNumber)

class MessageException(SpecificException):
    """This exception is raised when an error has occured and we want
    to pass a string as the message.
    
    Attributes:
        message: A message describing what went wrong
    """

    def __init__(self, message):
        self.message = message

    def __str__(self):
        return "Error: %s" % self.message




######################################################################
#                                                                    #
#  Classes                                                           #
#                                                                    #
######################################################################

class Player:
    """This is the base class for information about a player.  It will
    be specialized by the goaler class and by the forward class."""
    
    def __init__(self, definition, doc):
        lineParsed = definition.split(';')
        self.name = lineParsed[0].strip()
        self.team = lineParsed[1].strip()

        if len(lineParsed) == 3:
            # No different name specified for validation
            nameSplit = self.name.split()
            if len(nameSplit) != 2:
                raise MessageException("'" + self.name + \
                                       "' (in Categories) is not formatted correctly.")

            self.validationName = nameSplit[1].strip() + " " + nameSplit[0].strip()
        elif len(lineParsed) == 4:
            # A different name specified for validation
            self.validationName = lineParsed[3].strip()
        else:
            # Bad string
            raise MessageException("Error reading player '" + self.name + "'.")

        self.build(lineParsed[2].strip(), doc)

    def validateName(self, name):
        # Make sure it is the right player
        if self.validationName != name.strip():
            raise MessageException("'" + self.name + \
                                   "' stats are misplaced.")

    teamSheets = { "Dra" : "Dragons"   ,
                   "Fro" : "Frost"     ,
                   "Ott" : "Nationals" ,
                   "Pog" : "Pogos"     ,
                   "L'A" : "Sphinx"    ,
                   "Tit" : "Titans" }




class Forward(Player):
    """This is the class handling the non-goalie player specific
    tasks."""

    def build(self, infoString, doc):
        """This function reads the info needed to compute the stats
        for a forward player."""

        if infoString == "VIDE":
            self.goals   = 0
            self.assists = 0
            self.points  = 0
        else:
            row = string.atoi(infoString)
            name = getContent(doc, Player.teamSheets[self.team], row, 'b').encode("iso-8859-1")
            self.validateName(name)
            
            self.goals = string.atoi(getContent(doc,
                                                Player.teamSheets[self.team],
                                                row,
                                                'e'))
            self.assists = string.atoi(getContent(doc,
                                                  Player.teamSheets[self.team],
                                                  row,
                                                  'f'))
            self.points = self.goals + self.assists

    def getPointsDetail(self):
        return [ ("Buts"   , self.goals   , 1) ,
                 ("Passes" , self.assists , 1) ]




class Goalie(Player):
    """This is the class handling the goalie player specific tasks."""

    def build(self, infoString, doc):
        """This function reads the info needed to compute the stats
        for a forward player."""

        if infoString == "VIDE":
            self.goals    = 0
            self.assists  = 0
            self.wins     = 0
            self.losses   = 0
            self.OTwins   = 0
            self.OTlosses = 0
            self.ties     = 0
            self.shutouts = 0
            self.points   = 0
        else:
            # Goals and assists
            row = string.atoi(infoString.split()[0])
            name = getContent(doc, "Goaler", row, 'b').encode("iso-8859-1")
            self.validateName(name)
            
            self.goals   = string.atoi(getContent(doc, "Goaler", row, 'j'))
            self.assists = string.atoi(getContent(doc, "Goaler", row, 'k'))

            # Goalie-specific stuff
            row = string.atoi(infoString.split()[1])
            name = getContent(doc, "Goaler", row, 'b').encode("iso-8859-1")
            self.validateName(name)
            
            self.wins     = string.atoi(getContent(doc, "Goaler", row, 'd'))
            self.losses   = string.atoi(getContent(doc, "Goaler", row, 'e'))
            self.OTwins   = string.atoi(getContent(doc, "Goaler", row, 'f'))
            self.OTlosses = string.atoi(getContent(doc, "Goaler", row, 'g'))
            self.ties     = string.atoi(getContent(doc, "Goaler", row, 'h'))
            self.shutouts = string.atoi(getContent(doc, "Goaler", row, 'i'))

            self.points = self.goals    * 1 + \
                          self.assists  * 1 + \
                          self.wins     * 3 + \
                          self.losses   * 0 + \
                          self.OTwins   * 2 + \
                          self.OTlosses * 1 + \
                          self.ties     * 1 + \
                          self.shutouts * 3

    def getPointsDetail(self):
        return [ ("Buts"                      , self.goals    , 1) ,
                 ("Passes"                    , self.assists  , 1) ,
                 ("Victoires"                 , self.wins     , 3) ,
                 ("Victoires en prolongation" , self.OTwins   , 2) ,
                 ("Défaites en prolongation"  , self.OTlosses , 1) ,
                 ("Nulles"                    , self.ties     , 1) ,
                 ("Blanchissages"             , self.shutouts , 3) ]




######################################################################
#                                                                    #
#  Functions                                                         #
#                                                                    #
######################################################################

def readLineWithoutEnter(file):
    """This function reads a line from the file and removes the
    trailing enter if there is one.
    """
    returnValue = file.readline()
    if returnValue[-1] == '\n':
        returnValue = returnValue[:-1]
    return returnValue




def getNode(node, nodeName, criteria=lambda x:True, i=0):
    """This function returns the i-th node that has the given name."""

    count = 0
    for child in node.childNodes:
        repeatedAttribute = child.attributes.get("table:number-columns-repeated")
        if repeatedAttribute != None:
            repeatCount = string.atoi(repeatedAttribute.value)
        else:
            repeatedAttribute = child.attributes.get("table:number-rows-repeated")
            if repeatedAttribute != None:
                repeatCount = string.atoi(repeatedAttribute.value)
            else:
                repeatCount = 1

        while repeatCount > 0:
            if child.nodeName == nodeName and criteria(child):
                if count == i:
                    return child;
                else:
                    count = count + 1
            repeatCount = repeatCount - 1

    raise MessageException("'" + nodeName + "' not found in node '" + \
                           node.nodeName + "'")




def getContent(node, sheetName, row, column):
    """This function returns the content of a cell in the Excel
    sheets."""
    
    if node.documentElement.nodeName != "office:document-content":
        raise MessageException("Root node is not what it is supposed to be.")

    bodyNode = getNode(node.documentElement, "office:body")
    sheetNode = getNode(bodyNode,
                        "table:table",
                        lambda x:x.attributes["table:name"].value == sheetName)

    rowNode = getNode(sheetNode, "table:table-row", i=row-1)
    cellNode = getNode(rowNode,
                       "table:table-cell",
                       i=ord(column.lower()) - ord('a'))

    content = []
    for child in getNode(cellNode, "text:p").childNodes:
        if child.nodeType == Node.TEXT_NODE:
            content.append(child.nodeValue)

    return string.join(content)



def getPools(fileName, returnValue):
    """This function gets the name of the pool participants and
    returns a dictionnary with the pools.
    """

    try:
        inputFile = open(fileName, 'r')
    except IOError:
        raise OpenException(fileName)

    try:
        lineNumber = 1
        while 1:
            # Name line
            name = inputFile.readline()
            if name == "":
                break

            if name[-1] == '\n':
                name = name[:-1]

            lineNumber = lineNumber + 1

            # Choices line
            choices = inputFile.readline()
            if choices == "":
                break

            try:
                choices = map(int,string.split(choices))
            except ValueError:
                raise BadFormatException(fileName, lineNumber)

            lineNumber = lineNumber + 1

            # Must be followed by an empty line
            endofline = inputFile.readline()
            if not (endofline == "" or endofline == '\n'):
                raise BadFormatException(fileName, lineNumber)

            lineNumber = lineNumber + 1

            while name in returnValue.keys():
                print '"' + name + \
                      '" already has a pool.  Appending "_" to the name."'
                name = name + '_'

            # Everything went good
            returnValue[name] = choices

    finally:
        inputFile.close()

    return returnValue




def getCategories(fileName, playerClass):
    """This function gets the players in each category.
    """

    returnValue = []

    try:
        inputFile = open(fileName, 'r')
    except IOError:
        raise OpenException(fileName)

    try:

        try:
            doc = minidom.parse(POINTS_FILE)
        except:
            raise MessageException("Unable to parse '" + POINTS_FILE + "' correctly.")

        # Make sure everything is OK with the Excel file
        for sheet in Player.teamSheets.values():
            if (getContent(doc, sheet, 1, 'b').encode("iso-8859-1") != "Joueurs"):
                raise MessageException("The Excel structure changed ('Joueurs' " + \
                                       sheet + ").")
            if (getContent(doc, sheet, 1, 'e').encode("iso-8859-1") != "B"):
                raise MessageException("The Excel structure changed ('Buts' )." + \
                                       sheet + ").")
            if (getContent(doc, sheet, 1, 'f').encode("iso-8859-1") != "A"):
                raise MessageException("The Excel structure changed ('Aide' )." + \
                                       sheet + ").")

        if getContent(doc, "Goaler", 1, 'b') != "Gardiens":
            raise MessageException("The Excel structure changed ('Gardiens').")
        if getContent(doc, "Goaler", 1, 'j') != "B":
            raise MessageException("The Excel structure changed ('Buts').")
        if getContent(doc, "Goaler", 1, 'k') != "P":
            raise MessageException("The Excel structure changed ('Passes').")

        if getContent(doc, "Goaler", 18, 'b') != "Gardiens":
            print 'DEBUT'
            print getContent(doc, "Goaler", 18, 'b').encode("iso-8859-1")
            print 'FIN'
            raise MessageException("The Excel structure changed ('Gardiens').")
        if getContent(doc, "Goaler", 18, 'd') != "V":
            raise MessageException("The Excel structure changed ('Victoires').")
        if getContent(doc, "Goaler", 18, 'e') != "D":
            raise MessageException("The Excel structure changed ('Défaites').")
        if getContent(doc, "Goaler", 18, 'f') != "VP":
            raise MessageException("The Excel structure changed ('Victoires en prolongation').")
        if getContent(doc, "Goaler", 18, 'g') != "DP":
            raise MessageException("The Excel structure changed ('Défaites en prolongation').")
        if getContent(doc, "Goaler", 18, 'h') != "N":
            raise MessageException("The Excel structure changed ('Nulles').")
        if getContent(doc, "Goaler", 18, 'i') != "Blan":
            raise MessageException("The Excel structure changed ('Blanchissages').")
            

        lineNumber = 1
        while 1:
            # Name of the category
            name = inputFile.readline()
            if name == "":
                break

            if name[-1] == '\n':
                name = name[:-1]

            lineNumber = lineNumber + 1

            # Name of the players
            players = []
            line = inputFile.readline()
            while not (line == '\n' or line == ""):
                players.append(playerClass(line, doc))
                line = inputFile.readline()

            returnValue.append((name, players))

            # Are we done
            if line == "":
                break

            lineNumber = lineNumber + 1

    finally:
        inputFile.close()

    return returnValue




def checkCoherence(pools, categories):
    """This function checks that the choices made by the poolers are
    coherent with the categories."""
    
    # Check that the pools are valid
    for pooler in pools.keys():
        if len(pools[pooler]) != len(categories):
            raise MessageException('"%s" has not the right number of picks.' \
                                     % pooler)

        # Check that all choices are valid
        i = 0
        while (i < len(categories)):
            if len(categories[i][1]) < 1:
                raise MessageException('"%d" category has no player')

            choice = pools[pooler][i]
            if (choice < 1 or choice > len(categories[i][1])):
                raise MessageException('"%s" choice #%d is not valid' \
                                         % (pooler , i + 1))
            i = i + 1




def generateFile(fileName, function, *arguments):
    """This is the generic function that outputs a certain file which
    content is generated by a given function"""

    try:
        outputFile = open(fileName, 'w')
    except IOError:
        raise OpenException(fileName)

    try:
        # Beginning of the file
        outputFile.write('<?xml version="1.0" encoding="iso-8859-1"?>\n' + \
                         '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n' + \
                         '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n' + \
                         '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">\n')

        # Head section
        outputFile.write("<head>\n"                               + \
                         '<link rel="stylesheet" type="text/css"' + \
                         ' href="' + CSS_FILE_NAME + '" />\n'     + \
                         "<title> Pools de la LBGSÉM </title>\n"  + \
                         "</head>\n")

        outputFile.write('<body>\n<table class="layout">\n\n');

        # Header part
        outputFile.write('<tr>\n' + \
                         '<td colspan="2" id="header">\n' + \
                         '<img src="' + LOGO_FILE_NAME + '" alt="logo"/>\n' + \
                         '<div>' + \
                         'Site du pool de ballon sur glace de la ligue élite 2006-2007\n' + \
                         '</div></td>\n' + \
                         '</tr>\n\n')

        # Menu part
        outputFile.write('<tr>\n<td id="menu">\n')

        i = 1
        for fileToGenerate in OUTPUT_FILES:
            # We have to add a bottom border for the last item
            if i == len(OUTPUT_FILES):
                htmlClass = ' menuitemlast';
            else:
                htmlClass = '';

            # To guess which file we are generating to highlight the chosen menu item
            if (fileName == (OUTPUT_DIRECTORY  + '/' + \
                             fileToGenerate[0] + ".html")):
                htmlClass = htmlClass + ' selectedmenuitem'

            outputFile.write('<div class="menuitem' + htmlClass + \
                             '"><a href="' + fileToGenerate[0]  + \
                             '.html">' + fileToGenerate[1]      + \
                             '</a></div>\n')
            i = i + 1

        outputFile.write('</td>\n\n<td id="content">\n')

        # Content generation
        function(outputFile, *arguments)

        outputFile.write("</td>\n</tr>\n\n")

        # Footer part
        outputFile.write('<tr>\n<td id="footer" colspan="2">\n'  + \
                         'Commentaires? Suggestions? Erreurs?\n' + \
                         '<a href="Contact.html">\n'             + \
                         'Écrivez-nous</a>\n</td>\n</tr>\n'      + \
                         '</table>\n\n')

        # Google Analytics
        outputFile.write('<script src="http://www.google-analytics.com/urchin.js" ' +
                         'type="text/javascript">\n' +
                         '</script>\n' +
                         '<script type="text/javascript">\n' +
                         '_uacct = "UA-934124-1";\n' +
                         'urchinTracker();\n' +
                         '</script>\n\n');

        # End of the file
        outputFile.write("</body>\n</html>\n")

    finally:
        outputFile.close()




def getPlayerLocation(categories, player):
    """This function returns information about the location of a given
    player in the categories of the pool."""
    
    playerId   = 0
    categoryId = 0
    for category in categories:
        categoryPlayerId = 0
        for comparaisonPlayer in category[1]:
            if player.name == comparaisonPlayer.name:
                return (categoryId, categoryPlayerId, playerId)
            playerId         = playerId + 1
            categoryPlayerId = categoryPlayerId + 1

        categoryId = categoryId + 1

    raise MessageException('Player "' + player.name + '" not found.')




def getPlayerString(player, categories):
    """This function returns a string describing a given player, with
    the link to its page."""

    return '<a href="' + PLAYER_PREFIX                   + \
           str(getPlayerLocation(categories, player)[2]) + \
           '.html">' + player.name + " (" + player.team + ")</a>"




def getPoolerString(pools, poolerName):
    """This funtion returns a string describing a given pooler, with
    the link to its page."""

    return '<a href="' + POOL_PREFIX           + \
           str(pools.keys().index(poolerName)) + \
           '.html">' + poolerName + '</a>'




def generatePooler(outputFile, pools, categories, poolerName):
    """This function generate the file corresponding to a given
    pooler, i.e. the choices made."""

    # File containing the pool
    outputFile.write("<h1> " + poolerName + " </h1>\n"               + \
                     '<table class="sortedtable" cellspacing="0">\n' + \
                     '<tr>'                                          + \
                     '<th>Catégorie</th>'                            + \
                     '<th class="separatorcolumn"></th>'             + \
                     '<th class="namecell">Nom</th>'                 + \
                     '<th class="separatorcolumn"></th>'             + \
                     "<th>Points</th></tr>\n")

    categoryId = 0
    poolTotal  = 0
    while categoryId < len(categories):
        if categoryId % 2 != 0:
            htmlClass = 'oddrow'
        else:
            htmlClass = 'evenrow'

        category       = categories[categoryId]
        player         = category[1][pools[poolerName][categoryId] - 1]

        outputFile.write('<tr class="' + htmlClass + '">'             + \
                         '<td>' + category[0] + '</td><td></td>'      + \
                         '<td>' + getPlayerString(player, categories) + \
                         '</td><td></td>' + '<td class="rightcell">'  + \
                         str(player.points) + '</td></tr>\n')

        poolTotal = poolTotal + player.points

        categoryId = categoryId + 1

    outputFile.write('<tr class="totalrow"><td>Total</td>' + \
                     ('<td></td>' * 3)                     + \
                     '<td class="rightcell">' + str(poolTotal) + '</td></tr>')

    outputFile.write('</table>')

    outputFile.write('<hr/><h1> Catégories </h1>\n'                        + \
                     '<p>Voici la liste des différentes catégories du '    + \
                     'pool, avec les joueurs qui les composent ainsi que ' + \
                     'leur performance dans le pool.  Vous pouvez y voir ' + \
                     'les joueurs choisis et ainsi comparer avec les '     + \
                     'meilleurs joueurs de chaque catégorie.</p>')

    categoryId = 0
    for category in categories:
        chosenPlayerId = pools[poolerName][categoryId]

        outputFile.write('<br></br><h3>' + category[0] + '</h3>\n')

        outputFile.write('<table class="sortedtable" cellspacing="0">\n' + \
                         '<tr><th class="namecell">Nom</th>'             + \
                         '<th class="separatorcolumn"></th>'             + \
                         "<th>Points</th></tr>\n")

        oddRow = True
        playerId = 1
        for player in category[1]:
            if oddRow:
                htmlClass = 'oddrow'
            else:
                htmlClass = 'evenrow'

            if playerId == chosenPlayerId:
                htmlClass = htmlClass + " chosenplayer"

            outputFile.write('<tr class="' + htmlClass + '">'             + \
                             '<td>' + getPlayerString(player, categories) + \
                             '</td><td></td><td class="rightcell">'       + \
                             str(player.points) + "</td></tr>\n")

            oddRow = not oddRow
            playerId = playerId + 1

        outputFile.write('</table>')

        categoryId = categoryId + 1




def generatePlayer(outputFile, pools, categories, player):
    """This funtion generates the file describing a given player."""

    outputFile.write('<h1>' + player.name + '</h1>')

    categoryId , categoryPlayerId , playerId = getPlayerLocation(categories,
                                                                 player)

    # Output its points detail
    outputFile.write('<table class="sortedtable" cellspacing="0">\n' + \
                     '<tr><th class="namecell">Raison</th>'          + \
                     '<th class="separatorcolumn"></th>'             + \
                     '<th colspan="5">Points</th></tr>\n')

    oddRow = True
    for reason in player.getPointsDetail():
        if oddRow:
            htmlClass = 'oddrow'
        else:
            htmlClass = 'evenrow'

        outputFile.write('<tr class="' + htmlClass + '">'          + \
                         '<td>' + reason[0] + '</td><td></td><td>' + \
                         str(reason[1]) + '</td><td>x</td><td>'    + \
                         str(reason[2]) + '</td><td>=</td>'        + \
                         '<td class="rightcell">'                  + \
                         str(reason[1]*reason[2]) + '</td></tr>\n')

        oddRow = not oddRow

    outputFile.write('<tr class="totalrow"><td>Total</td>'         + \
                     ('<td></td>' * 5)                             + \
                     '<td class="rightcell">' + str(player.points) + \
                     '</td></tr>')

    outputFile.write('</table>\n')


    # Output its category
    outputFile.write('<br></br><h3>' + category[0] + '</h3>\n')

    outputFile.write('<table class="sortedtable" cellspacing="0">\n' + \
                     '<tr><th class="namecell">Nom</th>'             + \
                     '<th class="separatorcolumn"></th>'             + \
                     "<th>Points</th></tr>\n")

    oddRow = True
    playerIdIt = 0
    for player in category[1]:
        if oddRow:
            htmlClass = 'oddrow'
        else:
            htmlClass = 'evenrow'

        if playerIdIt == categoryPlayerId:
            htmlClass = htmlClass + " chosenplayer"

        outputFile.write('<tr class="' + htmlClass + '">'             + \
                         '<td>' + getPlayerString(player, categories) + \
                         '</td><td></td><td class="rightcell">'       + \
                         str(player.points) + "</td></tr>\n")

        oddRow = not oddRow
        playerIdIt = playerIdIt + 1

    outputFile.write('</table>')


    # Who picked this player
    poolers = []
    for poolerName in pools.keys():
        if pools[poolerName][categoryId] == categoryPlayerId + 1:
            poolers.append(poolerName)

    if len(poolers) == 0:
        outputFile.write("<p>Ce joueur n'a été choisi par aucun des " + \
                         "participants du pool.</p>\n")
    else:
        outputFile.write("<p>Ce joueur a été choisi par:</p>\n<ul>\n")
        for poolerName in poolers:
            outputFile.write('<li>' + \
                             getPoolerString(pools, poolerName) + \
                             '</li>\n')
        outputFile.write('</ul>\n')




def generateRanking(outputFile, pools, categories):
    """This function generates the ranking of the pool"""

    # Compute the list of leaders
    rankings = []
    for pooler in pools.keys():
        total = 0
        i = 0
        for choice in pools[pooler]:
            total = total + categories[i][1][choice-1].points
            i = i + 1
                            
        rankings.append((pooler, total))

    rankings.sort(cmp = lambda x, y: cmp(x[1],y[1]), reverse=True)


    # Generate a file with the leaders
    outputFile.write("<h1> Meneurs </h1>\n"                          + \
                     '<table class="sortedtable" cellspacing="0">\n' + \
                     '<tr><th>Rang</th>'                             + \
                     '<th>Nom</th>'                                  + \
                     '<th class="separatorcolumn"></th>'             + \
                     "<th>Points</th></tr>\n")

    rankNumber = 1
    for rank in rankings:
        poolerName = str(rank[0])
        if rankNumber % 2 != 0:
            htmlClass = 'oddrow'
        else:
            htmlClass = 'evenrow'

        outputFile.write('<tr class="' + htmlClass + '">'                 + \
                         '<td class="centercell">' + str(rankNumber)      + \
                         '</td><td>' + getPoolerString(pools, poolerName) + \
                         '</td><td></td><td class="rightcell">'           + \
                         str(rank[1]) + "</td></tr>\n")

        rankNumber = rankNumber + 1

    outputFile.write('</table>')




def generatePopular(outputFile, pools, categories):
    """This function generates the list of the most popular players in
    the pool"""

    # FIXME: The algorithm in this function could be improved to be
    # more efficient by looping on the pools and incrementing the "ref
    # count" for the chosen player instead of count how often a player
    # has been picked

    # Compute the list of number of picks by players
    populars = []
    categoryId = 0
    for category in categories:

        playerId = 1
        for player in category[1]:
            nbPicks = 0
            for choice in pools.values():
                if (choice[categoryId] == playerId):
                    nbPicks = nbPicks + 1

            populars.append((player, nbPicks))

            playerId = playerId + 1

        categoryId = categoryId + 1

    populars.sort(cmp = lambda x, y: cmp(x[1],y[1]), reverse=True)


    # Generate a file with the most popular players
    outputFile.write("<h1> Joueurs les plus populaires </h1>\n"      + \
                     '<table class="sortedtable" cellspacing="0">\n' + \
                     '<tr><th>Rang</th>'                             + \
                     '<th>Nom</th>'                                  + \
                     '<th class="separatorcolumn"></th>'             + \
                     "<th>Nombre de fois choisi</th></tr>\n")

    rankNumber = 1
    for player in populars:
        if rankNumber % 2 != 0:
            htmlClass = 'oddrow'
        else:
            htmlClass = 'evenrow'

        outputFile.write('<tr class="' + htmlClass + '">'            + \
                         '<td class="centercell">' + str(rankNumber) + \
                         '</td><td>'                                 + \
                         getPlayerString(player[0], categories)      + \
                         '</td><td></td><td class="centercell">'     + \
                         str(player[1]) + '</td></tr>\n')

        rankNumber = rankNumber + 1

    outputFile.write('</table>')




def generateBests(outputFile, pools, categories):
    """This function generates the list of the most rewarding players in
    the pool"""

    # FIXME: Something better than this could surely be done
    bests = []
    for category in categories:
        for player in category[1]:
            bests.append(player)

    bests.sort(cmp = lambda x, y: cmp(x.points,y.points), reverse=True)


    # Generate a file with the best players
    outputFile.write("<h1> Joueurs les plus payants </h1>\n"         + \
                     '<table class="sortedtable" cellspacing="0">\n' + \
                     '<tr><th>Rang</th>'                             + \
                     '<th>Nom</th>'                                  + \
                     '<th class="separatorcolumn"></th>'             + \
                     "<th>Points</th></tr>\n")

    rankNumber = 1
    for player in bests:
        if rankNumber % 2 != 0:
            htmlClass = 'oddrow'
        else:
            htmlClass = 'evenrow'

        outputFile.write('<tr class="' + htmlClass + '">'            + \
                         '<td class="centercell">' + str(rankNumber) + \
                         '</td><td>'                                 + \
                         getPlayerString(player, categories)         + \
                         '</td><td></td><td class="rightcell">'      + \
                         str(player.points) + "</td></tr>\n")

        rankNumber = rankNumber + 1

    outputFile.write('</table>')




def generateCategories(outputFile, pools, categories):
    """This function generates the list of the most rewarding players in
    the pool"""

    # FIXME: Something better than this could surely be done
    bests = []
    for category in categories:
        for player in category[1]:
            bests.append(player)

    bests.sort(cmp = lambda x, y: cmp(x.points,y.points), reverse=True)


    # Generate a file with the best players
    outputFile.write('<h1> Catégories </h1>\n'                             + \
                     '<p>Voici la liste des différentes catégories du '    + \
                     'pool, avec les joueurs qui les composent ainsi que ' + \
                     'leur performance dans le pool.</p>')

    for category in categories:
        outputFile.write('<br></br><h3>' + category[0] + '</h3>\n')

        outputFile.write('<table class="sortedtable" cellspacing="0">\n' + \
                         '<tr><th class="namecell">Nom</th>'             + \
                         '<th class="separatorcolumn"></th>'             + \
                         "<th>Points</th></tr>\n")

        oddRow = True
        for player in category[1]:
            if oddRow:
                htmlClass = 'oddrow'
            else:
                htmlClass = 'evenrow'

            outputFile.write('<tr class="' + htmlClass + '">'             + \
                             '<td>' + getPlayerString(player, categories) + \
                             '</td><td></td><td class="rightcell">'       + \
                             str(player.points) + "</td></tr>\n")

            oddRow = not oddRow

        outputFile.write('</table>')




def generateindex(outputFile, *unused_args):
    """This function generates the home page of the pool web site by
    copying the content of an HTML file."""

    try:
        inputFile = open(HOME_PAGE_FILE, 'r')
    except IOError:
        raise OpenException(fileName)

    try:
        outputFile.write(inputFile.read())
    finally:
        inputFile.close()




def generateContact(outputFile, *unused_args):
    """This function generates the contact page of the pool web site
    by copying the content of an HTML file."""

    try:
        inputFile = open(CONTACT_PAGE_FILE, 'r')
    except IOError:
        raise OpenException(CONTACT_PAGE_FILE)

    try:
        outputFile.write(inputFile.read())
    finally:
        inputFile.close()




def generatePrinterFile(fileName, function, *arguments):
    """This is the generic function that outputs a certain
    printer-friendly file which content is generated by a given
    function"""

    try:
        outputFile = open(fileName, 'w')
    except IOError:
        raise OpenException(fileName)

    try:
        # Beginning of the file
        outputFile.write('<?xml version="1.0" encoding="iso-8859-1"?>\n' + \
                         '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n' + \
                         '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n' + \
                         '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">\n')

        # Head section
        outputFile.write("<head>\n"                               + \
                         "<title> Pools de la LBGSÉM </title>\n"  + \
                         "</head>\n")

        outputFile.write('<body>\n');

        # Content generation
        function(outputFile, *arguments)

        # Google Analytics
        outputFile.write('<script src="http://www.google-analytics.com/urchin.js" ' +
                         'type="text/javascript">\n' +
                         '</script>\n' +
                         '<script type="text/javascript">\n' +
                         '_uacct = "UA-934124-1";\n' +
                         'urchinTracker();\n' +
                         '</script>\n\n');

        # End of the file
        outputFile.write("</body>\n</html>\n")

    finally:
        outputFile.close()




def generateResults(outputFile, categories):
    """This function generate the printer-friendly file containing the
    results for each player in each category."""

    outputFile.write("<h1> Résultats </h1>\n")

    # Table containing the pool
    outputFile.write('<table border="1">')

    categoryId = 0
    while (categoryId < len(categories)):
        columnsOver = [False] * POOLS_NB_COLUMNS

        outputFile.write('<tr>\n')

        # Column header
        columnId = 0
        while (columnId < POOLS_NB_COLUMNS):
            realCategoryId = categoryId + columnId

            outputFile.write('<th colspan="2">')
            if (realCategoryId < len(categories)):
                outputFile.write(categories[realCategoryId][0])
            else:
                columnsOver[columnId] = True
            outputFile.write('</th>\n')

            columnId = columnId + 1

        outputFile.write('</tr>\n')

        # Now the players of the categories
        rowId = 0
        while (False in columnsOver):
            outputFile.write('<tr>\n')

            columnId = 0
            while (columnId < POOLS_NB_COLUMNS):
                realCategoryId = categoryId + columnId

                if not columnsOver[columnId]:
                    player       = categories[realCategoryId][1][rowId]

                    outputFile.write('<td>' + player.name + '</td><td>' + \
                                     str(player.points) + '</td>\n')

                    if rowId == len(categories[realCategoryId][1]) - 1:
                        columnsOver[columnId] = True
                else:
                    outputFile.write('<td></td><td></td>\n')

                columnId = columnId + 1

            outputFile.write('</tr>\n')

            rowId = rowId + 1

        categoryId = categoryId + POOLS_NB_COLUMNS

    outputFile.write('</table>')




def copyFile(srcFileName, destFileName):
    """This function simply copies a file."""

    try:
        srcFile = open(srcFileName, 'r')
        try:
            try:
                destFile = open(destFileName, 'w')
                try:
                    try:
                        destFile.write(srcFile.read())
                    except IOError:
                        raise MessageException('Error copying "%s" to "%s".' % \
                                               (srcFileName , destFileName))
                finally:
                    destFile.close()
            except IOError:
                raise OpenException(destFileName)
        finally:
            srcFile.close()

    except IOError:
        raise OpenException(srcFileName)





######################################################################

# To be able to use this file as a stand-alone and an importable
# module
if __name__ == '__main__':
    import sys;

    try:
        # Read the files
        pools = {}
        for poolsFile in POOLS_FILES:
            getPools(poolsFile, pools)
        categories = getCategories(CATEGORIES_FORWARD_FILE, Forward) + \
                     getCategories(CATEGORIES_GOALIE_FILE, Goalie)

        # Check that everything went OK
        checkCoherence(pools, categories)

        # Generate everything
        try:
            os.mkdir(OUTPUT_DIRECTORY)
        except:
            raise MessageException('Unable to create "%s" directory' \
                                   % OUTPUT_DIRECTORY)

        # One file per pooler
        poolId = 0
        for poolerName in pools.keys():
            generateFile(OUTPUT_DIRECTORY + '/' + \
                         POOL_PREFIX + str(poolId) + ".html",
                         generatePooler,
                         pools, categories, poolerName)

            poolId = poolId + 1

        # One file per player
        playerId = 0
        for category in categories:
            for player in category[1]:
                generateFile(OUTPUT_DIRECTORY + '/' + \
                             PLAYER_PREFIX + str(playerId) + ".html",
                             generatePlayer,
                             pools, categories, player)
                playerId = playerId + 1

        # The different rankings
        for fileToGenerate in OUTPUT_FILES:
            generateFile(OUTPUT_DIRECTORY + '/' +     \
                         fileToGenerate[0] + '.html',
                         eval('generate' + fileToGenerate[0]),
                         pools, categories)

        # The static files
        for fileName in FILES_TO_COPY:
            copyFile(WEB_DIRECTORY + '/' + fileName,
                     OUTPUT_DIRECTORY + '/' + fileName)

        # The "printer-friendly" pages
        generatePrinterFile(OUTPUT_DIRECTORY + '/Results.html',
                            generateResults,
                            categories)
        generatePrinterFile(OUTPUT_DIRECTORY + '/Leaders.html',
                            generateRanking,
                            pools, categories)

    except SpecificException , exception:
        print exception




# - Vérifier que le HTML est bien formé
# - Mettre que les tables puissent être ordonnées
# - Commenter les fonctiosn dans les classes
# - Faire le "pool idéal" est ceux qui l'ont choisi
