#!/usr/bin/python3
import os
import argparse
import subprocess as sp
from perfact.generic import load_config


class Namespace(dict):
    """
    dict that also allows access to elements as attributes.
    """
    def __getattr__(self, key):
        if key not in self:
            raise AttributeError
        return self[key]

    def __setattr__(self, key, value):
        self[key] = value


env = Namespace()


def run(cmd, **kw):
    ''' Wrapper for subprocess.run that defaults to check=True. '''
    args = {'check': True}
    args.update(kw)
    if isinstance(cmd, str):
        cmd = cmd.split()
    return sp.run(cmd, **args)


def sudo(*cmds, **kw):
    ''' Run multiple commands with sudo.  '''
    returns = []
    for cmd in cmds:
        if isinstance(cmd, str):
            cmd = cmd.split()
        returns.append(run(['sudo'] + cmd, **kw))
    return returns


def chroot_cmds(*cmds, **kw):
    ''' Execute commands in chroot '''
    returns = []
    for cmd in cmds:
        if isinstance(cmd, str):
            cmd = cmd.split()
        ret = sudo(['chroot', env.mount_path] + cmd, **kw)
        returns.append(ret[0])
    return returns


def create_env():
    env.base_path = os.path.realpath(os.path.join(os.path.dirname(__file__),
                                                  '..'))
    env.config_path = os.path.join(env.base_path, 'config')
    env.build_path = os.path.join(env.base_path, 'build')
    env.disk_config = Namespace(**load_config(
        os.path.join(env.config_path, 'target_disk_conf.py')
    ))
    env.mount_path = os.path.join(env.build_path, env.disk_config.mount_dir)
    return env


def run_cmds_from_args(avail=None, default=None):
    """
    Create an argument parser and parse the arguments to call one or more of
    several available commands.
    The available commands are taken from the dictionary avail_cmds, but only
    callables not starting with _.
    Underscores in the name are replaced by dashes for construction of the
    argument parser.
    The docstring of each function is shown in the help text.
    """
    # Normalize
    cmds = {
        name.replace('_', '-'): fnc
        for name, fnc in avail.items()
        if not name.startswith('_') and callable(fnc)
    }

    maxlen = max(map(len, cmds.keys()))
    helptext = 'Available commands:\n'
    for cmd, fnc in cmds.items():
        # Fetch docstring for help
        doc = fnc.__doc__ or ''
        doc = doc.strip().replace('\n', ' ')
        # I guess with a regex this might be simpler
        while '  ' in doc:
            doc = doc.replace('  ', ' ')
        helptext += cmd.ljust(maxlen+4) + doc + '\n'

    parser = argparse.ArgumentParser(
        epilog=helptext,
        formatter_class=argparse.RawTextHelpFormatter,
    )
    parser.add_argument(
        'command', type=str, nargs='*',
        help='Only execute these wrappers or commands.'
    )
    args = parser.parse_args()

    if default and not args.command:
        args.command = [default]

    # Execute the commands that were given on the command line
    for cmdname in args.command:
        cmd = cmds[cmdname]
        cmd()
