#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
# This script is placed in the public domain - you can use it for whatever you want.

from optparse import OptionParser
from os import listdir, makedirs, stat
from os.path import isdir, isfile, isabs, basename, dirname
from os.path import join as joinpath
from re import compile
from shutil import copyfile
from subprocess import Popen, PIPE # note: this replaces popen2 and such
from sys import stdout
from time import localtime, strftime

#
# Settings you can (and should) alter
#
# inputdirs:  list of directories that contain the photos. If you have only one such directory, set it to ['/your/dir'].
# outputdir:  output directory to start. Examples: 'C:\apache\www\', '/var/www/'
# httpprefix: the prefix for all http links. Examples: 'http://my.photos.com/', '/static/'. This needs to be an absolute path, either starting with http://,https:// or / and ending with /
# title:      Prefix that will be used on every single page.
settings = {'inputdirs':['/home/quinox/photo_collection/'],
            'outputdir':'/var/www/photos.qtea.nl/',
            'httpprefix':'/',
            'title':u'Photographs @ Qtea.nl'}
#settings = {'inputdirs':['/home/quinox/photo_collection/winter/'],
#            'outputdir':'/var/www/photos.qtea.nl/test/',
#            'httpprefix':'/test/',
#            'title':u'TitTest'}

#
# Settings you probably shouldn't alter
#
settings.update({'output_encoding':u'utf8',
                 'meta_encoding':u'utf8',
                 'fs_encoding':u'utf8',
                 'css_location':'/galgen.css',
                 'copyrightlogo_location':'/copyright.png',
                 'publicdomainlogo_location':'/publicdomain.png',
                 'special_prefix':'+galgen',
                 'special_glue':'+',
                 'album_index_file':'index.html'})

# Notes:
#
# * Script depends on ImageMagick. See also http://www.imagemagick.org
# * Since the script creates pages for the deepest directories first, it should be save
#   to output directly to a live web server.
# * Indent by using tabs (and no such bullshit as "a tab is 4 spaces"... a tab is a tab),
#   except when you are have a long list, split up over multiple lines.
#   In that case use as many tabs as the first line, and use spaces for the rest of the gap.
#   Example:
#
#  <tab>some code
#  <tab>some other code
#  <tab>if something:
#  <tab><tab>do something
#  <tab>longlist = ([item1,item2,
#  <tab><   spaces  >item3,item4]);
#

