#!python

from __future__ import print_function
import os
import sys
import argparse
import logging
import threading

if sys.version_info[0] < 3:
    import ConfigParser as configparser
else:
    import configparser

import bropkg


def print_error(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)


def config_items(config, section):
    # Same as config.items(section), but exclude default keys.
    defaults = {key for key, _ in config.items('DEFAULT')}
    items = sorted(config.items(section))
    return [(key, value) for (key, value) in items if key not in defaults]


def is_exe(path):
    return os.path.isfile(path) and os.access(path, os.X_OK)


def find_program(prog_name):
    path, _ = os.path.split(prog_name)

    if path:
        return prog_name if is_exe(prog_name) else ''

    for path in os.environ["PATH"].split(os.pathsep):
        path = os.path.join(path.strip('"'), prog_name)

        if is_exe(path):
            return path

    return ''


def file_is_not_empty(path):
    return os.path.isfile(path) and os.path.getsize(path) > 0


def find_configfile():
    configfile = os.environ.get('BRO_PKG_CONFIG_FILE')

    if configfile and file_is_not_empty(configfile):
        return configfile

    configfile = os.path.join(os.path.expanduser('~'), '.bro-pkg', 'config')

    if file_is_not_empty(configfile):
        return configfile

    return None


def create_config(configfile):
    config = configparser.SafeConfigParser()

    if configfile:
        if not os.path.isfile(configfile):
            print_error('error: invalid config file "{}"'.format(configfile))
            sys.exit(1)

        config.read(configfile)

    if not config.has_section('sources'):
        config.add_section('sources')

    if not config.has_section('paths'):
        config.add_section('paths')

    if not configfile:
        config.set('sources', 'bro', 'https://github.com/bro/packages')

    def config_option_set(config, section, option):
        return config.has_option(section, option) and config.get(section,
                                                                 option)

    def get_option(config, section, option, default):
        if config_option_set(config, section, option):
            return config.get(section, option)

        return default

    state_dir = get_option(config, 'paths', 'state_dir',
                           os.path.join(os.path.expanduser('~'), '.bro-pkg'))
    script_dir = get_option(config, 'paths', 'script_dir',
                            os.path.join(state_dir, 'script_dir'))
    plugin_dir = get_option(config, 'paths', 'plugin_dir',
                            os.path.join(state_dir, 'plugin_dir'))
    bro_dist = get_option(config, 'paths', 'bro_dist', '')

    config.set('paths', 'state_dir', state_dir)
    config.set('paths', 'script_dir', script_dir)
    config.set('paths', 'plugin_dir', plugin_dir)
    config.set('paths', 'bro_dist', bro_dist)

    return config


def create_manager(config):
    state_dir = config.get('paths', 'state_dir')
    script_dir = config.get('paths', 'script_dir')
    plugin_dir = config.get('paths', 'plugin_dir')
    bro_dist = config.get('paths', 'bro_dist')

    manager = bropkg.Manager(state_dir=state_dir, script_dir=script_dir,
                             plugin_dir=plugin_dir, bro_dist=bro_dist)

    for key, value in config_items(config, 'sources'):
        if not manager.add_source(name=key, git_url=value):
            print_error(
                'error: invalid source repo "{}" in config file '.format(key))
            sys.exit(1)

    return manager


class InstallWorker(threading.Thread):

    def __init__(self, manager, package_name, package_version):
        super(InstallWorker, self).__init__()
        self.manager = manager
        self.package_name = package_name
        self.package_version = package_version
        self.error = ''

    def run(self):
        self.error = self.manager.install(
            self.package_name, self.package_version)


