#!/usr/bin/env python

#-----------------------------------------------------------------------------
# Copyright (c) 2013, The BiPy Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------

__author__ = "Daniel McDonald"
__copyright__ = "Copyright 2013, The pyqi Project"
__credits__ = ["Greg Caporaso", "Daniel McDonald", "Doug Wendel",
               "Jai Ram Rideout"]
__license__ = "BSD"
__version__ = "0.2.0"
__maintainer__ = "Daniel McDonald"
__email__ = "mcdonadt@colorado.edu"

import importlib
import textwrap
from sys import argv, exit, stderr
from pyqi.core.interface import get_command_names, get_command_config
from pyqi.core.interfaces.optparse import optparse_main, optparse_factory
from os.path import basename
from string import ljust 

### we actually have some flexibility here to make the driver interface agnostic as well

TERM_WIDTH = 80
INDENT = 3

def usage(cmd_cfg_mod, command_names):
    """Modeled after git..."""
    # limit to a reasonable number of characters
    valid_cmds = []
    invalid_cmds = []
    for c in command_names:
        cmd_cfg, error_msg = get_command_config(cmd_cfg_mod, c,
                                                exit_on_failure=False)

        if cmd_cfg is None:
            invalid_cmds.append((c, error_msg))
        else:
            desc = cmd_cfg.CommandConstructor.BriefDescription
            valid_cmds.append((c, desc))

    # determine widths
    max_cmd = max(map(lambda x: len(x[0]), valid_cmds + invalid_cmds))
    desc_limit = TERM_WIDTH - (INDENT + max_cmd + INDENT)
    cmd_end = INDENT + max_cmd + INDENT

    print "usage: %s <command> [<args>]" % argv[0]
    print
    print "The currently available commands are:"

    # format:
    # indent command indent description
    for c, desc in valid_cmds:
        cmd_formatted = ljust(''.join([' ' * INDENT, c]), cmd_end)
        print ''.join([cmd_formatted, desc[:desc_limit]])

    if invalid_cmds:
        print
        print "The following commands could not be loaded:"

        for c, error_msg in invalid_cmds:
            cmd_formatted = ljust(''.join([' ' * INDENT, c]), cmd_end)
            print ''.join([cmd_formatted, 'Error: %s' % error_msg])

    print
    print "See '%s help <command>' for more information on a specific command." % argv[0]
    exit(0)

def get_cmd_obj(cmd_cfg_mod, cmd):
    """Get a ``Command`` object"""
    cmd_cfg, _ = get_command_config(cmd_cfg_mod, cmd)
    return optparse_factory(cmd_cfg.CommandConstructor, cmd_cfg.usage_examples, 
                            cmd_cfg.inputs, cmd_cfg.outputs,
                            cmd_cfg.__version__)

def help_(cmd_cfg_mod, cmd):
    """Dump the help for a ``Command``"""
    cmd_obj = get_cmd_obj(cmd_cfg_mod, cmd)
    optparse_main(cmd_obj, ['help', '-h'])

def assert_command_exists(command_name, command_names, driver_name):
    if command_name not in command_names:
        error_msg = '\n'.join(textwrap.wrap("Unrecognized command %s. Please "
                                            "make sure that you didn't make a "
                                            "typo in the command name." %
                                            command_name, TERM_WIDTH))
        error_msg += '\n\n'
        error_msg += '\n'.join(textwrap.wrap("To see a list of all available "
                                             "commands, run the following "
                                             "command:", TERM_WIDTH))
        stderr.write("%s\n\n%s%s\n\n" % (error_msg, ' ' * INDENT, driver_name))
        exit(1)


if __name__ == '__main__':
    driver_name = 'pyqi'
    cmd_cfg_mod = 'pyqi.interfaces.optparse.config'

    if '--' in argv:
        stop_idx = argv.index('--')

        if '--driver-name' in argv[:stop_idx]:
            idx = argv.index('--driver-name')
            argv.pop(idx)
            driver_name = argv[idx]

            if driver_name.startswith('--'):
                stderr.write("pyqi driver option --driver-name requires a "
                             "value, e.g. --driver-name mydriver\n")
                exit(1)

            argv.pop(idx)
            stop_idx -= 2

        if '--command-config-module' in argv[:stop_idx]:
            idx = argv.index('--command-config-module')
            argv.pop(idx)
            cmd_cfg_mod = argv[idx]

            if cmd_cfg_mod.startswith('--'):
                stderr.write("pyqi driver option --command-config-module "
                             "requires a value, e.g. --command-config-module "
                             "my.command.config.module\n")
                exit(1)

            argv.pop(idx)
            stop_idx -= 2

        if stop_idx != 1:
            # We're not pointing at a command name, so there must have been
            # other stuff that we didn't recognize.
            stderr.write("Unrecognized pyqi driver option(s): %s\n" %
                         ' '.join(argv[1:stop_idx]))
            exit(1)

        argv.pop(stop_idx)

    command_names = get_command_names(cmd_cfg_mod)

    if len(argv) == 1:
        argv[0] = driver_name
        usage(cmd_cfg_mod, command_names)
    else:
        cmd_name = argv[1]

        if cmd_name.lower() in ['help', '--help', '-?', '-h']:
            if not len(argv) > 2:
                argv[0] = driver_name
                usage(cmd_cfg_mod, command_names)

            help_cmd = argv[2]
            assert_command_exists(help_cmd, command_names, driver_name)

            # tears. 
            # .
            # this voodoo is to coerce optparse/argparse to dump the program
            # name at usage and examples correctly.
            argv[0] = ' '.join([driver_name, help_cmd])
            help_(cmd_cfg_mod, help_cmd)
        else:
            assert_command_exists(cmd_name, command_names, driver_name)

            # see the note about crying about tears.
            argv[0] = ' '.join([driver_name, cmd_name])
            cmd_obj = get_cmd_obj(cmd_cfg_mod, cmd_name)

            # execute FTW
            optparse_main(cmd_obj, argv[1:])
