## A simple photo gallery generator
## Ian Wienand <ianw@ieee.org>
## http://www.wienand.org
## Released into the public domain 

from optparse import OptionParser
import sys, os
import ihooks
import Image

usage = """
 	gallery.py --output=output --config=config.py
"""

includes = { "header" : "header.inc",
             "footer" : "footer.inc",
             "photos" : "photos.descr"
             }

class HTMLPage:
    global options, includes, config
    
    def __init__(self, photolist):
        self.menubar = MenuBar(photolist)
        self.photolist = photolist
        self.first = photolist.first()
        self.end = photolist.last()

    def write_page(self, filename, body):
        titleblock = """
<div class="title">
<h1>%s</h1>
<h2>%s</h2>
<h3>%s - %s - %s</h3>
</div> """ % (config.title, config.extra_text, config.photographer, config.date, config.location)

        outfile = open(filename, "w")
        header = open(includes["header"], "r")
        for line in header.readlines():
            line = line.replace("%title%", config.page_title)
            line = line.replace("%stylesheet%", config.stylesheet)
            outfile.write(line)
        outfile.write(titleblock)
        outfile.write(body)
        footer = open(includes["footer"], "r")        
        header.close()
        for line in footer.readlines():
            outfile.write(line)
        footer.close()
        outfile.close()

    def create_page(self, prev, cur, next):
        print "creating page " + cur.html_filename
        body = self.menubar.get_html(prev, next) + cur.get_html()
        self.write_page(options.output_dir + cur.html_filename, body)

class IndexHTMLPage(HTMLPage):

    def __init__(self, photolist):
        HTMLPage.__init__(self,photolist)

    def create_page(self):
        print "creating index page"

        i = 1
        menuimages = '<div class="menuimages"><table><tr>'
        #because the photolist iterator returns prev,cur,next we need to
        #take them all.
        for prev,image,next in self.photolist:
            menuimages = menuimages + '<td class="menuimages_image">'
            menuimages = menuimages + '<a href="%s">' % image.html_filename
            menuimages = menuimages + '<img src="%s" alt="%s" />' % (image.thumb_filename, image.alt)
            menuimages = menuimages + '</a></td>'
            if (i == options.menu_column_limit) and next != None:
                menuimages = menuimages + '</tr><tr>'
                i = 0
            else:
                i = i + 1
        menuimages = menuimages + '</tr></table></div>'

        self.write_page(options.output_dir + "index.html", menuimages)
    

class MenuBar:
    """ Create a menubar.
    On instanisation create clickable thumbnails.
    get_html() should return this and the direction buttons.
    """
    def __init__(self, photolist):
        self.first = photolist.first()
        self.last = photolist.last()

    def get_html(self, prev, next):
        str = '<div class="menubar"><table><tr>'
        str = str + '<td class="menubar_first"><a href="%s">|&lt;</a></td>' % self.first.html_filename
        str = str + '<td class="menubar_prev">'
        if prev != None:
            str = str + '<a href="%s">&lt;</a>' % prev.html_filename
        str = str + '</td>'
        str = str + '<td class="menubar_current"><a href="index.html">O</a></td>'
        str = str + '<td class="menubar_next">'
        if next != None:
            str = str + '<a href="%s">&gt;</a>' % next.html_filename
        str = str + '</td>'
        str = str + '<td class="menubar_last"><a href="%s">&gt;|</a></td>' % self.last.html_filename
        str = str + '</tr></table></div>'

        return str

