#!/usr/bin/python

import sys
import os
import optparse


def walkrepos(path, filter):
    '''yield every repository matched from filter(x) under path, recursively.'''
    def errhandler(err):
        if err.filename == path:
            raise err

    for root, dirs, files in os.walk(path, onerror=errhandler):
        if filter(root):
            yield root
        for d in dirs:
            dir = os.path.join(root, d)
            if filter(dir):
                yield dir
                dirs.remove(d)


def walkrepos_flat(path, filter):
    """yield every repository matched by filter(x) in the current directory"""
    if filter(path):
        yield path
    for i in os.listdir(path):
        dir = os.path.join(path, i)
        if filter(dir):
            yield dir


def repofilter(dir, x):
    '''generic filter which is currently sufficient accurate'''
    return os.path.isdir(os.path.join(dir, x))


def gitbarefilter(dir, x):
    '''special filter to match bare git repositories exact'''
    tmp = os.path.abspath(dir)
    if tmp.endswith(x):
        if len(os.path.basename(tmp)) >4:
            if os.path.exists(os.path.join(tmp, 'config')):
                return True
    return False


'''Various filter functions
input:   path to a repository
return:  either true or false
'''
repo_types = {'hg':      lambda dir=None: repofilter(dir, '.hg'),
              'git':     lambda dir=None: repofilter(dir, '.git'),
              'gitbare': lambda dir=None: gitbarefilter(dir, '.git'),
              'bzr':     lambda dir=None: repofilter(dir, '.bzr'),
              'darcs':   lambda dir=None: repofilter(dir, '_darcs')}


def parse_filter_spec(specstring):
    '''create internal filter specification from userstring'''
    return specstring.split(',')


def create_filter(spec):
    '''create the filter function possibly combinding several filter functions
    spec: ['git', 'hg', ...]
    return: lambda multiplex(dir) -> filterfun_git(dir) && true
                                  -> filterfun_hg(dir) && true
                                  -> false
    '''
    filter_funs = [ repo_types[i] for i in set(spec) ]
    def multipex(funs, dir):
        for i in funs:
            if i(dir):
                return True
    return lambda dir: multipex(filter_funs, dir)

if __name__ == "__main__":
    usage = "usage: %prog [options] <dir1> <dir2> ...\n\n" +\
            "%prog is a program to scan for repositories of different vcs in\nthe given directories.\n\n" +\
            "TYPE: " + ", ".join(sorted(repo_types.keys()))
    parser = optparse.OptionParser(usage)
    parser.add_option("-r", "--recursive", action="store_true", dest="recursive", default=False,
            help="Search the given directories recursive for mercurial repositories")
    parser.add_option("-t", "--type", dest="type", default="hg",
            help="Type of repository (default=%default, seperate with ',')")
    (opts, args) = parser.parse_args()

    '''default is walkrepos_flat and the current directory'''
    walkfunc = walkrepos_flat
    if opts.recursive:
        walkfunc = walkrepos
    if len(args) == 0:
        args.append(".")

    filter_spec = parse_filter_spec(opts.type)
    filter_fun = create_filter(filter_spec)

    printfunc = lambda x: sys.stdout.write(x + '\n')
    for i in args:
        filter(printfunc, walkfunc(i, filter_fun))

