#!/usr/local/bin/python2.5
# http://www.davefancella.com/software/gensite.html

# This script generates a site with txt2tags
# Basically it copies all files over except those with txt2tags extension, those are
# run through txt2tags.

import os, sys
from optparse import OptionParser
from ConfigParser import ConfigParser

knownTools = {}
gensiteBase = None
defaultExtension = ".html"

def main():
    parser = OptionParser()
    
    parser.add_option("-i", "--input", default="./", help="input directory to generate from", dest="inputdir")
    parser.add_option("-o", "--output", default="./", help="directory to put all output in.  gensite will not create it if it doesn't exist, you must create it before running gensite", dest="outputdir")
    parser.add_option("-g", "--generate-config", action="store_true", default=False, dest="genConf", help="Generate a default config file.")
    (options, args) = parser.parse_args()

    if options.genConf is True:
        print "Generating default config file...",
        GenerateDefaultConfig()
        print "done!"
        sys.exit(0)

    config = None
    buildstatus = False
    
    if os.path.exists(options.inputdir):
        if os.path.exists(options.outputdir):
            if os.path.exists(os.path.join(options.inputdir, 'gensiterc') ):
                config = ConfigParser()
                config.read(os.path.join(options.inputdir, 'gensiterc') )
                Configure(config)
                
            targetDir = os.path.abspath(options.outputdir)
            os.chdir(options.inputdir)
            
            buildstatus, generatedFiles = gensite(targetDir, config)
            
            pruneDestination(targetDir, generatedFiles, config)
        else:
            print "Destination directory doesn't exist!"
            buildstatus = False
    else:
        print "Input directory doesn't exist!"
        buildstatus = False
        
    if buildstatus is True:
        sys.exit(0)
    else:
        sys.exit(10)
            
def copyFile(source, dest):
    theCommand = "cp -f " + source + " " + dest
    
    res1 = os.system(theCommand)
    res2 = os.system("chmod 664 " + dest)
    
    if res1 != 0 or res2 != 0:
        return False
        
    return True
            
def generate(theCommand, dest):
    print theCommand
    res1 = os.system(theCommand)
    res2 = os.system("chmod 664 " + dest)
    
    if res1 != 0 or res2 != 0:
        return False
    return True

def pruneDestination(targetdir, allowedFiles, config):
    os.chdir(targetdir)
    
    for (dirpath, dirnames, filenames) in os.walk('./', False):
        if dirpath.startswith('./'):
            dirpath = dirpath[2:]
            
        for aFile in filenames:
            testFile = os.path.join(dirpath, aFile)
            if testFile.startswith('./'):
                testFile = testFile[2:]
            
            if testFile in allowedFiles:
                pass
            else:
                # Delete the file
                print "Deleting '" + testFile + "', as it is not in the source directory and is not a generated file"
                os.remove(testFile)
        
        if dirpath == '':
            dirpath = "./"
        if len(os.listdir(dirpath)) == 0:
            print "Deleting '" + dirpath + "', as the directory is now empty."
            os.rmdir(dirpath)

# Returns a list of files that exist in svn, relative to the root of the checkout
def gensite(targetdir, config=None):
    global knownTools, defaultExtension
    
    allGeneratedFiles = []
    
    buildstatus = True
    
    globalIgnoreDirs = ['.svn', 'CVS', 'templates']
    globalIgnoreFiles = ['gensiterc']
    
    if config is not None:
        if config.has_section('main'):
            if config.has_option('main', 'ignoredirs'):
                globalIgnoreDirs += config.get('main', 'ignoredirs').split()
        if config.has_section('main'):
            if config.has_option('main', 'ignorefiles'):
                globalIgnoreFiles += config.get('main', 'ignorefiles').split()
    
    for (dirpath, dirnames, filenames) in os.walk('.'):
        if dirpath.startswith("./"):
            dirpath = dirpath[2:]
        # find and remove any files that are to be ignored
        for a in dirnames:
            if a.startswith("./"):
                a = a[2:]
            if a in globalIgnoreDirs:
                print "Directory " + a + " on ignore list, not entering."
                del dirnames[dirnames.index(a)]
        for a in filenames:
            if a.startswith("./"):
                a = a[2:]
            if a in globalIgnoreFiles:
                print "File " + a + " on ignore list, ignoring."
                del filenames[filenames.index(a)]
        
        # Create any directories that don't exist
        for a in dirnames:
            if a.startswith("./"):
                a = a[2:]
            if os.path.exists(os.path.join(targetdir, dirpath, a) ) is False:
                os.mkdir(os.path.join(targetdir, dirpath, a) )
                os.system('chmod 775 ' + os.path.join(targetdir, dirpath, a) )
                print "Making directory " + a
        
        # Iterate through filenames and do something with them, either copy or generate
        for aFile in filenames:
            if aFile.startswith("./"):
                aFile = aFile[2:]
            root, ext = os.path.splitext(aFile)
            ext = ext.lower()[1:]
            sourcefile = os.path.join(dirpath, aFile)
            destfile = ''
            generator = 'cp'

            if ext in knownTools.keys():
                theCommandLine = knownTools[ext]
                # Do the simple substitutions first
                theCommand = theCommandLine.replace("$SAME", gensiteBase)
                theCommand = theCommand.replace("$SOURCE", sourcefile)

                # Dig out the $DEST variable and determine if we need to provide
                # an extension, or if an extension is already provided.
                theNewExt = ".html"
                while theCommand.find("$DEST") > -1:
                    theLoc = theCommand.find("$DEST")
                    if len(theCommand[theLoc:]) > 5:
                        if theCommand[theLoc+5] == ".":
                            theNewExt = ""
                        else:
                            theNewExt = defaultExtension
                    else:
                        theNewExt = defaultExtension
                    destfile = os.path.join(targetdir, dirpath, root + theNewExt)
                    theCommand = theCommand.replace("$DEST", destfile, 1)
                    
                    genFile = os.path.join(dirpath, root + theNewExt)
                    if genFile.startswith('./'):
                        genFile = genFile[2:]
                    allGeneratedFiles.append(genFile )
            else:
                destfile = os.path.join(targetdir, dirpath, aFile)
                genFile = os.path.join(dirpath, aFile) 
                theCommand = "cp -f " + sourcefile + " " + destfile
                if genFile.startswith('./'):
                    genFile = genFile[2:]
                allGeneratedFiles.append(genFile)
            
            generateFile = False
            
            # If the file exists, check it's modified times.  If it doesn't exist, generate it
            if os.path.exists(destfile):
                if os.path.getmtime(sourcefile) > os.path.getmtime(destfile):
                    generateFile = True
                elif ext == 't2t':
                    generateFile = CheckTimestampsT2t(os.path.normpath(sourcefile), destfile)
            else:
                generateFile = True
                
            # generate the file now
            if generateFile is True:
                if generate(theCommand, destfile) is True:
                    pass
                else:
                    buildstatus = False
                    print "Error: generation of '" + sourcefile + "' to '" + destfile + "' failed."

                    
    return (buildstatus, allGeneratedFiles)


