"""Command-line support for coverage.py"""

import getopt, sys

from coverage.annotate import AnnotateReporter
from coverage.summary import SummaryReporter
from coverage.execfile import run_python_file

USAGE = r"""
Coverage version %(__version__)s

Usage:

coverage -x [-p] MODULE.py [ARG1 ARG2 ...]
    Execute module, passing the given command-line arguments, collecting
    coverage data. With the -p option, write to a temporary file containing
    the machine name and process ID.

coverage -e
    Erase collected coverage data.

coverage -c
    Combine data from multiple coverage files (as created by -p option above)
    and store it into a single file representing the union of the coverage.

coverage -r [-m] [-i] [-o DIR,...] [FILE1 FILE2 ...]
    Report on the statement coverage for the given files.  With the -m
    option, show line numbers of the statements that weren't executed.

coverage -a [-d DIR] [-i] [-o DIR,...] [FILE1 FILE2 ...]
    Make annotated copies of the given files, marking statements that
    are executed with > and statements that are missed with !.  With
    the -d option, make the copies in that directory.  Without the -d
    option, make each copy in the same directory as the original.

-h  Print this help.

-i  Ignore errors while reporting or annotating.

-o DIR,...
    Omit reporting or annotating files when their filename path starts with
    a directory listed in the omit list.
    e.g. coverage -i -r -o c:\python25,lib\enthought\traits

Coverage data is saved in the file .coverage by default.  Set the
COVERAGE_FILE environment variable to save it somewhere else.
""".strip()


class CoverageScript:
    def __init__(self):
        import coverage
        self.covpkg = coverage
        self.coverage = coverage.coverage()

    def help(self, error=None):     #pragma: no cover
        if error:
            print error
            print
        print USAGE % self.covpkg.__dict__

    def command_line(self, argv, help_fn=None):
        # Collect the command-line options.
        help_fn = help_fn or self.help
        OK, ERR = 0, 1
        settings = {}
        optmap = {
            '-a': 'annotate',
            '-c': 'combine',
            '-d:': 'directory=',
            '-e': 'erase',
            '-h': 'help',
            '-i': 'ignore-errors',
            '-m': 'show-missing',
            '-p': 'parallel-mode',
            '-r': 'report',
            '-x': 'execute',
            '-o:': 'omit=',
            }
        short_opts = ''.join(map(lambda o: o[1:], optmap.keys()))
        long_opts = optmap.values()
        options, args = getopt.getopt(argv, short_opts, long_opts)
        for o, a in options:
            if optmap.has_key(o):
                settings[optmap[o]] = True
            elif optmap.has_key(o + ':'):
                settings[optmap[o + ':']] = a
            elif o[2:] in long_opts:
                settings[o[2:]] = True
            elif o[2:] + '=' in long_opts:
                settings[o[2:]+'='] = a

        if settings.get('help'):
            help_fn()
            return OK

        # Check for conflicts and problems in the options.
        for i in ['erase', 'execute']:
            for j in ['annotate', 'report', 'combine']:
                if settings.get(i) and settings.get(j):
                    help_fn("You can't specify the '%s' and '%s' "
                              "options at the same time." % (i, j))
                    return ERR

        args_needed = (settings.get('execute')
                       or settings.get('annotate')
                       or settings.get('report'))
        action = (settings.get('erase') 
                  or settings.get('combine')
                  or args_needed)
        if not action:
            help_fn("You must specify at least one of -e, -x, -c, -r, or -a.")
            return ERR
        if not args_needed and args:
            help_fn("Unexpected arguments: %s" % " ".join(args))
            return ERR
        
        # Do something.
        self.coverage.parallel_mode = settings.get('parallel-mode')
        self.coverage.get_ready()

        if settings.get('erase'):
            self.coverage.erase()
        
        if settings.get('execute'):
            if not args:
                help_fn("Nothing to do.")
                return ERR
            
            # Run the script.
            self.coverage.start()
            try:
                run_python_file(args[0], args)
            finally:
                self.coverage.stop()
        
        if settings.get('combine'):
            self.coverage.combine()

        ignore_errors = settings.get('ignore-errors')
        show_missing = settings.get('show-missing')
        directory = settings.get('directory=')

        omit = settings.get('omit=')
        if omit:
            omit = omit.split(',')
        
        if settings.get('report'):
            reporter = SummaryReporter(self.coverage, show_missing, ignore_errors)
            reporter.report(args, omit_prefixes=omit)
        if settings.get('annotate'):
            reporter = AnnotateReporter(self.coverage, ignore_errors)
            reporter.report(args, directory, omit_prefixes=omit)
        
        return OK
    
# Main entrypoint.  This is installed as the script entrypoint, so don't
# refactor it away...
def main():
    return CoverageScript().command_line(sys.argv[1:])
