#! /usr/bin/env python

import os, sys, re

from reportlab.lib import colors
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.lib.units import cm
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.pdfgen import canvas
from reportlab.platypus.flowables \
   import Spacer, Preformatted, Image, KeepTogether
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.xpreformatted \
   import PythonPreformatted
from reportlab.platypus.frames import Frame
from reportlab.platypus.doctemplate \
   import PageTemplate, BaseDocTemplate


def eachPage(canvas, doc):
   "Adornments for each page."

   canvas.saveState()
   canvas.setFont('Helvetica', 8)
   canvas.setFillColor(colors.black)
   # add page number
   num = "%d" % doc.page
   canvas.drawCentredString(10.5*cm, 1*cm, num)
   canvas.restoreState()


class LinuxMagDocTemplate(BaseDocTemplate):
   "The document template used for all pages."

   def __init__(self, filename, **kw):
       fw, fh = 8.5*cm, 26.5*cm
       f1 = Frame(2*cm, 1.5*cm, fw, fh, id='c1')
       f2 = Frame(10.5*cm, 1.5*cm, fw, fh, id='c2')
       self.allowSplitting = 0
       BDT, PT = BaseDocTemplate, PageTemplate
       apply(BDT.__init__, (self, filename), kw)
       pt = PT('TwoCol', [f1, f2], eachPage)
       self.addPageTemplates(pt)


def handlePara(para):
   "Convert intra-paragraph markup."

   # quotes
   while '"' in para:
       for j in chr(227), chr(210):
           i = para.find('"')
           para = para[:i] + j + para[i+1:]

   # guillemets and URLs
   data = [
       ('<C>', chr(200), chr(199)),
       # ('<U>', '<font color="blue">', '</font>')]
       ('<U>', '<a href="%s" color="blue">', '</a>')]
   for (tag, before, after) in data:
       pat = '%s.*?%s' % (tag, tag)
       while para.find(tag) >= 0:
           m = re.search(pat, para)
           if not m:
               continue
           start, end = m.start()+3, m.end()-3
           word = para[start:end]
           if tag == '<U>':
               before = before % word
           word = before + word + after
           para = re.sub(pat, word, para, 1)

   return para


def handleCode(lines, i):
   "Handle external code listing."

   listing = lines

   # strip off heading and trailing empty lines
   if listing:
       while not listing[0].strip():
           del listing[0]
       while not listing[-1].strip():
           del listing[-1]

   # number lines if we have more than 10 lines
   listLen = len(listing)
   if listLen > 10:
       format = "%%%dd %%s"
       format = format % len(str(listLen))
       for k in xrange(len(listing)):
           listing[k] = format % (k+1, listing[k])

   # assemble and add to story
   listing = ''.join(listing)

   return listing, i


def handleInlineListing(lines, i):
   "Handle inline listing."

   # extract lines belonging to the listing
   listing = []
   j = 0
   while 1:
       j = j + 1
       try:
           line = lines[i + j]
       except IndexError:
           break
       if line and line[0] == '@':
           break
       else:
           listing.append(line)
   i = i + j - 1

   return handleCode(listing, i)


def main(path):
   "The main meat."

   lines = open(path).readlines()
   print len(lines)

   # Platypus variables.
   story = []
   styleSheet = getSampleStyleSheet()
   h1 = styleSheet['Heading1']
   h2 = styleSheet['Heading2']
   h3 = styleSheet['Heading3']
   code = styleSheet['Code']
   bt = styleSheet['BodyText']
   for h in (h1, h2, h3):
       h.fontName = 'Helvetica-Bold'
       ## h.spaceAfter = -5
   bt.spaceBefore = 0
   bt.spaceAfter = 0
   bt.fontSize = 9
   bt.alignment = TA_JUSTIFY
   bt.bulletFontName = 'ZapfDingbats'
   bt.bulletFontSize = 7
   code.leftIndent = 0
   code.firstLineIndent = 0
   code.fontSize = 7
   code.spaceBefore = 3
   code.spaceAfter = 9

   P = Paragraph
   bold = '<b>%s</b>'
   author = ''
   allowedKeys = 'T V ZT IT L IL KT LI B Bi CO'
   allowedKeys = allowedKeys.split()

   i = -1
   while i < len(lines)-1:
       i = i + 1
       line = lines[i].strip()

       key = ''
       m = re.match('^@([A-Za-z]+):', line)
       if m:
           key = m.groups()[0]
           keyLen = len(key) + 2
       elif line[:2] == '* ':
           key, keyLen = '* ', 2
       else:
           continue
       rline = line[keyLen:].strip()

       if key == 'A':
           author = line[keyLen:]

       elif key == 'Bi':
           p = Image(rline,
                     width=8.5*cm, height=200,
                     kind='proportional')
           print "img: %s " % rline

       elif key == 'LI':
           listing, i = handleInlineListing(lines, i)
           p = PythonPreformatted(listing, code)

       # Added to LinuxMag format
       elif key == 'CO':
           clines = open(rline).readlines()
           print "%s (%d)" % (rline, len(clines))
           listing, i = handleCode(clines, i)
           p = PythonPreformatted(listing, code)

       else:
           para = handlePara(rline)
           bpara = bold % para
           vformat = "<i>%s (%s)</i>"
           dict = {
               'L':  P(para, bt),
               'IL': P(bpara, bt),
               'T':  P(para, h1),
               'ZT': P(para, h2),
               'IT': P(bpara, h3),
               'B':  P(bpara, h3),
               'KT': P(bpara, h3),
               '* ': P(para, bt, bulletText=chr(110)),
               'V':  KeepTogether([
                      P(vformat % (para, author), bt),
                        Spacer(0, 12)])
           }
           p = dict.get(key, None)

       if key in allowedKeys + ['* ']:
           story.append(p)

   # Build the PDF document.
   path = os.path.splitext(path)[0] + '.pdf'
   doc = LinuxMagDocTemplate(path)
   doc.build(story)


if __name__ == '__main__':
   main(sys.argv[1])