class Node(object):
	"""Used for both internal and external."""
	date_regex = compile('^(19[0-9][0-9]|20[0-9][0-9])[-/](0?[1-9]|1[012])[-/](0?[1-9]|1[0-9]|2[0-9]|3[012])$')

	def __init__(self, parent, abspath, outputhandle, title, summary, license, status, date):
		"""constructor

		abspath is the input path
		outputhandle is the output handle. It needs to be unique between siblings -
		the parent node must be able to guarantee that at all times. Note that the parent
		might alter this at any time, but should leave it intact as much as possible. Also,
		it should be altered in a consistent manner: when re-running the script with precisely
		the same input directories, the output links and pages need to be identical
		"""
		assert isabs(abspath),'I dont do relative addresses!'
		assert type(abspath) == type(str('')),'Bleh'
		self.parent = parent
		self.title = u''
		self.summary = []
		self.license = u''
		self.status  = u''
		self.date    = u''
		# Set to True if this node has already produces its HTML/images
		self.processed = False
		self.inputpath = abspath
		self.setHandle(outputhandle)
		self.setTitle(title)
		self.setSummary(summary)
		self.setLicense(license)
		# These functions might fail
		self.setStatus(status)
		self.setDate(date)
	def setHandle(self, handle):
		handle = handle.replace('\\','_')
		handle = handle.replace('//','_')
		self.outputhandle = handle
	def setTitle(self, title):
		assert type(title) == type(u''),'Bleh'
		self.title = title
		return True
	def setSummary(self, summary):
		assert type(summary) == type([]),'Bleh'
		for element in summary:
			assert type(element) == type(u''),'Bleh'
		self.summary = summary
		return True
	def setLicense(self, license):
		assert type(license) == type(u''),'Bleh'
		self.license = license
		return True
	def setStatus(self, status):
		"""updates status

		public  = accessible,     shown in the gallery,     will show up at other places
		hidden  = accessible,     not shows in gallery,     will not show up at other places
		private = accessible,     shown in the gallery,     will not show up at other places
		locked  = not accessible, not shown in the gallery, does not show up at other places

		Kids will always inherit their parents status, except when the parent is
		hidden. in that case the childs status is private."""
		status = status.strip().lower()
		if status == u'': self.status = u''
		elif status in (u'public',u'published'):                    self.status = u'public'
		elif status in (u'hidden',u'secret'):                       self.status = u'hidden'
		elif status in (u'private'):                                self.status = u'private'
		elif status in (u'locked',u'unaccessible',u'inaccessible'): self.status = u'locked'
		else:
			print repr(status)
			raise
	def setDate(self, date):
		assert type(date) == type(u''),'Bleh'
		outcome = self.date_regex.match(date)
		if outcome:
			self.date = '%04i/%02i/%02i' % (int(outcome.group(1)),int(outcome.group(2)),int(outcome.group(3)))
			return True
		else:
			return False
	def getTitle(self):
		if self.title:  return self.title
		if self.parent: return self.parent.getTitle()
		return u'TOP NODE TITLE, NOT SET'
	def getSummary(self):
		if self.summary: return self.summary
		if self.parent:  return self.parent.getSummary()
		return u'TOP NODE SUMMARY, NOT SET'
	def getLicense(self):
		if self.license: return self.license
		if self.parent:  return self.parent.getLicense()
		return u'Copyright'
	def getStatus(self):
		if self.status: return self.status
		if self.parent: 
			parent = self.parent.getStatus()
			if parent == u'hidden':
				return 'private'
			return parent
		return u'locked'
	def getDate(self):
		if self.date:   return self.date
		if self.parent: return self.parent.getDate()
		return u''
class DirNode(Node):
	"""An internal node."""

	# getHTTPPath:  the string used to access this node over HTTP. Example: http://some.domain/prefix/node_handle/my_index.html
	# getDirHTTPPath: the string used to access the directory of this node over HTTP. http://some.domain/prefix/node_handle/
	# getThumbnail: the string used to access the thumbnail of this node over HTTP

	def __init__(self, abspath, parent=None, title=u'', summary=[], license=u'',status=u'',date=u''):
		super(DirNode, self).__init__(parent, abspath, lastdir(abspath), title, summary, license, status, date)
		self.kids = []
		self.type = u'album'
	def addKid(self, kid):
		self.addKids([kid])
	def addKids(self, kids):
		handles = []
		for kid in self.kids:
			handles.append(kid.outputhandle)
		for kid in kids:
			while kid.outputhandle in handles:
				# To make sure every handle is unique
				kid.outputhandle = ''.join(['_',kid.outputhandle])
			self.kids.append(kid)
	def prettyprint(self,indentation='',style='info'):
		if style == 'info':
			format = u'%s* %-'+str(50-len(indentation))+'s %10s %7s %20s (%s)'
			print format % (indentation,self.getTitle(), self.getDate(),self.getStatus(),self.getLicense(),self.inputpath)
		elif style == 'mapping':
			format = u'%s= %-'+str(20-len(indentation))+'s %-50s -> %20s [%20s]'
			print format % (indentation, self.getTitle(), self.getOutputPath(), self.getHTTPPath(), self.getThumbnail())
		for kid in self.kids:
			kid.prettyprint(indentation+'  ',style)
	def setOutputPath(self, path):
		assert isabs(path), 'Oops'
		self.outputpath = path
	def setHTTPPath(self, path):
		self.httppath = path.rstrip('/')
	def getOutputPath(self):
		if self.parent: return joinpath(self.parent.getOutputPath(),self.outputhandle)
		return self.outputpath
	def getDirHTTPPath(self):
		if self.parent:
			if self.outputhandle:
				return ''.join([self.parent.getDirHTTPPath(),self.outputhandle,'/'])
			return self.parent.getDirHTTPPath() # happens to the first generation kids of a fake root
		return ''.join([self.httppath+'/'])
	def getHTTPPath(self):
		return self.getDirHTTPPath()+settings['album_index_file']
	def getThumbnail(self):
		if self.kids:
			return self.kids[0].getThumbnail()
		else:
			return ''.join([settings['httpprefix'],settings['special_glue'].join([settings['special_prefix'],'empty','.jpeg'])])
	def getStats(self):
		albums = 0
		photos = 0
		for kid in self.kids:
			if kid.getStatus() == 'public':
				if kid.type == 'photo':
					photos += 1
				if kid.type == 'album':
					(_albums, _photos) = kid.getStats()
					albums += _albums + 1
					photos += _photos
		return (albums, photos)
