import yaml
import textwrap
from itertools import chain


def find_first(l, predicate, default=None):
    return next((x for x in l if predicate(x)), default)


def find_first_index(l, predicate, default=None):
    if default is None:
        default = len(l)
    return next((i for i, x in enumerate(l) if predicate(x)), default)


def delete_first(l, predicate):
    i = find_first_index(l, predicate)
    if i < len(l):
        del l[i]


def message_box(title, content, aligner="<", max_width=70):
    lines = [textwrap.shorten(line, width=max_width) for line in content.splitlines()]

    width = max(map(len, [title] + lines)) + 2

    nb = width - 2  # number of blanks
    border = f"│{{: ^{nb}}}│"

    out = []
    out.append("┌" + "─" * nb + "┐")
    out.append(border.format(title.capitalize()))
    out.append("├" + "─" * nb + "┤")

    for line in lines:
        out.append(border.replace("^", aligner).format(line.strip()))

    out.append("└" + "─" * nb + "┘")

    return "\n".join(out)


def print_args(args):
    args = [f"{k}: {v}" for k, v in sorted(vars(args).items())]
    print(message_box("Arguments", "\n".join(args)))


def load_yaml(path, command):
    with open(path, "r") as f:
        # remove the blank at the end of lines
        lines = f.read().splitlines()
        data = "\n".join([l.rstrip() for l in lines])
        data = yaml.load(data, Loader=yaml.FullLoader) or {}

    if command in data:
        data.update(data[command])

    # except for command, only support first level
    for key in list(data.keys()):
        if type(data[key]) is dict:
            del data[key]

    # load the default (i.e. base) yaml of the current yaml
    # a left base will be overwriten by the right base
    if "default" in data:
        bases = data["default"]
        if not isinstance(bases, list):
            bases = [bases]
        for base in reversed(bases):
            base = load_yaml(base, command)
            base.update(data)
            data = base
        del data["default"]

    return data


def to_val(x):
    if isinstance(x, (tuple, list)):
        return list(chain.from_iterable(map(to_val, x)))
    return [str(x)]


def to_key(k):
    return f"--{k.replace('_', '-')}"


def to_argv(k, v):
    argv = [to_key(k)]
    if v != "":
        argv.extend(to_val(v))
    return argv


def yaml2argv(path, command, ignored):
    data = load_yaml(path, command)
    for key in ignored:
        del data[key]
    argv = chain.from_iterable(to_argv(k, v) for k, v in data.items())
    return list(argv)