def cmd_install(manager, args, config):
    if args.version and len(args.package) > 1:
        print_error(
            'error: "install --version" may only be used for a single package')
        sys.exit(1)

    join_timeout = 0.01
    tick_interval = 1

    for name in args.package:
        name = bropkg.package.canonical_url(name)
        time_accumulator = 0
        tick_count = 0
        worker = InstallWorker(manager, name, args.version)
        worker.start()

        while worker.isAlive():
            worker.join(join_timeout)
            time_accumulator += join_timeout

            if time_accumulator >= tick_interval:
                if tick_count == 0:
                    print('installing "{}"'.format(name), end='')
                else:
                    print('.', end='')

                sys.stdout.flush()
                tick_count += 1
                time_accumulator -= tick_interval

        if tick_count != 0:
            print('')

        if worker.error:
            print('failed installing "{}": {}'.format(name, worker.error))
            continue

        print('installed "{}"'.format(name))

        load_error = manager.load(name)

        if load_error:
            print('skipped loading "{}": {}'.format(name, load_error))
        else:
            print('loaded "{}"'.format(name))

        if args.version:
            ipkg = manager.pin(name)

            if ipkg:
                print('pinned "{}" at version: {} ({})'.format(
                    name, ipkg.status.current_version,
                    ipkg.status.current_hash))
            else:
                print('failed pinning "{}"'.format(name))


def cmd_remove(manager, args, config):
    for name in args.package:
        name = bropkg.package.canonical_url(name)

        if manager.remove(name):
            print('removed "{}"'.format(name))
        else:
            print('failed removing "{}": no such package installed'.format(name))


def outdated(manager):
    return [ipkg.package.qualified_name()
            for ipkg in manager.installed_packages()
            if ipkg.status.is_outdated]


def cmd_refresh(manager, args, config):
    src_pkgs_before = {i.qualified_name() for i in manager.source_packages()}
    outdated_before = {i for i in outdated(manager)}
    manager.refresh()
    src_pkgs_after = {i.qualified_name() for i in manager.source_packages()}
    outdated_after = {i for i in outdated(manager)}

    if src_pkgs_before == src_pkgs_after:
        print('refreshed source packages: no changes')
    else:
        print('source package changes:')
        diff = src_pkgs_before.symmetric_difference(src_pkgs_after)

        for name in diff:
            change = 'added' if name in src_pkgs_after else 'removed'
            print('\t{} {}'.format(change, name))

    if outdated_before == outdated_after:
        print('refreshed installed packages: no new outdated packages')
    else:
        print('installed packages are outdated:')
        diff = outdated_before.symmetric_difference(outdated_after)

        for name in diff:
            print('\t{}'.format(name))


def cmd_upgrade(manager, args, config):
    if args.package:
        pkg_list = args.package
    elif args.all:
        pkg_list = outdated(manager)
    else:
        print_error('error: either explicitly specify packages to upgrade'
                    ' or use `upgrade --all`.')
        sys.exit(1)

    for name in pkg_list:
        name = bropkg.package.canonical_url(name)
        res = manager.upgrade(name)

        if res:
            print('failed upgrading "{}": {}'.format(name, res))
        else:
            ipkg = manager.find_installed_package(name)
            print('upgraded "{}" ({})'.format(
                name, ipkg.status.current_version))


def cmd_load(manager, args, config):
    for name in args.package:
        name = bropkg.package.canonical_url(name)
        load_error = manager.load(name)

        if load_error:
            print('failed to load "{}": {}'.format(name, load_error))
        else:
            print('loaded "{}"'.format(name))


def cmd_unload(manager, args, config):
    for name in args.package:
        name = bropkg.package.canonical_url(name)

        if manager.unload(name):
            print('unloaded "{}"'.format(name))
        else:
            print(
                'failed unloading "{}": no such package installed'.format(name))


def cmd_pin(manager, args, config):
    for name in args.package:
        name = bropkg.package.canonical_url(name)
        ipkg = manager.pin(name)

        if ipkg:
            print('pinned "{}" at version: {} ({})'.format(
                name, ipkg.status.current_version, ipkg.status.current_hash))
        else:
            print('failed pinning "{}": no such package installed'.format(name))


def cmd_unpin(manager, args, config):
    for name in args.package:
        name = bropkg.package.canonical_url(name)
        ipkg = manager.unpin(name)

        if ipkg:
            print('unpinned "{}" from version: {} ({})'.format(
                name, ipkg.status.current_version, ipkg.status.current_hash))
        else:
            print(
                'failed unpinning "{}": no such package installed'.format(name))


