#!/usr/bin/python
#
# mkgcm -- make small geocache html files for a big GPX download.
#
# usage: mkgcm -i index.html -it "Monterey, CA" -o outdir monterey.gpx
#
# Copyright (c) 2007 Brian "Beej Jorgensen" Hall
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

import sys
import os
import os.path
import xml.dom.minidom
import re

class Cache(object):
	def __init__(self):
		self.lat = None
		self.lon = None
		self.id = None
		self.desc = None
		self.url = None
		self.type = None
		self.name = None
		self.owner = None
		self.container = None
		self.difficulty = None
		self.terrain = None
		self.shortdesc = None
		self.longdesc = None
		self.available = True
		self.archived = False
		self.hints = None
		self.logs = []
		self.travelbugs = []

class AppInfo(object):
	def __init__(self):
		self.outdirname = "."
		self.infilename = None
		self.indexfilename = None
		self.indextitle = None
		self.dom = None

def usageExit():
	sys.stderr.write("usage: mkgcm [-i indexfile] [-it indextitle] [-o outdir] infile\n")
	sys.exit(1)

def parseCommandLine(argv):
	argv.pop(0)
	ai = AppInfo()

	while len(argv) > 0:
		if argv[0] == "-?" or argv[0] == "-h" or argv[0] == "--help":
			usageExit()
		elif argv[0] == "-o":
			if len(argv) == 1: usageExit()
			argv.pop(0)
			ai.outdirname = argv[0]
		elif argv[0] == "-i":
			if len(argv) == 1: usageExit()
			argv.pop(0)
			ai.indexfilename = argv[0]
		elif argv[0] == "-it":
			if len(argv) == 1: usageExit()
			argv.pop(0)
			ai.indextitle = argv[0]
		elif ai.infilename == None:
			ai.infilename = argv[0]
		else:
			usageExit()

		argv.pop(0)

	if ai.infilename == None:
		usageExit()

	if ai.indextitle == None:
		ai.indextitle = ai.infilename
	
	return ai

def textUnder(node):
	text = ""
	for n in node.childNodes:
		if n.nodeType == n.TEXT_NODE:
			text = text + n.data

	return text

def buildCacheList(dom):
	dom = dom.firstChild  # <gpx>
	cacheList = []
	for wpt in dom.getElementsByTagName("wpt"): # all <wpt> elements
		cache = Cache()
		cache.lat = wpt.getAttribute("lat")
		cache.lon = wpt.getAttribute("lon")
		cache.id = textUnder(wpt.getElementsByTagName("name")[0])
		cache.url = textUnder(wpt.getElementsByTagName("url")[0])
		gs = wpt.getElementsByTagName("groundspeak:cache")[0]
		cache.available = (gs.getAttribute("available") == "True")
		cache.archived = (gs.getAttribute("archived") == "True")
		cache.container = textUnder(gs.getElementsByTagName("groundspeak:container")[0])
		cache.owner = textUnder(gs.getElementsByTagName("groundspeak:owner")[0])
		cache.name = textUnder(gs.getElementsByTagName("groundspeak:name")[0])
		cache.type = textUnder(gs.getElementsByTagName("groundspeak:type")[0])
		cache.difficulty = textUnder(gs.getElementsByTagName("groundspeak:difficulty")[0])
		cache.terrain = textUnder(gs.getElementsByTagName("groundspeak:terrain")[0])
		cache.shortdesc = textUnder(gs.getElementsByTagName("groundspeak:short_description")[0])
		cache.longdesc = textUnder(gs.getElementsByTagName("groundspeak:long_description")[0])
		cache.hints = textUnder(gs.getElementsByTagName("groundspeak:encoded_hints")[0]).strip()

		logparent = gs.getElementsByTagName("groundspeak:logs")[0]
		for l in logparent.getElementsByTagName("groundspeak:log"):
			date = textUnder(l.getElementsByTagName("groundspeak:date")[0])
			date = date.split("T")[0]
			type = textUnder(l.getElementsByTagName("groundspeak:type")[0])
			finder = textUnder(l.getElementsByTagName("groundspeak:finder")[0])
			cache.logs.append("%s: %s (%s)" % (date, type, finder))

		tbparent = gs.getElementsByTagName("groundspeak:travelbugs")[0]
		for b in tbparent.getElementsByTagName("groundspeak:travelbug"):
			ref = b.getAttribute("ref")
			name = textUnder(b.getElementsByTagName("groundspeak:name")[0])

			cache.travelbugs.append("%s (%s)" % (name, ref))

		cacheList.append(cache)

	return cacheList

