#!/usr/bin/env python
"""Git subcommand to provide operations on CVS repositories."""

from glob import glob
import os
import re
import string
import sys
import traceback

from cvsgit.cmd import Cmd
from cvsgit.i18n import _
from cvsgit.error import Error
import cvsgit.command
import cvsgit.utils

class CLI(Cmd):
    """Operate on a CVS repository using Git.

    Usage: %prog <command> [options] [arguments]

    git-cvs is a conduit for changesets between CVS and Git.  It
    currently provides only a unidirectional flow of changes between
    a CVS repository and a Git repository.

    git-cvs can track the HEAD branch (aka the "trunk") in a CVS
    repository."""

    def initialize_options(self):
        self.add_option('--profile', action='store_true', help=\
            _("Enable profiling output for this command."))
        self.add_option('--detailed-errors', action='store_true', help=\
            _("Display full stack traces on error and display the "
              "documentation for exceptions, if available."))

    def finalize_options(self):
        pass

    def run(self):
        if len(self.args) > 0:
            self.run_command_with_exception_handling(self.args)
        else:
            self.print_available_commands()

    def run_command_with_exception_handling(self, argv):
        try:
            self.run_command(self.args)
        except Error as e:
            if self.options.detailed_errors:
                self.error(str(e) + ' (details follow)')

                (exc_type, exc_value, exc_traceback) = sys.exc_info()
                print 'Traceback (most recent call last):'
                traceback.print_tb(exc_traceback)

                if type(e).__doc__:
                    print '-'*72
                    print type(e).__name__ + ':',
                    print cvsgit.utils.dedent(type(e).__doc__).rstrip()
                    print '-'*72
            else:
                self.error(str(e) + ' (--detailed-errors for more information)')

            exit(1)

    def run_command(self, argv):
        command = argv[0]
        klass = self.get_command_class(command)
        if klass is None:
            self.usage_error(_('invalid command: %s') % command)
        else:
            if self.options.profile:
                import cProfile
                import pstats
                import tempfile
                with tempfile.NamedTemporaryFile() as tmp:
                    filename = tmp.name
                    cProfile.runctx('klass().main(argv)', globals(),
                                    {'klass':klass, 'argv':argv},
                                    filename)
                    stats = pstats.Stats(filename)
                    stats.sort_stats('cumulative')
                    stats.print_stats(10)
                    stats.sort_stats('time')
                    stats.print_stats(10)
            else:
                klass().main(argv)

    def print_available_commands(self):
        print _("Available commands:")
        for command in sorted(self.get_command_names()):
            print "  " + command

    def get_command_names(self):
        modules = {}
        for path in cvsgit.command.__path__:
            pattern = os.path.join(path, '[!_]*.py')
            for filename in glob(pattern):
                module_name = os.path.basename(filename)[:-3]
                modules[module_name] = True
        return modules.keys()

    def get_command_class(self, command):
        if not command in self.get_command_names():
            return None

        klass_name = re.sub('[^A-Za-z_]', '_', command)
        module_name = '%s.%s' % ('cvsgit.command', command)
        __import__(module_name)
        module = sys.modules[module_name]

        try:
            try:
                klass = getattr(module, klass_name)
            except AttributeError:
                alt_name = string.join(map(lambda s: s.title(),
                                           klass_name.split('_')), '')
                klass = getattr(module, alt_name)
        except AttributeError:
            raise RuntimeError(
                _("invalid command '%s' (no class '%s' in module '%s')") \
                % (command, klass_name, module_name))
        return klass

if __name__ == '__main__':
    CLI()