# This function opens sourcefile and checks every included file to see if the target file
# needs to be updated
def CheckTimestampsT2t(sourcefile, destfile, currentPath = None):
    sourcedir, sourcefile = os.path.split(sourcefile)
    
    if currentPath is None:
        currentPath = ""
    if os.path.getmtime(os.path.join(sourcedir, sourcefile) ) > os.path.getmtime(destfile):
        return True
    else:
        # if it's a txt2tags file, we need to scan for includes, in which case we just go ahead
        # and regenerate it because there's no sane way to handle it otherwise right now
        theFile = open(os.path.join(sourcedir, sourcefile), 'r')
        
        # Todo: turn this into a recursive function so it checks all included files, even files
        #   included by included files.
        for line in theFile:
            if line.lower().startswith('%!include:') or line.lower().startswith('%!includeconf:'):
                theInclude, theFileName = line.split()
                theDir, basename = os.path.split(theFileName)
                
                theFileName = os.path.join(sourcedir, theFileName.strip() )

                generateFile = CheckTimestampsT2t(theFileName, destfile)
                # Go ahead and return if we've got a match that says we need to generate
                if generateFile is True:
                    theFile.close()
                    return True
        
        return False
                
def Configure(confObj):
    global knownTools, defaultExtension
    print "Configuring gensite"
    if confObj.has_section('tools'):
        for opt in confObj.options('tools'):
            knownTools[opt] = confObj.get('tools',opt)
    if confObj.has_option('main', 'defaultextension'):
        theExt = confObj.get('main', 'defaultextension')
        if theExt.startswith("."):
            defaultExtension = theExt
        else:
            defaultExtension = "." + theExt
        
def GenerateDefaultConfig():
    theFileContents = '''[main]

# This is a list of directories to ignore.  They will not be processed, and if they're
# already in the destination location, they will not be pruned.
ignoredirs = templates

# This is the default extension to use for files that are not copied, but generated, if
# the commandline doesn't provide an extension.  This is a convenience only.
defaultextension = html

[baselayout]
# This section provides rules for the basic layout of a page.  Subdirectories can override this
# It should be the key "layout" followed by a comma-separated list of files that should
# be concatenated together, where $CONTENT is the page that contains the content of the
# file.  This isn't implemented yet
layout = header.t2t, $CONTENT, footer.t2t

# basedir is the directory where all of these files can be found.  It will be prepended
# to each filename, so it can be left blank if you don't have such a thing.  It will
# not be prepended to variables.
basedir = templates

[tools]
# This section contains tools referenced by lower-case extension that are used to generate
# the files for the site.  If a tool isn't found here, the file is just copied.  A tool
# here should provide a command line for the tool.
#
# $SAME means the tool is in the same path as the gensite script.
# $PATH means the system-wide $PATH variable should be used to find the tool - not implemented yet
# $SOURCE refers to the source file with extension
# $DEST refers to the destination file without extension.  You should append the
#       extension, i.e. $DEST.html.  If this is not followed by a . , then the global
#       setting will be used (which defaults to html).
#
# Gensite has some builtin support for some files, most notably txt2tags and will do extra
# work of its own to determine if the file needs to be generated.
t2t = $SAME/txt2tags --target xhtml --css-sugar -i $SOURCE -o $DEST

'''
    theFile = open("gensiterc", "w")
    theFile.write(theFileContents)
    theFile.close()
    
if __name__ == "__main__":
    fullbase = os.path.realpath(sys.path.pop(0) )
    gensiteBase = fullbase
    
    main()