def ll2hm(llstr, isLat):
	llfloat = float(llstr)

	if llfloat < 0:
		llfloat = -llfloat
		if isLat: dir = "S"
		else: dir = "W"
	else:
		if isLat: dir = "N"
		else: dir = "E"

	lldeg = int(llfloat)
	llmin = (llfloat - lldeg) * 60

	return "%s %d&deg; %.3f'" % (dir, lldeg, llmin)

def writeFiles(cacheList):
	for c in cacheList:
		fp = file("%s.html" % c.id, "w")
		fp.write("<html>\n</head>\n")
		fp.write("<title>%s</title>\n" % c.id)
		fp.write("</head>\n\n<body>\n")

		fp.write("<b>%s</b>\n" % c.name.encode("ascii", "replace"))
		fp.write("<br>Lat: %.5f, Lon: %.5f\n" % (float(c.lat), float(c.lon)))
		fp.write("<br>Lat: %s, Lon: %s\n" % (ll2hm(c.lat, True), ll2hm(c.lon, False)))
		fp.write("<p>Type: %s\n" % c.type)
		fp.write("<br>Container: %s\n" % c.container)
		fp.write("<br>Difficulty: %s/5\n" % c.difficulty)
		fp.write("<br>Terrain: %s/5\n" % c.terrain)
		fp.write("<br>Owner: %s\n" % c.owner.encode('ascii', 'replace'))
		fp.write("<br>ID: <a href=\"%s\">%s</a>\n" % (c.url, c.id))

		if not c.available:
			fp.write("<p><b>WARNING!</b>  Cache marked as Unavailable!\n")
		if c.archived:
			fp.write("<p><b>WARNING!</b>  Cache marked as Archived!\n")

		if c.travelbugs != []:
			fp.write("<p><b>Travelbugs</b>:\n<p>")
			fp.write("<ul>\n")
			for b in c.travelbugs:
				fp.write("<li>%s</li>\n" % b.encode('ascii', 'replace'))
			fp.write("</ul>\n")

		fp.write("<p><b>Description</b>:\n")
		fp.write("<p>%s\n<p>%s\n" % (c.shortdesc.encode('ascii', 'replace'), c.longdesc.encode('ascii', 'replace')))

		fp.write("<p><b>Hints</b>:\n")
		if c.hints == "":
			fp.write("<p>None :-(<br>\n")
		else:
			c.hints = re.sub("\n", "\n<br>", c.hints)
			fp.write("<p>%s\n" % c.hints.encode("ascii", "replace"))

		fp.write("<p><b>Last Logs</b>:\n<p>")
		if c.logs == []:
			fp.write("None!<br>\n")
		else:
			count = 0
			for l in c.logs:
				fp.write("%s<br>\n" % l)
				count = count + 1
				if count > 5: break

		fp.write("<hr>\n</body>\n</html>\n")

		fp.close()

def writeIndexFiles(ai, cacheList):
	fp = file(ai.indexfilename, "w")
	fp.write("<html>\n</head>\n")
	fp.write("<title>%s</title>\n" % ai.indextitle)
	fp.write("</head>\n\n<body>\n")

	fp.write("<h2>%s</h2>\n" % ai.indextitle)
	fp.write("<ul>\n")

	idList = [c.id for c in cacheList]
	idList.sort()
	for id in idList:
		fp.write("<li><a href=\"%s.html\">%s</a></li>\n" % (id, id))

	fp.write("</ul>\n<hr>\n</body>\n</html>\n")
	fp.close()

def main(argv):
	ai = parseCommandLine(argv)

	sys.stderr.write("reading XML...\n")
	ai.dom = xml.dom.minidom.parse(ai.infilename)

	sys.stderr.write("building cache list...\n")
	cacheList = buildCacheList(ai.dom)

	sys.stderr.write("writing cache files...\n")
	if not os.path.exists(ai.outdirname):
		os.makedirs(ai.outdirname)

	os.chdir(ai.outdirname)
	writeFiles(cacheList)

	if ai.indexfilename != None:
		sys.stderr.write("writing index file...\n")
		writeIndexFiles(ai, cacheList)

	return 0

if __name__ == "__main__": sys.exit(main(sys.argv))

