# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - Slideshow action

    Usage - Allows treatment of a wiki page as a set of slides.  Displays a
            single slide at a time, along with a navigation aid.

            A slide show page looks like this:

                general introduction or comments
                = Slide 1 title =
                Slide 1 contents
                = Slide 2 title =
                Slide 2 contents
                ...
                = Final slide title =
                Final slide contents

            The slideshow action takes a parameter 'slidenumber', which is
            the (1-based) number of the slide to display.  The display uses
            the large screen 'projection' media.

            This action adds two sets of navigation aids for the slides:
            1) some javascript, so that mouse clicks or space bar move to the
               next page
            2) a navigation footer, similar to the [[Navigation]] macro

    History
    1.0 2005-03-25 First version
    1.1 2005-03-30 Revised thanks to some comments from NirSoffer.
                   Parse H1 tags instead of using the [[Slide]] macro.
                   Improve navigation.
                           
    @copyright: 2005 Jim Clark
    @license: GNU GPL, see COPYING for details.
"""

import re
from MoinMoin import config, wikiutil
from MoinMoin.Page import Page
from MoinMoin.parser.text_moin_wiki import Parser
from MoinMoin.formatter.text_html import Formatter

header_re = r"^(?P<heading>\s*(?P<hmarker>=)\s(?P<htext>.*)\s(?P=hmarker))$"

class slideshow:
    script_template = """
<script type="text/javascript"><!--
function firstSlide() {window.location="%s"}
function prevSlide() {window.location="%s"}
function nextSlide() {window.location="%s"}
function lastSlide() {window.location="%s"}
function handleKey(e) {
    var key;
    if (e == null) {key = event.keyCode} //IE
    else {if (e.altKey || e.ctrlKey) {return true} key = e.which} // Mozilla
    switch(key) {
        case 49: firstSlide(); break //'1'
        case 32: nextSlide(); break  //space
        case 60: prevSlide(); break  //'<'
        case 62: nextSlide(); break  //'>'
        default:
    }
}
document.onkeypress = handleKey
//-->
</script>"""

    def __init__(self, pagename, request):
        self.pagename = pagename
        try:
            self.slidenumber = int(request.form.get('slidenumber', [1])[0])
        except ValueError:
            self.slidenumber = 1
        request.formatter = Formatter(request)
        request.page = Page(request, pagename)
        request.formatter.page = request.page
        self.request = request
        self.page = self.request.page
        self.formatter = self.request.formatter
        self.body = self.page.get_raw_body()
        self._parseslides()

    def _parseslides(self):
        """ Parse slide content from the base page.  slideinfo is a tuple
        of (title, startcharacter, endcharacter) representing each slide.
        The slides are separated by H1 markers.
        """
        self.slideinfo = []
        title = ""
        h_pattern = re.compile(header_re, re.MULTILINE)
        laststart = 0
        for match in h_pattern.finditer(self.body):
            if laststart:
                self.slideinfo.append((title, laststart, match.start()))
            title = match.group('htext').strip()
            laststart = match.start()
        if laststart:
            self.slideinfo.append((title, laststart, len(self.body)))


    def slideurl(self, slidenumber):
        """ Return a url for a link to another slide based on this page
        """
        return "%s/%s?action=Slideshow&slidenumber=%d" % \
               (self.request.getScriptname(), self.pagename, slidenumber)

    def navigationscript(self):
        """ Return a section of javascript for capturing mouse button and
        keyboard events and using for slide navigation.
        """
        urls = (self.slideurl(1),
                self.slideurl(self.slidenumber - 1),
                self.slideurl(self.slidenumber + 1),
                self.slideurl(len(self.slideinfo) - 1))
        return (self.script_template % urls)

            
    def _makeslidelink(self, i, text=None):
        """ Return a URL link to slide number 'i'.  The url title will be the
        slide number and the title attribute will be the slide title.
        """
        if text == None:
            text = str(i)
        if i == self.slidenumber:
            return text
        else:
            return self.page.link_to(self.request, text=text, \
                         querystr='action=Slideshow&slidenumber=%d' % i, \
                         attrs='title="%s"' % self.slideinfo[i-1][0])
        
    def navigationtable(self):
        """ Return an HTML table for navigating between slides
        """

        # setup a start and end range around the current slide - up to 20
        # slides will be displayed
        slidestart = 1
        slideend = len(self.slideinfo)
        if slideend > 20:
            if self.slidenumber > 10:
                slideend = min(self.slidenumber + 10, slideend)
                slidestart = slideend - 19           
            else:
                slideend = 20
        
        # Links to the parent page, and the first and previous slides
        items = []
        items.append(self.page.link_to(self.request, text='^',
                     attrs='title="Stop Slideshow"'))
        items.append(self._makeslidelink(1, '|<'))
        items.append(self._makeslidelink(max(self.slidenumber - 1, 1), '<<'))

        # Numbered slide links
        for i in range(slidestart, slideend + 1):
            items.append(self._makeslidelink(i))

        # Links to the next and last slides
        items.append(self._makeslidelink(min(self.slidenumber + 1,
                                             len(self.slideinfo)), '>>'))
        items.append(self._makeslidelink(len(self.slideinfo), '>|'))
        
        return self.formatter.table(1, {'tableclass': 'navigation'}) + \
               self.formatter.table_row(1) + \
               self.formatter.table_cell(1) + \
               "  ".join(items) + \
               self.formatter.table_cell(0) + \
               self.formatter.table_row(0) + \
               self.formatter.table(0)

    def render(self):
        """ Render a slide from a page """
        request = self.request

        # Non-existant slide requested.
        if self.slidenumber < 0 or self.slidenumber > len(self.slideinfo):
            # request.emit_http_headers()
            request.page.send_page(msg='End of slides') 
            return
        
        # request.emit_http_headers()
        request.setContentLanguage(request.lang)
        
        # Send the header in 'projection' mode - for large print and no headings
        # Q. Would it be better to use the slide title instead of the overall
        # page title?
        request.theme.send_title(self.page.split_title(force=1),
                                 print_mode=1, media='projection')

        # Extract the requested slide, and write it as a page in its own right,
        # along with the navigation aids
        request.write(self.navigationscript())
        request.write(request.formatter.startContent("content"))
        title, start, end = self.slideinfo[self.slidenumber - 1]
        parser = Parser(self.body[start:end], request)
        parser.format(request.formatter)
        request.write(self.navigationtable())
        request.write(request.formatter.endContent())
        request.theme.send_footer(self.pagename, print_mode=1)

def execute(pagename, request):
    slideshow(pagename, request).render()