def cmd_list(manager, args, config):
    if args.category == 'all':
        pkg_dict = dict()

        for ipkg in manager.installed_packages():
            pkg_dict[ipkg.package.qualified_name()] = True

        for pkg in manager.source_packages():
            pkg_qn = pkg.qualified_name()

            if pkg_qn not in pkg_dict:
                pkg_dict[pkg_qn] = False

        for pkg_qn, is_installed in sorted(pkg_dict.items()):
            output_fmt = '{} (installed)' if is_installed else '{}'
            print(output_fmt.format(pkg_qn))

    elif args.category == 'installed':
        installed_pkgs = [
            ipkg.package for ipkg in manager.installed_packages()]

        for pkg in sorted(installed_pkgs):
            print(pkg.qualified_name())

    elif args.category == 'not_installed':
        for pkg in sorted(manager.source_packages()):
            ipkg = manager.find_installed_package(pkg.name)

            if not ipkg:
                print(pkg.qualified_name())

    elif args.category == 'loaded':
        loaded_pkgs = [ipkg.package for ipkg in manager.loaded_packages()]

        for pkg in sorted(loaded_pkgs):
            print(pkg.qualified_name())

    elif args.category == 'unloaded':
        for ipkg in sorted(manager.installed_packages()):
            if not ipkg.status.is_loaded:
                print(ipkg.package.qualified_name())

    elif args.category == 'outdated':
        for ipkg in sorted(manager.installed_packages()):
            if ipkg.status.is_outdated:
                print(ipkg.package.qualified_name())

    else:
        raise NotImplementedError


def cmd_search(manager, args, config):
    src_pkgs = manager.source_packages()
    matches = set()

    for search_text in args.search_text:
        if search_text[0] == '/' and search_text[-1] == '/':
            import re

            try:
                regex = re.compile(search_text[1:-1])
            except re.error as error:
                print('invalid regex: {}'.format(error))
                sys.exit(1)
            else:
                for pkg in src_pkgs:
                    if regex.search(pkg.name_with_source_directory()):
                        matches.add(pkg)

                    for tag in pkg.tags():
                        if regex.search(tag):
                            matches.add(pkg)

        else:
            for pkg in src_pkgs:
                if search_text in pkg.name_with_source_directory():
                    matches.add(pkg)

                for tag in pkg.tags():
                    if search_text in tag:
                        matches.add(pkg)

    if matches:
        for match in sorted(matches):
            print(match.qualified_name())

            if 'tags' in match.index_data:
                print('\ttags: {}'.format(match.index_data['tags']))
    else:
        print("no matches")


def cmd_info(manager, args, config):
    if args.version and len(args.package) > 1:
        print_error(
            'error: "info --version" may only be used for a single package')
        sys.exit(1)

    for name in args.package:
        name = bropkg.package.canonical_url(name)
        info = manager.info(name, args.version)
        print('"{}" info:'.format(name))

        if info.invalid_reason:
            print('\tinvalid package: {}'.format(info.invalid_reason))
            continue

        pkg = info.package
        print('\tgit URL: {}'.format(pkg.git_url))
        print('\tversions: {}'.format(info.versions))

        if info.status:
            print('\tinstall status:')

            for key, value in sorted(info.status.__dict__.items()):
                print('\t\t{} = {}'.format(key, value))

        print('\tmetadata from version "{}":'.format(info.metadata_version))

        for key, value in sorted(info.metadata.items()):
            print('\t\t{} = {}'.format(key, value))


def cmd_config(manager, args, config):
    if args.config_param == 'all':
        if sys.version_info[0] < 3:
            from StringIO import StringIO
        else:
            from io import StringIO

        out = StringIO()
        config.write(out)
        print(out.getvalue())
        out.close()
    elif args.config_param == 'sources':
        for key, value in config_items(config, 'sources'):
            print('{} = {}'.format(key, value))
    else:
        print(config.get('paths', args.config_param))


