# Filesystem utilities

# I've gratuitously implemented some of these in a functional stylee
# (probably not even very well)
# Feel free to increase betterness.

import os

from helpers import log

# Return all the files below 'rootdir'/'leaf' as paths relative to 'leaf'.
# Directory basenames must pass dirnameFilter to be examined;
# file basenames must pass filenameFilter to be included.
def findAllFilesInChildren(rootdir, leaf='',
                           filenameFilter=lambda x:True,
                           dirnameFilter=lambda x:True):
    # This might involve less cons'ing if done imperatively
    top = os.path.join(rootdir, leaf)
    accum = []
    for f in os.listdir(top):
        full = os.path.join(top, f)
        if (os.path.isfile(full) and filenameFilter(f)):
            accum.append(os.path.join(leaf, f))
        elif (os.path.isdir(full) and dirnameFilter(f)):            
            accum.extend(findAllFilesInChildren(rootdir,
                                                os.path.join(leaf, f),
                                                filenameFilter, dirnameFilter))
    return accum
    
#     return reduce(lambda accum, (filename, fullpath):
#                   ((os.path.isdir(fullpath) and \
#                    dirnameFilter(filename) and \
#                    accum + findAllFileChildren(fullpath,
#                                        filenameFilter,
#                                        dirnameFilter)) or \
#                   (os.path.isfile(fullpath) and \
#                    filenameFilter(filename) and \
#                    accum + [fullpath]) or accum),
#                   # all the basenames and full filenames
#                   [(name, os.path.join(rootdir, name)) \
#                    for name in os.listdir(rootdir)],
#                   [])

# Return all files in directory below root and above or in 'root/leaf',
# that pass filt(basename(filename)), in breadth-first order.
def findAllFilesInAncestors(root, leaf, filt):
    # dirname gives the same directory if you have a trailing '/'
    leaf = os.path.normpath(leaf)
    full = os.path.join(root, leaf)
    inhere = [pair[1] for pair in
              filter(lambda (name, fullpath):
                     (os.path.isfile(fullpath) and filt(name)),
                     [(name, os.path.join(full, name))
                      for name in os.listdir(full)])]
    if (leaf != '.'): # '.' is what normpath gives if passed ''
        inhere = findAllFilesInAncestors(root, os.path.dirname(leaf), filt) + inhere
    return inhere

# Return the first file that passes filt(basename(filename)), trying directories
# starting with 'root/leaf' and ending at root.  Return None if no match is found.
# We allow leaf to be a file, and just take the directory name if it is.
def findNearestFileInAncestor(root, leaf, filt):
    log("fsutil.findNearestFileInAncestor:: Trying to find file in '%s' under '%s'" % (leaf, root))
    full = os.path.join(root, leaf)
    # %%% Is this the Right Thing To Do?
    while (not os.path.isdir(full)):
        if leaf == '':
            log("fsutil.indNearestFileInAncestor:: File not found '%s'" % (full))
            return None
        leaf = os.path.dirname(leaf)
        full = os.path.join(root, leaf)

    for name in os.listdir(full):
        filename = os.path.join(full, name)
        if (os.path.isfile(filename) and filt(name)):
            #print "File found: %s" % (filename)
            return filename
    leaf = os.path.normpath(leaf)
    if (leaf != '.'):
        return findNearestFileInAncestor(root, os.path.dirname(leaf), filt)
    else:
        return None
