#!/usr/bin/env python
# coding: utf-8

import sys, string, getopt, xml.dom.minidom

class handler:
	def __init__(self):
		self.functions = { }
		self.file = None
		self.format = None
		self.extension = None
	def run_function(self, name, node):
		if self.functions.has_key(name):
			self.functions[name](node)
	def open(self, filename = None, basename = None):
		if filename is None:
			if basename is None:
				basename = "resume"
			filename = basename + "." + self.extension
		if filename == "-":
			self.file = sys.stdout
		else:
			self.file = file(filename, "wb")
		return self.file
	def close(self):
		self.file.close()
	def write(self, data):
		self.file.write(data.encode("utf-8", "replace"))
	def start(self, root):
		pass
	def end(self, root):
		pass
	def separate(self, a, b):
		pass
	def wrap(self, text, indent = "", firstindent = None, column = 0, width = 72, min_width = 0):
		result = ""
		i = 0
		while i < len(text) and text[i] in string.whitespace:
			i = i + 1
		if firstindent is None:
			firstindent = indent
		ind = firstindent
		while i < len(text):
			ind_length = len(ind.expandtabs())
			p = i
			while i < len(text) and text[i] not in string.whitespace:
				i += 1
			if column != 0 and column + i - p + 1 > width and column + i - p + 1 - ind_length >= min_width:
				result += "\n"
				column = 0
			if column == 0:
				result += ind
				column += ind_length
			else:
				result += " "
				column += 1
			result += text[p:i]
			column += i - p
			while i < len(text) and text[i] in string.whitespace:
				i += 1
			ind = indent
		result += "\n"
		return result

class handler_text(handler):
	def __init__(self):
		handler.__init__(self)
		self.format = "text"
		self.extension = "txt"
		self.functions = { "header": self.header,
		                   "blurb": self.blurb,
		                   "par": self.par,
		                   "category": self.category }
	def separate(self, a, b):
		if self.functions.has_key(a) and self.functions.has_key(b):
			self.write("\n")
		if a == "pagebreak":
			self.write("\n")
	def escape(self, str):
		tmp = ""
		for c in str:
			if ord(c) == 160: # Non-breaking space. It might break.
				s = " "
			elif ord(c) == 233:
				s = "e"
			elif ord(c) == 0x2013:
				s = "-"
			elif ord(c) == 0x2014:
				s = "--"
			elif ord(c) == 0x2018:
				s = "'"
			elif ord(c) == 0x2019:
				s = "'"
			elif ord(c) == 0x201C:
				s = "\""
			elif ord(c) == 0x201D:
				s = "\""
			elif ord(c) >= 128:
				s = ""
			else:
				s = c
			tmp = tmp + s
		return tmp
	def read_text(self, node):
		def recurse(node):
			text = ""
			for child in node.childNodes:
				if child.nodeType in [ xml.dom.Node.TEXT_NODE, xml.dom.Node.CDATA_SECTION_NODE ]:
					text += child.data
				elif child.nodeType == xml.dom.Node.ELEMENT_NODE:
					text += recurse(child)
			return text
		return recurse(node)
	def header(self, node):
		for name in [ "name", "title", "phone", "web", "email" ]:
			attr = node.getAttributeNode(name)
			if attr is not None:
				self.write(self.escape(attr.nodeValue) + "\n")
	def blurb(self, node):
		self.par(node);
	def par(self, node):
		self.write(self.wrap(self.escape(self.read_text(node))))
	def category(self, node):
		def recurse(node, level = 0):
			if node.nodeType != xml.dom.Node.ELEMENT_NODE:
				return
			if node.localName == "category":
				attr = node.getAttributeNode("name")
				if attr is not None:
					name = attr.nodeValue
				else:
					name = None
				if (level < 0):
					level = 0
				if name is not None:
					self.write(self.wrap(self.escape(name), indent = "  " * level))
				for child in node.childNodes:
					recurse(child, level + 1)
			elif node.localName == "item":
				self.write(self.wrap(self.escape(self.read_text(node)), firstindent = "  " * level + "* ", indent = "  " * (level + 1)))
		recurse(node)