def cmd_autoconfig(manager, args, config):
    bro_config = find_program('bro-config')

    if not bro_config:
        print_error('error: "bro-config" not found in PATH')
        sys.exit(1)

    if sys.version_info[0] < 3:
        from StringIO import StringIO
    else:
        from io import StringIO

    import subprocess
    cmd = subprocess.Popen([bro_config,
                            '--site_dir', '--plugin_dir', '--bro_dist'],
                           stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                           bufsize=1)
    script_dir, plugin_dir, bro_dist = (line.strip() for line in cmd.stdout)

    autoconfig = configparser.SafeConfigParser()
    autoconfig.add_section('sources')
    autoconfig.add_section('paths')
    autoconfig.set('sources', 'bro', 'https://github.com/bro/packages')
    autoconfig.set('paths', 'state_dir',
                   os.path.join(os.path.expanduser('~'), '.bro-pkg'))
    autoconfig.set('paths', 'script_dir', script_dir)
    autoconfig.set('paths', 'plugin_dir', plugin_dir)
    autoconfig.set('paths', 'bro_dist', bro_dist)

    out = StringIO()
    autoconfig.write(out)
    print(out.getvalue())
    out.close()


def cmd_env(manager, args, config):
    import subprocess

    bro_config = find_program('bro-config')
    bropath = os.environ.get('BROPATH')
    pluginpath = os.environ.get('BRO_PLUGIN_PATH')

    if bro_config:
        cmd = subprocess.Popen([bro_config, '--bropath', '--plugin_dir'],
                               stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                               bufsize=1)
        output = [line.strip() for line in cmd.stdout]

        if not bropath:
            bropath = output[0]

        if not pluginpath:
            pluginpath = output[1]

    bropaths = [p for p in bropath.split(':')] if bropath else []
    pluginpaths = [p for p in pluginpath.split(':')] if pluginpath else []

    bropaths.append(manager.bropath())
    pluginpaths.append(manager.bro_plugin_path())

    if os.environ['SHELL'].endswith('csh'):
        print('setenv BROPATH {}'.format(':'.join(bropaths)))
        print('setenv BRO_PLUGIN_PATH {}'.format(':'.join(pluginpaths)))
    else:
        print('export BROPATH={}'.format(':'.join(bropaths)))
        print('export BRO_PLUGIN_PATH={}'.format(':'.join(pluginpaths)))


def top_level_parser():
    top_parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='A command-line package manager for Bro.',
        epilog='Environment Variables:\n\n'
        '    ``BRO_PKG_CONFIG_FILE``:\t'
        'Same as ``--configfile`` option, but has less precedence.'
    )
    top_parser.add_argument('--version', action='version',
                            version='%(prog)s ' + bropkg.__version__)
    top_parser.add_argument('--configfile',
                            help='Path to Bro Package Manager config file.')
    top_parser.add_argument('--verbose', '-v', action='count', default=0,
                            help='Increase program output for debugging.'
                            ' Use multiple times for more output (e.g. -vvv).')
    return top_parser