class PhotoNode(Node):
	"""A photo."""
	def __init__(self, abspath, parent=None, title=u'', summary=[], license=u'', status=u'',date=u''):
		super(PhotoNode, self).__init__(parent, abspath, basename(abspath), title, summary, license, status, date)
		self.type = u'photo'
	def prettyprint(self,indentation='',style='info'):
		if style == 'info':
			format = u'%s\\ %-'+str(50-len(indentation))+'s %10s %7s %20s (%s)'
			print format % (indentation,self.getTitle(),self.getDate(),self.getStatus(),self.getLicense(),self.inputpath)
		elif style == 'mapping':
			format = u'%s+ %-'+str(20-len(indentation))+'s %-50s -> %20s [%20s]'
			print format % (indentation, self.getTitle(), self.getOutputPath(), self.getHTTPPath(), self.getThumbnail())
	def getImageOutputPath(self):
		return joinpath(self.parent.getOutputPath(),self.outputhandle)
	def getImageHTTPPath(self):
		return ''.join([self.parent.getDirHTTPPath(),self.outputhandle])
	def getOutputPath(self):
		return joinpath(self.parent.getOutputPath(),self.outputhandle+'.html')
	def getHTTPPath(self):
		return ''.join([self.parent.getDirHTTPPath(),self.outputhandle,'.html'])
	def getThumbnail(self):
		return ''.join([self.parent.getDirHTTPPath(),settings['special_glue'].join([settings['special_prefix'],'thumb',self.outputhandle])])
	def getThumbnailOutputPath(self):
		return joinpath(self.parent.getOutputPath(),settings['special_glue'].join([settings['special_prefix'],'thumb',self.outputhandle]))
	def getSmall(self):
		return ''.join([self.parent.getDirHTTPPath(),settings['special_glue'].join([settings['special_prefix'],'small',self.outputhandle])])
	def getSmallOutputPath(self):
		return joinpath(self.parent.getOutputPath(),settings['special_glue'].join([settings['special_prefix'],'small',self.outputhandle]))

def lastdir(path):
	path.rstrip('/')
	(pre, sep, last) = path.rpartition('/')
	return last
def readInfo(infofile):
	"""Reads out the given meta data file and returns a dictionary with all the information"""
	info = {}
	try:
		handle = open(infofile,'Ur')
	except IOError, msg:
		return
	try:
		data = unicode(handle.read(),settings['meta_encoding'])
	except UnicodeDecodeError, msg:
		output(u'Failed to parse meta data file \'%s\' because of incorrect encoded characters: %s' % (infofile, msg))
		return
	except IOError, msg:
		output(u'Failed to read file \'%s\': %s' % (infofile, msg))
		return
	finally:
		handle.close()
	# Dropping all the newlines at the end of the file
	# so it doesn't matter if you terminate your last line or not
	mode = 'tags'
	partial = u''
	lines = data.rstrip('\n').split('\n')
	for line in lines:
		if mode == 'tags':
			(command, sep, value) = line.partition(':')
			command = command.lower()
			value = value.lstrip('\t ')
			if not sep:
				mode = 'summary'
				info['summary'] = []
				if line:
					info['summary'].append(line)
			else:
				# US-UK spelling
				if command in ('licence',): command = 'license'
				if info.has_key(command):
					output(u'WARNING: Overwriting value \'%s\': \'%s\' becomes \'%s\'' % (command, info[command], value))
				info[command] = value
		else:
			if line == '':
				info['summary'].append(partial)
				partial = u''
			else:
				if partial:
					partial += ' '+line
				else:
					partial  = line
				#if info['summary'][-1] == '\n':
				#	info['summary'] += ''.join(['\n',line])
				#else:
				#	info['summary'] += ''.join([' ',line])
	if partial:
		info['summary'].append(partial)
	return info