class handler_html(handler):
	def __init__(self):
		handler.__init__(self)
		self.format = "HTML"
		self.extension = "html"
		self.functions = { "header": self.header, "blurb": self.blurb,
		                   "par": self.par, "category": self.category }
	def escape(self, str):
		tmp = ""
		for c in str:
			if c == "&":
				s = "&amp;"
			elif c == "<":
				s = "&lt;"
			elif c == ">":
				s = "&rt;"
			elif c == "\"":
				s = "&quot;"
			elif c == "'":
				s = "&apos;"
			elif ord(c) == 160: # Non-breaking space
				s = "&nbsp;"
			elif ord(c) >= 128:
				s = "&#%d;" % ord(c)
			else:
				s = c
			tmp = tmp + s
		return tmp
	def read_text(self, node):
		def recurse(node):
			text = ""
			for child in node.childNodes:
				if child.nodeType in [ xml.dom.Node.TEXT_NODE, xml.dom.Node.CDATA_SECTION_NODE ]:
					text += self.escape(child.data)
				elif child.nodeType == xml.dom.Node.ELEMENT_NODE:
					if child.localName == "link":
						text += "<a href=\"" + self.escape(child.getAttribute("target")) + "\">" + recurse(child) + "</a>"
					elif child.localName == "cite":
						text += "<cite>" + recurse(child) + "</cite>"
					else:
						text += recurse(child)
			return text
		return recurse(node)
	def start(self, root):
		self.write("""\
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title>""" + self.escape(root.getAttribute("title")) + """</title>
	</head>
	<body>
""")
	def end(self, root):
		self.write("""\
	</body>
</html>
""")
	def separate(self, a, b):
		if a == "header":
			self.write("\t\t<div><hr /></div>\n")
	def header(self, node):
		self.write("\t\t<p>\n")
		attr = node.getAttributeNode("name")
		if attr is not None:
			self.write("\t\t<strong>" + self.escape(attr.nodeValue) + "</strong><br />\n")
		for name in [ "title", "phone" ]:
			attr = node.getAttributeNode(name)
			if attr is not None:
				self.write("\t\t" + self.escape(attr.nodeValue) + "<br />\n")
		attr = node.getAttributeNode("web")
		if attr is not None:
			self.write("\t\t <a href=\"" + self.escape(attr.nodeValue) + "\">" + self.escape(attr.nodeValue) + "</a><br />\n")
		attr = node.getAttributeNode("email")
		if attr is not None:
			self.write("\t\t <a href=\"mailto:" + self.escape(attr.nodeValue) + "\">" + self.escape(attr.nodeValue) + "</a><br />\n")
		self.write("\t\t</p>\n")
	def blurb(self, node):
		text = "\t\t<p>\n" + self.wrap(self.read_text(node), indent = "\t\t") + "\t\t</p>\n"
		self.write(text)
	def par(self, node):
		text = "\t<p>\n" + self.wrap(self.read_text(node), indent = "\t") + "\t</p>\n"
		self.write(text)
	def category(self, node):
		def recurse(node, level = 0):
			if (node.nodeType != xml.dom.Node.ELEMENT_NODE) or (node.localName != "category"):
				return
			attr = node.getAttributeNode("name")
			if attr is not None:
				name = attr.nodeValue
			else:
				name = None
			if level < 0:
				level = 0
			self.write(self.wrap("<li><em>" + self.escape(name) + "</em>", indent = "\t" * (level + 3), min_width = 40))
			self.write("\t" * (level + 3) + "<ul>\n")
			for child in node.childNodes:
				if (child.nodeType == xml.dom.Node.ELEMENT_NODE) and (child.localName == "item"):
					self.write(self.wrap("<li>" + self.read_text(child).strip() + "</li>", indent = "\t" * (level + 4), min_width = 40))
				elif (child.nodeType == xml.dom.Node.ELEMENT_NODE) and (child.localName == "category"):
					recurse(child, level + 1)
			self.write("\t" * (level + 3) + "</ul>\n")
			self.write(self.wrap("</li>", indent = "\t" * (level + 3), min_width = 40))
		self.write("\t" * 2 + "<ul>\n")
		recurse(node)
		self.write("\t" * 2 + "</ul>\n")