class PhotoList:
    def __init__(self):
        self.list = []
        self.cur = 0

    def add(self, photo):
        self.list.append(photo)

    def first(self):
        return self.list[0]

    def last(self):
        return self.list[len(self.list)-1]

    def is_empty(self):
        if len(self.list) == 0:
            return True
        else:
            return False
    
    def __iter__(self):
        return self

    # the itererator returns a list
    # (prev, current, next)
    # with None for prev and next when on the first and last images
    def next(self):
        if self.cur == (len(self.list)):
            # reset iterator
            self.cur = 0
            raise StopIteration
        else:
            #get previous
            if self.cur == 0:
                images = [None]
            else:
                images = [self.list[self.cur - 1]]

            #put in current
            images.append(self.list[self.cur])

            #next
            if self.cur + 1 == len(self.list):
                images.append(None)
            else:
                images.append(self.list[self.cur + 1])

            #increment
            self.cur = self.cur + 1
            return images

class Photo:
    def __init__(self, input_filename, alt, descr):
        self.path, self.input_filename = os.path.split(input_filename)
        self.input_filename, self.ext  = os.path.splitext(self.input_filename)
        self.image_filename = "pictures/" + self.input_filename + ".jpg"
        self.thumb_filename = "thumbs/" + self.input_filename + "_thumb.jpg"
        self.html_filename = self.input_filename + ".html"
        self.alt   = alt
        self.descr = descr

        try:
            self.image = Image.open(input_filename)
        except IOError, err:
            print "%s" % (err)
            sys.exit(1)

        self.image.thumbnail((config.constrain_x,config.constrain_y), Image.ANTIALIAS)    
        self.image.save(options.output_dir+self.image_filename, "JPEG")
        
        self.image.thumbnail((config.thumbnail_x, config.thumbnail_y), Image.ANTIALIAS)
        self.image.save(options.output_dir+self.thumb_filename, "JPEG")


    def get_html(self):
        str = '<div class="photo"><table>'
        str = str + '<tr><td class="photo_frame"><img src="%s" alt="%s" /></td></tr>' % (self.image_filename, self.alt)
        str = str + '<tr><td class="photo_descr">%s</td></tr>' % self.descr
        str = str + '</table></div>'
        return str


def import_module(filename):
    """Import a module give as a filename"""
    loader = ihooks.BasicModuleLoader()
    path, file = os.path.split(filename)
    name, ext  = os.path.splitext(file)
    module = loader.find_module_in_dir(name, path)
    if not module:
        raise ImportError, name
    module = loader.load_module(name, module)
    return module


def create_dirs(directory):
    dir = os.path.dirname(directory)
    if dir and not os.path.isdir(dir):
        create_dirs(dir)
        os.mkdir(dir)

def create_gallery():
    global options, config

    parser = OptionParser(usage, version="%prog 1.0")
    parser.add_option("-o", "--output-dir", dest="output_dir",
                      help="Output directory", type="string", default=None)
    parser.add_option("-c", "--config", dest="config",
                      help="Config file", type="string", default=None)

    (options, args) = parser.parse_args()

    if options.output_dir == None:
        print "Please specify output directory"
        sys.exit(1)

    if options.config == None:
        print "Please specify config file"
        sys.exit(1)

    try:
        if not options.output_dir.startswith("/") or not options.output_dir.startswith("./"):
            options.output_dir = "./" + options.output_dir
        options.output_dir = options.output_dir + "/"
        create_dirs(options.output_dir)
        create_dirs(options.output_dir+"/thumbs/")
        create_dirs(options.output_dir+"/pictures/")
    except:
        print "Can't create directories"
        sys.exit(1)

    try:
        config = import_module(options.config).Config
    except ImportError:
        print "Invalid config %s" % options.config
        sys.exit(1)


    try:
        options.menu_column_limit = config.menu_column_limit
    except ValueError:
        options.menu_column_limit = 4

    photolist = PhotoList()
    for image in config.photos:
        filename, alt, descr = image
        photo = Photo(filename, alt, descr)
        photolist.add(photo)

    html = HTMLPage(photolist)
    IndexHTMLPage(photolist).create_page()
    
    menubar = MenuBar(photolist)

    for prev, cur, next in photolist:
        html.create_page(prev, cur, next)
        
if __name__ == '__main__':
    create_gallery()