def updateInfo(node, info):
	if info.has_key('title'):   node.setTitle(info['title'])
	if info.has_key('summary'): node.setSummary(info['summary'])
	# US spelling
	if info.has_key('license'):
		if info['license'].lower() in ('copyright','copyrighted'):
			node.setLicense(u'Copyright')
		else:
			node.setLicense(info['license'])
	if info.has_key('date'):
		if not node.setDate(info['date']):
			print 'Warning: Date value for \'%s\' was not accepted' % (node.getTitle())
	if info.has_key('status'):  node.setStatus(info['status'])
	if info.has_key('published'):
		if info['published'].lower() in ('yes','y'):
			node.setStatus(u'public')
		elif info['published'].lower() in ('no','n'):
			node.setStatus(u'locked')
		else:
			print 'Unknown value \'%s\' for \'%s\'. Possible options: yes, y, no, n.'
def makeLeaf(image, parent):
	"""Generates a leaf."""
	assert type(image) == type(str(''))
	info = readInfo(''.join([image,'.txt']))
	if info:
		leaf = PhotoNode(image, parent)
		updateInfo(leaf, info)
		# Custom updating
		if info.has_key('title'):
			leaf.setTitle(info['title'])
		else:
			(name, sep, extension) = basename(image).rpartition('.')
			name = unicode(name, settings['fs_encoding'], 'ignore')
			name = name.replace('.',' ')
			name = name.replace('_',' ')
			leaf.setTitle(name)
		if leaf.getDate() == u'':
			if parent.getDate() != u'':
				leaf.setDate(parent.getDate())
			else:
				stats = stat(leaf.inputpath)
				leaf.setDate(u''+strftime('%Y/%m/%d',localtime(stats.st_mtime)))
		return leaf
def makeTree(directory, parent=None):
	"""Generates a tree."""
	assert type(directory) == type(str('')),'Yeah... dont think so... %s' % (type(directory))
	info = readInfo(joinpath(directory,'metadata.txt'))
	if not info:
		output(u'Could not process directory "%s", perhaps you\'re missing the metadata.txt file?' % (raw2uni(directory),))
		return
	rootnode = DirNode(directory,parent)
	updateInfo(rootnode,info)
	# Adding kids to the node
	dirlist = listdir(directory)
	dirlist.sort()
	for relative in dirlist:
		kidpath = joinpath(directory,relative)
		if isdir(kidpath):
			kidnode = makeTree(kidpath,rootnode)
			if kidnode:
				rootnode.addKid(kidnode)
		elif isfile(kidpath):
			kidnode = makeLeaf(kidpath,rootnode)
			if kidnode:
				rootnode.addKid(kidnode)
		else:
			output(u'Not doing anything with %s' % kidpath)
	return rootnode

def tagescape(string):
	string = string.replace('<','&lt;')
	string = string.replace('>','&gt;')
	return string
def generateHeader(node):
	return ''.join(['<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n',
	                '<html xmlns="http://www.w3.org/1999/xhtml">\n<head>\n<title>',
	                tagescape(node.getTitle()),
	                '</title>\n<link rel="stylesheet" type="text/css" href="',
	                settings['css_location'],
	                '" />\n</head>\n<body>\n<div class="body">\n<div class="header">'+settings['title']+'</div>',
	                '\n'])
