import os, re
from distutils import util
from distutils.core import Command

class GenerateBisonGen(Command):

    command_name = 'generate_bgen'

    description = "regenerate BisonGen parsers"

    user_options = [
        ('force', 'f',
         'force rebuild (ignore timestamps)'),
        ]

    boolean_options = ['force']

    def initialize_options(self):
        self.bgen_files = None
        self.force = 0

    def finalize_options(self):
        self.set_undefined_options('generate', ('force', 'force'))
        self.bgen_files = self.distribution.bgen_files

    _include_re = re.compile('<\?include (?P<file>.*(?=\?>))\?>')
    def _find_includes(self, file):
        includes = []
        source = open(file).read()
        base = os.path.dirname(file)

        match = self._include_re.search(source)
        while match:
            filename = util.convert_path(match.group('file'))
            include = os.path.normpath(os.path.join(base, filename))
            includes.append(include)
            includes.extend(self._find_includes(include))
            match = self._include_re.search(source, match.end())
        return includes

    def get_source_files(self):
        sources = []
        for filename in self.bgen_files:
            sources.append(filename)
            includes = self._find_includes(filename)
            for filename in includes:
                if filename not in sources:
                    sources.append(filename)
        return sources

    def get_outputs(self):
        return []

    def run(self):
        if not self.bgen_files:
            return

        try:
            from BisonGen import Main
        except:
            self.warn('Could not load BisonGen, skipping rebuild')
            return

        outputs = []
        args = ['BisonGen']
        if self.force:
            args.append('--force')
        if not self.distribution.verbose:
            args.append('--quiet')
        for filename in self.bgen_files:
            outputs.extend(Main.Run(args + [filename]))
        return