class handler_tex(handler):
	def __init__(self):
		handler.__init__(self)
		self.format = "TeX"
		self.extension = "tex"
		self.functions = { "header": self.header,
		                   "pagebreak": self.pagebreak,
				   "blurb": self.blurb,
				   "par": self.par,
		                   "category": self.category }
	def escape(self, str):
		# See the TeXbook, chapter 7, page 38
		tmp = ""
		for c in str:
			if c == "\\":
				s = "\\backslash"
			elif c == "{":
				s = "$\\{$"
			elif c == "}":
				s = "$\\}$"
			elif c == "$":
				s = "\\$"
			elif c == "%":
				s = "\\%"
			elif c == "&":
				s = "\\&"
			elif c == "#":
				s = "\\#"
			elif c == "_":
				s = "\\_"
			elif ord(c) == 160: # Non-breaking space
				s = "~"
			elif ord(c) == 233:
				s = "\\'e"
			elif ord(c) == 0x2013:
				s = "--"
			elif ord(c) == 0x2014:
				s = "---"
			elif ord(c) == 0x2018:
				s = "`"
			elif ord(c) == 0x2019:
				s = "'"
			elif ord(c) == 0x201C:
				s = "``"
			elif ord(c) == 0x201D:
				s = "''"
			elif ord(c) >= 128:
				s = ""
			else:
				s = c
			tmp = tmp + s
		return tmp
	def read_text(self, node):
		def recurse(node, lower = 0):
			text = ""
			for child in node.childNodes:
				if child.nodeType in [ xml.dom.Node.TEXT_NODE, xml.dom.Node.CDATA_SECTION_NODE ]:
					tmp = child.data
					if lower:
						tmp = tmp.lower()
					text += self.escape(tmp)
				elif child.nodeType == xml.dom.Node.ELEMENT_NODE:
					if child.localName == "cite":
						text += "{\\sl " + recurse(child) + "}"
					elif child.localName == "sc":
						text += "{\\sc " + recurse(child, lower = 1) + "}"
					else:
						text += recurse(child)
			return text
		return recurse(node)
	def start(self, root):
		self.write("""\
\\nopagenumbers

\\font\\bigrm = cmr17
\\font\\bigbf = cmbx12 at 17 pt
\\font\\medsf = cmss12 at 14 pt
\\font\\rm = cmr12
\\font\\sf = cmss12
\\font\\tt = cmtt12
\\font\\sc = cmcsc10 at 12 pt
\\font\\sl = cmsl12
\\rm

\\vbox to 0 pt {}
""")
	def end(self, root):
		self.write("""\

\\vfill
\\eject
\\end
""")
	def separate(self, a, b):
		if a in [ "header", "blurb" ] or b in [ "header", "blurb" ]:
			self.write("""
\\bigskip

""")
	def header(self, node):
		attr = node.getAttributeNode("name")
		if attr is not None:
			self.write(r"\centerline {\bigbf " + self.escape(attr.nodeValue) + "}\n")
		attr = node.getAttributeNode("title")
		if attr is not None:
			self.write(r"\centerline {\sf " + self.escape(attr.nodeValue) + "}\n")
		attr = node.getAttributeNode("phone")
		if attr is not None:
			self.write(r"\centerline {\sf " + self.escape(attr.nodeValue).replace(" ", r"\thinspace ") + "}\n")
		for name in [ "web", "email" ]:
			attr = node.getAttributeNode(name)
			if attr is not None:
				self.write(r"\centerline {\tt " + self.escape(attr.nodeValue) + "}\n")
	def pagebreak(self, node):
		self.write("""\

\\vfill
\\eject
""")
	def blurb(self, node):
		text = """\
\\noindent \\hfil \\vbox {
\\hsize = 4.25 in
\\noindent
""" + self.wrap(self.read_text(node)) + """\
} \\hfil
"""
		self.write(text)
	def par(self, node):
		text = "\\noindent\n" + self.wrap(self.read_text(node))
		self.write(text)
	def category(self, node):
		def recurse(node, level = 0):
			if node.nodeType != xml.dom.Node.ELEMENT_NODE:
				return
			if node.localName == "category":
				attr = node.getAttributeNode("name")
				if attr is not None:
					name = attr.nodeValue
				else:
					name = None
				if level < 0:
					level = 0
				if name is not None:
					if level == 0:
						self.write(self.wrap("\\noindent {\\medsf " + self.escape(name) + "}"))
					elif level == 1:
						self.write(self.wrap("\\item {} {\\sf " + self.escape(name) + "}"))
					else:
						self.write(self.wrap("\\itemitem {} {\\sf " + self.escape(name) + "}"))
				for child in node.childNodes:
					recurse(child, level + 1)
			elif node.localName == "item":
				if level == 1:
					self.write("\\item {}\n")
				else:
					self.write("\\itemitem {}\n")
				self.write(self.wrap(self.read_text(node)))
		recurse(node)
		self.write("\\smallskip\n")

xml_handlers = [ handler_text(), handler_html(), handler_tex() ]

filename = "resume.xml"
output_formats = [ "html" ]
output_filename = None

try:
	opts, args = getopt.getopt(sys.argv[1:], "f:ho:", [ "formats=", "help", "output=" ])
except getopt.GetoptError:
	print "Bad option"
	sys.exit(1)
for opt, arg in opts:
	if opt in [ "-f", "--formats" ]:
		output_formats = arg.lower().split(",")
	if opt in [ "-o", "--output" ]:
		output_filename = arg
	if opt in [ "-h", "--help" ]:
		print "Help!"
		for handler in xml_handlers:
			print handler.format

if len(args) == 1:
	filename = args[0]
elif len(args) > 1:
	print "Too many filenames"
	sys.exit(1)

handlers = []

for handler in xml_handlers:
	if handler.format.lower() in output_formats:
		handlers.append(handler)

if output_filename is not None and len(handlers) != 1:
	print "You may not specify an output file when using more than one format"
	sys.exit(1)

resume = xml.dom.minidom.parse(filename).documentElement

for handler in handlers:
	handler.open(output_filename)
	handler.start(resume)

prev_node_name = None
for node in resume.childNodes:
	if node.nodeType != xml.dom.Node.ELEMENT_NODE:
		continue
	node_name = node.localName
	for handler in handlers:
		handler.separate(prev_node_name, node_name)
		handler.run_function(node_name, node)
	prev_node_name = node_name
for handler in handlers:
	handler.separate(node_name, None)

for handler in handlers:
	handler.end(resume)
	handler.close()