def generateLicenseHTML(node):
	if node.type == u'album':
		logo      = ''
		substring = ''
		if node.getLicense().lower() == u'public domain':
			if settings['publicdomainlogo_location']:
				logo      = '<img src="/publicdomain.png" alt="Public Domain"/><br />'
			substring = 'released in the public domain'
		elif node.getLicense().lower() == u'copyright':
			if settings['copyrightlogo_location']:
				logo      = '<img src="/copyright.png" alt="Copyright Protected"/><br />'
			substring = 'protected by copyright'
		elif node.getLicense().lower() == u'unknown' or node.getLicense() == u'':
			logo        = '<img src="/unknown.png" alt="Unknown license"/><br />'
			substring = 'dubious licensed'
		else:
			logo      = ''
			substring = 'released under the %s license' % (node.getLicense())
		return '%sMost of the photos in this specific album are %s, but please check the license of every photo individually for specifc details.<br />' % (logo, substring)
	if node.type == u'photo':
		if node.getLicense().lower() == u'public domain':
			substring  = '<img src="/publicdomain.png" alt="Public Domain"/><br />'
			substring += 'The author of this work hereby waives all claim of copyright (economic and moral) in this work and immediately places it in the public domain; it may be used, distorted or destroyed in any manner whatsoever without further attribution or notice to the creator.'
		elif node.getLicense().lower() == u'copyright':
			substring  = '<img src="/copyright.png" alt="Copyright Protected"/><br />'
			substring += 'This photo is protected by copyright, please do not use it in any way.'
		elif node.getLicense().lower() == u'unknown' or node.getLicense() == u'':
			substring =  'The license of this photo is unknown. Your safest bet would be to assume it has a copyright license (<b>NOTE</b>: I\'m not claiming it).'
		else:
			substring = 'This photo has been released under the %s license.' % (node.getLicense())
		return substring
	raise
def generateFooter(node):
	# <img src="/publicdomain.png" alt="Public Domain"/><br />All content of this site are placed in the <a href="http://en.wikipedia.org/wiki/Public_domain">public domain</a>
	# <div class="smalltext">I grant anyone the right to use the non-Switzerland pictures for any purpose, without any conditions.<br />
	string  = u'<div class="footer">'
	string += generateLicenseHTML(node)
	string += '<div class="smalltext">For questions, remarks or just a friendly note, contact me at <i>photos<b>  </b>@ <span>qtea</span></i>.<i>nl</i></div>'
	string += '<div class="extrasmalltext">Site generated with <a href="/generate.py">GalGen</a>.</div>'
	string += '<div class="w3c"><p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml11" alt="Valid XHTML 1.1" height="31" width="88" /></a></p></div>'
	string += '</div></div>' # for the div.body and div.footer elements
	string += '</body>\n</html>' # 
	return string
def generateTitle(node):
	return node.getTitle()
def getBreadcrumbs(node):
	crumbs = [node.getTitle()]
	while node.parent:
		crumbs.append(u'<a href="'+node.parent.getHTTPPath()+'">'+node.parent.getTitle()+'</a>')
		node = node.parent
	# Makes more sense this way
	crumbs.reverse()
	return crumbs
def generateMiscbar(node):
	string = u'<div class="misc">'
	string += ' / '.join(getBreadcrumbs(node))
	if node.type == 'album':
		(albums, photos) = node.getStats()
		string += u'<span class="stats">'+str(photos)+' photos and '+str(albums)+' albums</span>'
	elif node.type == 'photo':
		string += u'<span class="stats">...</span>'
	string += '</div>'
	return string
def uni2raw(unicode):
	assert type(unicode) == type(u'')
	return unicode.encode(settings['output_encoding'])+'\n'
def raw2uni(unicode, encoding='UTF-8'):
	return unicode.decode(encoding, 'replace')
def generateBox(node):
	if node.type == 'album':
		(albums, photos) = node.getStats()
		string  = u'<div class="albumbox">'
		string += u'<a href="'+node.getHTTPPath()+'"><img src="'+node.getThumbnail()+'" alt="" /></a>'
		string += u'<div class="albumtitle">'+node.getTitle()+'</div>'+str(albums)+' albums, '+str(photos)+' photos'
		string += u'</div>'
		return string
	elif node.type == 'photo':
		string  = u'<div class="photobox">'
		string += u'<div class="photo"><a href="'+node.getHTTPPath()+'"><img src="'+node.getThumbnail()+'" alt="" /></a></div>'
		string += u'<div class="gallerytitle"><b>'+node.getTitle()+'</b></div>'
		date = node.getDate()
		if date != '':
			string += u'<div class="date">'+node.getDate()+'</div>'
		string += u'</div>'
		return string
	else: raise
