#format python
'''
    Simple Vote macro for MoinMoin.
    Author: Martin Stone <martins@evos.net>
    Hacked by: John Cocula (john@cocula.com)
               Thomas Waldmann

    Usage:
        [[Vote(pageName, voteTitle, candidate1, ...)]]

    E.g:
        [[Vote(MyBlog, Do polls work?, yes)]]
    
    Just like Martin's Vote macro, except you must specify the name of the
    page on which to vote "appears."  Without this information, when a user
    votes from an included page, after pressing the Vote button they are
    returned to the included page, not the main page.  Also different is
    that the current number of votes is hidden until the user has voted.
    Lastly, it is possible to have a poll that only has one choice, which
    is kind of funny.  The remainder of the description is Martin's:

    You can have multiple votes on one page (so long as the titles are unique).
    You can add candidates after a vote has started, but note that renaming 
    candidates will lose the votes (and not allow users to recast their votes).
    Changing the vote title creates a new vote.

    Vote data is stored in data_dir/pages/pagename/votes 
    (i.e. alongside the attachments directory for a page).

    You can customise the appearance by defining the following CSS styles:
        table.vote 
        td.votelogin (for "must login" message)
'''

import os, pickle
from MoinMoin import wikiutil
from MoinMoin.util import filesys

def makeFilename(thisPage, voteName):
    votesDir = wikiutil.getPagePath(thisPage, 'votes')
    if not os.path.exists(votesDir):
        filesys.makeDirs(votesDir)
    voteName = wikiutil.quoteFilename(voteName) + '.pik'
    return os.path.join(votesDir, voteName)

def loadVotes(thisPage, voteName):
    votesFile = makeFilename(thisPage, voteName)
    try:
        f = open(votesFile, 'r')
        return pickle.load(f)
    except: # XXX
        return {}

def saveVotes(thisPage, voteName, votes):
    votesFile = makeFilename(thisPage, voteName)
    f = open(votesFile, 'w')
    pickle.dump(votes, f)

def countVotes(votes):
    results = {}
    for v in votes.values():
        results.setdefault(v, 0)
        results[v] += 1
    return results

def execute(macro, args):
    args = args.split(",")
    args = map(str.strip, args)

    if len(args) < 3:
        return macro.formatter.rawHTML('<pre>[[Vote: Insufficient macro arguments]]</pre>')

    pageName = args[0]
    voteName = args[1]
    candidates = args[2:]

    form = macro.form
    request = macro.request
    if request.user.valid:
        voter = request.user.name
    else:
        voter = ''
    thisPage = macro.formatter.page
    thisPageName = thisPage.page_name
    votes = loadVotes(thisPageName, voteName)

    # votes are stored in a dictionary as {user: candidate} to avoid duplicate votes
    # (a voter could switch their vote but this UI doesn't expose that facility)
    if form.has_key('vote') and form.has_key('voteName') and voter:
        if form['voteName'][0] == voteName:
            votes[voter] = form['vote'][0]
            try:
               saveVotes(thisPageName, voteName, votes)
            except: # XXX
               return macro.formatter.rawHTML('<a id="voteform"><pre>[[Vote: failed to store vote]]</pre>')

    # generate dictionary {candidate: numvotes}
    results = countVotes(votes)

    hasVoted = voter in votes

    # spit out votes table (as part of a form if the user hasn't voted yet)
    html = ''
    if voter and not hasVoted:
        voteButton = '<td><input type="radio" name="vote" value="%s">vote</td>'
        html += '''
            <form method="get" action="%(url)s#voteform">\n
            <input type="hidden" name="voteName" value="%(voteName)s">
            ''' % {
                'url': thisPage.url(request),
                'voteName': voteName,
            }
    else:
        voteButton = ''

    html += '<a id="voteform"><table class="vote"><tr><th colspan="2">%s</th></tr>' % voteName

    for candidate in candidates:
        button = voteButton and (voteButton % candidate)
        if voter:
            if hasVoted:
                button = candidate
                count = results.setdefault(candidate, 0)
            else:
                button = '<td><input type="radio" name="vote" value="%s">%s</td>' % (candidate, candidate)
                count = '???'
        else:
            button = candidate
            count = '???'
        html += '<tr><td>%s</td><td>%s</td></tr>\n' % (button, count)

    if not voter:
        html += '<tr><td colspan="3" class="votelogin">You need to login to vote</td></tr>'

    html += '</table>\n'

    if voter and not hasVoted:
        html += '<input type="submit" value="Vote">\n</form>\n'
        
    return macro.formatter.rawHTML(html)

