# Pyntor::POD - Pyntor component for Plain Old Documentation
# -*- coding: utf-8 -*-
#               Perl meets Python, hump me!
# Copyright (C) 2006 Josef Spillner <josef@coolprojects.org>
# Published under GNU GPL conditions

import pygame
from pygame.locals import *

import re

class POD:
	def init(self, options):
		podfile = options["podfile"]

		f = open(podfile)
		lines = f.readlines()
		f.close()

		paragraphs = []
		tmp = []
		for line in lines:
			line = line.rstrip()
			if not line:
				paragraphs.append(tmp)
				tmp = []
			else:
				tmp.append(unicode(line, "utf-8"))
		if tmp != []:
			paragraphs.append(tmp)

		#print "POD paragraphs", paragraphs

		podcommands = (
			"pod",
			"cut",
			"head1",
			"head2",
			"head3",
			"head4",
			"over",
			"item",
			"back",
			"begin",
			"end",
			"for",
			"encoding",
			"cut"
		)

		rcommand = re.compile("^=(\w+)(?: ((?:\ ?\S+)+))?$")
		rverbatim = re.compile("^\s(.*)$")

		self.podparagraphs = []
		insidepod = False
		kind = None

		for paragraph in paragraphs:
			firstline = paragraph[0]

			mcommand = rcommand.match(firstline)
			mverbatim = rverbatim.match(firstline)
			if mcommand:
				# -> command paragraph
				insidepod = True
				kind = "command"

				keyword = mcommand.group(1)
				if keyword not in podcommands:
					print "(POD) Error: unknown pod command", keyword
					self.disabled = 1
					return
				if keyword == "pod":
					pass
				elif keyword == "cut":
					insidepod = False
			elif mverbatim:
				# -> verbatim paragraph
				kind = "verbatim"
			else:
				# -> normal paragraph
				kind = "ordinary"

			if insidepod:
				self.podparagraphs.append((kind, paragraph))

		#print "(POD) got", len(paragraphs), "paragraphs"
		#print "(POD) of which", len(self.podparagraphs), "are pods"
		#print self.podparagraphs

		self.scrollpos = 0
		self.scrollbottom = 0

		self.pages = 1

	def render(self, screen, page, globalpage):
		fore = (0, 0, 0)
		back = (230, 200, 140)

		surface = pygame.Surface((screen.get_width(), screen.get_height()))
		surface.fill(back)

		head1font = pygame.font.SysFont("Vera Sans", 42)
		head2font = pygame.font.SysFont("Vera Sans", 30)
		head3font = pygame.font.SysFont("Vera Sans", 24)
		head4font = pygame.font.SysFont("Vera Sans", 20)
		textfont = pygame.font.SysFont("Vera Sans", 18)
		codefont = pygame.font.SysFont("Fixed", 18)
		codefont.set_bold(True)

		x = 50
		y = 50 - self.scrollpos
		overstack = []

		for podparagraph in self.podparagraphs:
			(kind, paragraph) = podparagraph
			#print "para", kind

			if kind == "command":
				rcommand = re.compile("^=(\w+)(?: ((?:\ ?\S+)+))?$")
				mcommand = rcommand.match(paragraph[0])
				keyword = mcommand.group(1)
				if keyword == "head1":
					y += 10
					heading = mcommand.group(2)
					#f = head1font.render(heading, 1, fore)
					#surface.blit(f, (x, y))
					(x, y) = self.render_interior(surface, heading, (x, y), head1font)
					y += 10
				elif keyword == "head2":
					y += 8
					heading = mcommand.group(2)
					#f = head2font.render(heading, 1, fore)
					#surface.blit(f, (x, y))
					(x, y) = self.render_interior(surface, heading, (x, y), head2font)
					y += 8
				elif keyword == "head3":
					y += 5
					heading = mcommand.group(2)
					#f = head3font.render(heading, 1, fore)
					#surface.blit(f, (x, y))
					(x, y) = self.render_interior(surface, heading, (x, y), head3font)
					y += 5
				elif keyword == "head4":
					y += 2
					heading = mcommand.group(2)
					#f = head4font.render(heading, 1, fore)
					#surface.blit(f, (x, y))
					(x, y) = self.render_interior(surface, heading, (x, y), head4font)
					y += 2
				elif keyword == "over":
					indentstr = mcommand.group(2)
					if indentstr:
						indent = int(mcommand.group(2))
					else:
						indent = 4
					f = textfont.render("M", 1, fore)
					indentwidth = indent * f.get_width()
					overstack.append(indentwidth)
					x += indentwidth
				elif keyword == "item":
					y += 10
					item = mcommand.group(2)
					f = textfont.render(u"·", 1, fore)
					surface.blit(f, (x - 10, y))
					(x, y) = self.render_interior(surface, item, (x, y), textfont)
					y += 10
				elif keyword == "back":
					if overstack:
						x -= overstack.pop()
				elif keyword == "begin":
					format = mcommand.group(2)
				elif keyword == "end":
					format = mcommand.group(2)
				elif keyword == "for":
					format = mcommand.group(2)
				elif keyword == "encoding":
					type = mcommand.group(2)
			elif kind == "verbatim":
				sh = 0
				sw = 0
				for text in paragraph:
					f = codefont.render(text, 1, fore)
					if f.get_width() > sw:
						sw = f.get_width()
					sh += f.get_height() + 5
				surface.fill((255, 230, 160), ((x - 2, y), (sw + 4, sh)))
				for text in paragraph:
					f = codefont.render(text, 1, fore)
					surface.blit(f, (x, y))
					y += f.get_height() + 5
			elif kind == "ordinary":
				text = " ".join(paragraph)
				(x, y) = self.render_interior(surface, text, (x, y), textfont)

		if self.scrollbottom == 0:
			self.scrollbottom = y

		screen.blit(surface, (0, 0))

		# HACK!
		if pygame.display.get_surface():
			pygame.display.update()

	def render_interior(self, surface, text, coords, textfont):
		(x, y) = coords

		# FIXME: duplication of those attributes
		#textfont = pygame.font.SysFont("Vera Sans", 18)
		fore = (0, 0, 0)

		rinterior = re.compile("(\S)\<([^\>]*)\>")
		sinterior = rinterior.split(text)
		#print "SINT:"
		state = "normal"
		xorig = x
		foreorig = fore
		flagstack = []
		for part in sinterior:
			#print "***", sint
			words = part.split(" ")
			if state == "interior-flag":
				flag = words[0]
				flagstack.append(flag)
				if flag == "I":
					textfont.set_italic(1)
				if flag == "B":
					textfont.set_bold(1)
				if flag == "L":
					fore = (255, 0, 0)
			else:
				for word in words:
					f = textfont.render(word, 1, fore)
					if x + f.get_width() > surface.get_width() - 100:
						y += f.get_height() + 1
						x = xorig
					surface.blit(f, (x, y))
					x += f.get_width() + 10
				x -= 10

			if state == "interior-content":
				flag = flagstack.pop()
				if flag == "I":
					textfont.set_italic(0)
				if flag == "B":
					textfont.set_bold(0)
				if flag == "L":
					fore = foreorig

			if state == "normal":
				state = "interior-flag"
			elif state == "interior-flag":
				state = "interior-content"
			elif state == "interior-content":
				state = "normal"
		y += f.get_height() + 1
		x = xorig

		return (x, y)

	def interactive(self, event):
		oldpos = self.scrollpos

		h = pygame.display.get_surface().get_height()

		if event.type == KEYDOWN:
			key = event.key
			if key == K_ESCAPE or pygame.event.peek(QUIT):
				return "exit"
			if key == K_RETURN:
				return "next"
			if key == K_BACKSPACE:
				return "previous"
			if key == K_UP:
				if self.scrollpos >= 25:
					self.scrollpos -= 25
				else:
					self.scrollpos = 0
			if key == K_DOWN:
				if self.scrollpos <= self.scrollbottom - h - 25:
					self.scrollpos += 25
				else:
					self.scrollpos = self.scrollbottom - h
			if key == K_PAGEUP:
				if self.scrollpos >= 700:
					self.scrollpos -= 700
				else:
					self.scrollpos = 0
			if key == K_PAGEDOWN:
				if self.scrollpos <= self.scrollbottom - h - 700:
					self.scrollpos += 700
				else:
					self.scrollpos = self.scrollbottom - h
			if key == K_g:
				if event.mod == KMOD_LSHIFT:
					self.scrollpos = self.scrollbottom - h
				else:
					self.scrollpos = 0

		if self.scrollpos != oldpos:
			self.render(pygame.display.get_surface(), 1, 1)

component = POD()

parameters = (
	("podfile", "Filename from which to extract POD information", None),
)

metainfo = {
	"version": "0.1",
	"author": "Josef Spillner",
	"authoremail": "<josef@coolprojects.org>",
	"licence": "GPL"
}

doc = """
POD: creation of slides from Plain Old Documentation

Usage: 'pod' -podfile <podfile>
 Where <podfile> is any file with embedded POD, like Perl source files

Display: yes

Interactivity: scrolling with up/down, page up/page down, jumping with g/G
"""