def generateNewest(albumnode):
	return u'<div class="new"><div class="newbanner"><img src="/newest.png" alt="" /></div><div class="photobox">Under construction...</div></div>'
def processText(lines):
	outputlist = []
	# Processing URLs
	#
	# URL links are enclosed in square brackets []. 
	# If the first word starts with a protocol it will be used as the destination
	# and the rest of the words will be used as the visible part of the link.
	# Otherwise if the last word start wtih a protocol it will be used as the destination
	# and the rest of the words will be used as the visible part of the link.
	# If there is only 1 word and it has a protocol, it will be used for both the
	# destination and the visible part.
	#
	# Otherwise the text, brackets included, is literally returned.

	#
	# output = the string that will be returned, will grow or stay the same during the execution of this function
	# text   = the yet-to-be-processed text, will shrink with every loop (no excuses!)
	for text in lines:
		output = ''
		start  = text.find('[')
		end    = 0
		while start > -1:
			end  = text.find(']',start)
			if end > -1:
				words = text[start+1:end].split(' ')
				(protocol, sep, address) = words[0].partition('://')
				if sep:
					# If we have only 1 word, use this for both the destination and the visible part
					if len(words) == 1:
						link = '<a href="'+words[0]+'">'+words[0]+'</a>'
					else:
						link = '<a href="'+words[0]+'">'+' '.join(words[1:])+'</a>'
				else:
					(protocol, sep, address) = words[-1].partition('://')
					if sep:
						link = '<a href="'+words[-1]+'">'+' '.join(words[:-1])+'</a>'
					else:
						link = '['+' '.join(words)+']'
				output += ''.join([text[:start],link])
				text = text[end+1:]
			else:
				# if there is no end bracket we are done with this line
				output = text
				text = u''
			start = text.find('[')
		output += text
		outputlist.append(output)
	return '<br />'.join(outputlist)
def generateSummary(node):
	return u'<div class="summary">'+processText(node.getSummary())+'</div>'
def generatePhoto(photonode):
	string  = u'<div class="smallphoto">'
	string += u'<a href="'+photonode.getImageHTTPPath()+'">'
	string += u'<img src="'+photonode.getSmall()+'" alt="" />'
	string += u'</a>'
	string += u'</div>'
	return string

def generateAlbumHTML(dirnode):
	if not isdir(dirnode.getOutputPath()):
		makedirs(dirnode.getOutputPath())
	handle = open(joinpath(dirnode.getOutputPath(),settings['album_index_file']),'w')
	handle.write(uni2raw(generateHeader(dirnode)))
	handle.write(uni2raw(generateNewest(dirnode)))
	handle.write(uni2raw(generateMiscbar(dirnode)))
	# Filtering and ordering the kids
	albumkids = []
	photokids = []
	for kid in dirnode.kids:
		if kid.getStatus() == u'public' or kid.getStatus() == u'private':
			if kid.type == 'album':
				albumkids.append(kid)
			else:
				photokids.append(kid)
	for kid in albumkids+photokids:
		handle.write(uni2raw(generateBox(kid)))
	handle.write(uni2raw(generateSummary(dirnode)))
	handle.write(uni2raw(generateFooter(dirnode)))
	handle.close()