def argparser():
    pkg_name_help = 'The name(s) of package(s) to operate on.  The package' \
                    ' may be named in several ways.  If the package is part' \
                    ' of a package source, it may be referred to by the' \
                    ' base name of the package (last component of git URL)' \
                    ' or its path within the package source.' \
                    ' If two packages in different package sources' \
                    ' have conflicting paths, then the package source' \
                    ' name may be prepended to the package path to resolve' \
                    ' the ambiguity. A full git URL may also be used to refer' \
                    ' to a package that does not belong to a source. E.g. for' \
                    ' a package source called "bro" that has a package named' \
                    ' "foo" located in "alice/bro-pkg.index" the following' \
                    ' names work: "foo", "alice/foo", "bro/alice/foo".'

    top_parser = top_level_parser()
    command_parser = top_parser.add_subparsers(
        title='commands', dest='command',
        help='See `%(prog)s <command> -h` for per-command usage info.')

    # install
    sub_parser = command_parser.add_parser(
        'install',
        help='Installs Bro packages.',
        description='Installs packages from a configured package source or'
                    ' directly from a git URL.  After installing, the package'
                    ' is marked as being "loaded" (see the ``load`` command).',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_install)
    sub_parser.add_argument(
        'package', nargs='+', help=pkg_name_help)
    sub_parser.add_argument(
        '--version', default=None,
        help='The version of the package to install.  Only one package may be'
        ' specified at a time when using this flag.  A version tag or branch'
        ' name may be specified here.  By default, the latest version tag is'
        ' installed, or if a package has no version tags, the "master"'
        ' branch is installed.  Using this flag also pins the installed'
        ' package to the given version (see the ``pin`` command).')

    # remove
    sub_parser = command_parser.add_parser(
        'remove',
        help='Uninstall a package.',
        description='Unloads (see the ``unload`` command) and uninstalls a'
        ' previously installed package.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_remove)
    sub_parser.add_argument('package', nargs='+', help=pkg_name_help)

    # refresh
    sub_parser = command_parser.add_parser(
        'refresh',
        help='Retrieve updated package information.',
        description='Retrieve latest metadata from package sources and checks'
        ' whether any installed packages have available upgrades.'
        ' Note that this does not actually upgrade any packages (see the'
        ' ``upgrade`` command for that).',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_refresh)

    # upgrade
    sub_parser = command_parser.add_parser(
        'upgrade',
        help='Upgrade installed packages to latest versions.',
        description='Uprades the specified package(s) to latest available'
        ' version.  If no specific packages are specified, then all installed'
        ' packages that are outdated and not pinned are upgraded.  For packages'
        ' that are installed with ``--version`` using a git branch name, the'
        ' package is updated to the latest commit on that branch, else the'
        ' package is updated to the highest available git version tag.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_upgrade)
    sub_parser.add_argument(
        'package', nargs='*', default=[], help=pkg_name_help)
    sub_parser.add_argument(
        '--all', action='store_true',
        help='When this option is used, all eligible packages will be upgraded'
        ' without having to explicitly specify each one as an argument.')

    # load
    sub_parser = command_parser.add_parser(
        'load',
        help='Register packages to be be auto-loaded by Bro.',
        description='The Bro Package Manager keeps track of all packages that'
        ' are marked as "loaded" and maintains a single Bro script that, when'
        ' loaded by Bro (e.g. via ``@load packages``), will load the scripts'
        ' from all "loaded" packages at once.'
        ' This command adds a set of packages to the "loaded packages" list.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_load)
    sub_parser.add_argument(
        'package', nargs='+', default=[],
        help='Name(s) of package(s) to load.')

    # unload
    sub_parser = command_parser.add_parser(
        'unload',
        help='Unregister packages to be be auto-loaded by Bro.',
        description='The Bro Package Manager keeps track of all packages that'
        ' are marked as "loaded" and maintains a single Bro script that, when'
        ' loaded by Bro, will load the scripts from all "loaded" packages at'
        ' once.  This command removes a set of packages from the "loaded'
        ' packages" list.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_unload)
    sub_parser.add_argument(
        'package', nargs='+', default=[], help=pkg_name_help)

    # pin
    sub_parser = command_parser.add_parser(
        'pin',
        help='Prevent packages from being automatically upgraded.',
        description='Pinned packages are ignored by the ``upgrade`` command.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_pin)
    sub_parser.add_argument(
        'package', nargs='+', default=[], help=pkg_name_help)

    # unpin
    sub_parser = command_parser.add_parser(
        'unpin',
        help='Allows packages to be automatically upgraded.',
        description='Packages that are not pinned are automatically upgraded'
        ' by the ``upgrade`` command',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_unpin)
    sub_parser.add_argument(
        'package', nargs='+', default=[], help=pkg_name_help)

    # list
    sub_parser = command_parser.add_parser(
        'list',
        help='Lists packages.',
        description='Outputs a list of packages that match a given category.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_list)
    sub_parser.add_argument('category', nargs='?', default='installed',
                            choices=['all', 'installed', 'not_installed',
                                     'loaded', 'unloaded', 'outdated'],
                            help='Package category used to filter listing.')

    # search
    sub_parser = command_parser.add_parser(
        'search',
        help='Search packages for matching names.',
        description='Perform a substring search on package names and metadata'
        ' tags.  Surround search text with slashes to indicate it is a regular'
        ' expression (e.g. ``/text/``).',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_search)
    sub_parser.add_argument(
        'search_text', nargs='+', default=[],
        help='The text(s) or pattern(s) to look for.')

    # info
    sub_parser = command_parser.add_parser(
        'info',
        help='Display package information.',
        description='Shows detailed information/metadata for given packages.'
        ' If the package is currently installed, additional information about'
        ' the status of it is displayed.  E.g. the installed version or whether'
        ' it is currently marked as "pinned" or "loaded."',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_info)
    sub_parser.add_argument(
        'package', nargs='+', default=[], help=pkg_name_help)
    sub_parser.add_argument(
        '--version', default=None,
        help='The version of the package metadata to inspect.  A version tag,'
        ' branch name, or commit hash and only one package at a time may be'
        ' given when using this flag.  If unspecified, the behavior depends'
        ' on whether the package is currently installed.  If installed,'
        ' the metadata will be pulled from the installed version.  If not'
        ' installed, the latest version tag is used, or if a package has no'
        ' version tags, the "master" branch is used.')

    # config
    sub_parser = command_parser.add_parser(
        'config',
        help='Show Bro Package Manager configuration info.',
        description='The default output of this command is a valid package'
        ' manager config file that corresponds to the one currently being used,'
        ' but also with any defaulted field values filled in.  This command'
        ' also allows for only the value of a specific field to be output if'
        ' the name of that field is given as an argument to the command.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_config)
    sub_parser.add_argument(
        'config_param', nargs='?', default='all',
        choices=['all', 'sources', 'state_dir', 'script_dir',
                 'plugin_dir', 'bro_dist'],
        help='Name of a specific config file field to output.')

    # autoconfig
    sub_parser = command_parser.add_parser(
        'autoconfig',
        help='Generate a Bro Package Manager configuration file.',
        description='The output of this command is a valid package manager'
        ' config file that is generated by using the ``bro-config`` script'
        ' that is installed along with Bro.  It is the suggested configuration'
        ' to use for most Bro installations.  For this command to work, the'
        ' ``bro-config`` script must be in ``PATH``.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_autoconfig)

    # env
    sub_parser = command_parser.add_parser(
        'env',
        help='Show the value of environment variables that need to be set for'
        ' Bro to be able to use installed packages.',
        description='This command returns shell commands that, when executed,'
        ' will correctly set ``BROPATH`` and ``BRO_PLUGIN_PATH`` to utilize the'
        ' scripts and plugins from packages installed by the package manager.'
        ' For this command to function properly, either have the ``bro-config``'
        ' script (installed by bro) in ``PATH``, or have the ``BROPATH`` and'
        ' ``BRO_PLUGIN_PATH`` environment variables already set so this command'
        ' can append package-specific paths to them.',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    sub_parser.set_defaults(run_cmd=cmd_env)

    return top_parser


def main():
    args = argparser().parse_args()

    if args.verbose > 0:
        formatter = logging.Formatter(
            '%(asctime)s %(levelname)-8s %(message)s', '%Y-%m-%d %H:%M:%S')
        handler = logging.StreamHandler()
        handler.setFormatter(formatter)

        if args.verbose == 1:
            bropkg.LOG.setLevel(logging.WARNING)
        elif args.verbose == 2:
            bropkg.LOG.setLevel(logging.INFO)
        elif args.verbose == 3:
            bropkg.LOG.setLevel(logging.DEBUG)

        bropkg.LOG.addHandler(handler)

    configfile = args.configfile

    if not configfile:
        configfile = find_configfile()

    config = create_config(configfile)
    manager = create_manager(config)

    args.run_cmd(manager, args, config)


if __name__ == '__main__':
    main()
    sys.exit(0)