def generatePhotoHTML(photonode):
	# Image  input fs path:     photonode.inputpath
	# Image output fs path:     photonode.getImageOutputPath()
	# Thumbnail output fs path: photonode.getThumbnailOutputPath()
	# Small output fs path:     photonode.getSmallOutputPath()
	#
	# HTTP Link to description page: photonode.getHTTPPath()
	# HTTP Link to image:            photonode.getImageHTTPPath()
	# HTTP Link to thumbnail:        photonode.getThumbnail()
	# HTTP Link to small:            photonode.getSmall()
	if not isdir(dirname(photonode.getOutputPath())):
		makedirs(dirname(photonode.getOutputPath()))
	if not isfile(photonode.getImageOutputPath()):
		copyfile(photonode.inputpath,photonode.getImageOutputPath())
	if not isfile(photonode.getThumbnailOutputPath()):
		print 'Creating %s...' % (photonode.getThumbnailOutputPath()),
		stdout.flush()
		proc = Popen(['convert',photonode.inputpath,'-resize','200x200',photonode.getThumbnailOutputPath()])
		if proc.wait() != 0:
			print 'Failure!'
		else:
			print 'OK.'
	if not isfile(photonode.getSmallOutputPath()):
		print 'Creating %s...' % (photonode.getSmallOutputPath()),
		stdout.flush()
		proc = Popen(['convert',photonode.inputpath,'-resize','800x800',photonode.getSmallOutputPath()])
		if proc.wait() != 0:
			print 'Failure!'
		else:
			print 'OK.'
	handle = open(photonode.getOutputPath(),'w')
	handle.write(uni2raw(generateHeader(photonode)))
	handle.write(uni2raw(generateMiscbar(photonode)))
	handle.write(uni2raw(generatePhoto(photonode)))
	handle.write(uni2raw(generateSummary(photonode)))
	handle.write(uni2raw(generateFooter(photonode)))
	handle.close()

def generateNodeHTML(node):
	if node.processed:
		# Already processed this node, skipping it
		return
	if node.type == 'album':
		generateAlbumHTML(node)
		node.processed = True
	elif node.type == 'photo':
		generatePhotoHTML(node)
		node.processed = True
	else:
		raise

def generateHTML(node):
	# Depth first
	if node.type == 'album':
		for kid in node.kids:
			if kid.getStatus() != u'locked':
				generateHTML(kid)
	generateNodeHTML(node)
	#generateAlbumHTML(tree)

def main(inputdirs,outputdirectory,httpprefix):
	"""Processes the settings and outputs the gallery."""
	tree = None
	while not tree:
		try:
			tree = makeTree(inputdirs.pop(0), None)
		except IndexError:
			print 'There were no useful input directories. Start this script with --help to get some information about how to use meta data files.'
			return
	#for dir in inputdirs[:1]:
		# Using first directory as root
	#	tree = makeTree(dir,None)
	tree.setOutputPath(outputdirectory)
	tree.setHTTPPath(httpprefix)
	for dir in inputdirs[1:]:
		# And putting the kids of the other directories into this directory
		subtree = makeTree(dir,tree)
		tree.addKids(subtree.kids)
	output(u'My tree:')
	tree.prettyprint('','info')
	# Now, lets write away the pages!
	if not pretend:
		print 'Generating HTML...'
		generateHTML(tree)
		print 'HTML Generated.'
def output(unicode):
	assert type(unicode) == type(u''),'You silly bastard!'
	print unicode.encode('utf8','replace')

if __name__ == '__main__':
	desc = """Generates a static photo gallery.

Every directory that you want to process should contain a file called
metadata.txt, and for every image foo.jpeg there needs to be a file
foo.jpeg.txt before it gets included in the gallery.

The metadata files are separated in two sections, it starts with a number of
lines declaring properties, followed by an empty line, followed by the
description of the gallery/image.  Properties can be things like 'summary',
'owner', 'license' and 'title'.

An example metadata.txt file:
Title:   My Own Gallery
License: Public domain
Status:  Public
Owner:   Mr. Me

This is my very own gallery, enjoy!"""

	optionparser = OptionParser(usage='Usage: %prog [options]',
			description='Generates a static photo gallery.')
	optionparser.set_defaults(pretend=False)
	optionparser.add_option('-p', '--pretend', dest='pretend', action='store_true', help='Don\'t actually perform the renaming, only show what would be done.')
	(options, pa) = optionparser.parse_args()
	pretend = options.pretend
	main(settings['inputdirs'],settings['outputdir'],settings['httpprefix'])
