"""
Helper functions for dealing with compiler replacement.

This provides a few key functions to deal with varying/measuring the compilers
used inside the benchbuild study.
From a high-level view, there are 2 interesting functions:
    * lt_clang(cflags, ldflags, func)
    * lt_clang_cxx(cflags, ldflags, func)

These generate a wrapped clang/clang++ in the current working directory and
hide the given cflags/ldflags from the calling build system. Both will
give you a working plumbum command and call a python script that redirects
to the real clang/clang++ given the additional cflags&ldflags.

The wrapper-script generated for both functions can be found inside:
    * wrap_cc()

Are just convencience methods that can be used when interacting with the
configured llvm/clang source directories.
"""
from benchbuild.settings import CFG
from benchbuild.utils.wrapping import wrap_cc
from plumbum.commands.base import BoundCommand


def wrap_cc_in_uchroot(cflags, ldflags, func=None, cc_name='cc'):
    """
    Generate a clang wrapper that may be called from within a uchroot.

    This basically does the same as lt_clang/lt_clang_cxx. However, we do not
    create a valid plumbum command. The generated script will only work
    inside a uchroot environment that has is root at the current working
    directory, when calling this function.

    Args:
        cflags: The CFLAGS we want to hide
        ldflags: The LDFLAGS we want to hide
        func (optional): A function that will be pickled alongside the compiler.
            It will be called before the actual compilation took place. This
            way you can intercept the compilation process with arbitrary python
            code.
        uchroot_path: Prefix path of the compiler inside the uchroot.
        cc_name: Name of the generated script.
    """
    from os import path
    from plumbum import local

    def compiler():  # pylint:  disable=C0111
        pi = __get_paths()
        cc = local["/usr/bin/env"]
        cc = cc[cc_name]
        cc = cc.with_env(LD_LIBRARY_PATH=pi["ld_library_path"])
        return cc

    def gen_compiler_extension(ext):  # pylint:  disable=C0111
        return path.join("/", cc_name + ext)
    wrap_cc(cc_name, cflags, ldflags, compiler, func, gen_compiler_extension,
            python="/usr/bin/env python3")


def wrap_cxx_in_uchroot(cflags, ldflags, func=None):
    """Delegate to wrap_cc_in_uchroot)."""
    wrap_cc_in_uchroot(cflags, ldflags, func, CFG["compiler"]["cc"].value())


def lt_clang(cflags, ldflags, func=None):
    """
    Return a clang that hides CFLAGS and LDFLAGS.

    This will generate a wrapper script in the current directory
    and return a complete plumbum command to it.

    Args:
        cflags: The CFLAGS we want to hide.
        ldflags: The LDFLAGS we want to hide.
        func (optional): A function that will be pickled alongside the compiler.
            It will be called before the actual compilation took place. This
            way you can intercept the compilation process with arbitrary python
            code.

    Returns (benchbuild.utils.cmd):
        Path to the new clang command.
    """
    from benchbuild.utils import cmd

    cc = CFG["compiler"]["c"].value()
    wrap_cc(cc, cflags, ldflags, compiler(cc), func)
    return cmd["./cc"]


def lt_clang_cxx(cflags, ldflags, func=None):
    """
    Return a clang++ that hides CFLAGS and LDFLAGS.

    This will generate a wrapper script in the current directory
    and return a complete plumbum command to it.

    Args:
        cflags: The CFLAGS we want to hide.
        ldflags: The LDFLAGS we want to hide.
        func (optional): A function that will be pickled alongside the compiler.
            It will be called before the actual compilation took place. This
            way you can intercept the compilation process with arbitrary python
            code.

    Returns (benchbuild.utils.cmd):
        Path to the new clang command.
    """
    from plumbum import local

    cxx = CFG["compiler"]["cxx"].value()
    wrap_cc(cxx, cflags, ldflags, compiler(cxx), func)
    return local["./c++"]


def __get_paths():
    from os import getenv
    from benchbuild.utils.path import list_to_path

    path = getenv("PATH", "")
    lib_path = getenv("LD_LIBRARY_PATH", "")
    _lib_path = CFG["env"]["ld_library_path"].value()
    _path = CFG["env"]["path"].value()
    _lib_path = list_to_path(_lib_path)
    _path = list_to_path(_path)

    path = list_to_path([_path, path])
    lib_path = list_to_path([_lib_path, lib_path])

    return {"ld_library_path": lib_path, "path": path}


def compiler(name):
    """
    Get a usable clang++ plumbum command.

    This searches for a usable clang++ in the llvm binary path

    Returns:
        plumbum Command that executes clang++
    """
    from plumbum import local
    pinfo = __get_paths()
    _compiler = local[name]
    _compiler = _compiler.setenv(PATH=pinfo["path"],
                                 LD_LIBRARY_PATH=pinfo["ld_library_path"])
    return _compiler


class ExperimentCommand(BoundCommand):
    __slots__ = ["cmd", "args"]

    def __init__(self, cmd, args, exp_args):
        self.cmd = cmd
        self.args = args if args is not None else []
        for inner_list in list(exp_args):
            self.args.extend(inner_list)

@@ -2483,17 +2483,18 @@
 ler%22%5D%5B%22c
-c
+xx
 %22%5D.value
@@ -3306,11 +3306,31 @@
 %5B%22./
+%7Bname%7D%22.format(name=
 cc
-%22
+)
 %5D%0A%0A%0A
@@ -3996,39 +3996,48 @@
 %22%22%0A    from 
-plumbum
+benchbuild.utils
  import 
 local%0A%0A    c
@@ -4020,29 +4020,27 @@
 tils import 
-local
+cmd
 %0A%0A    cxx = 
@@ -4140,21 +4140,39 @@
 urn 
-local%5B%22./c++%22
+cmd%5B%22./%7Bname%7D%22.format(name=cxx)
 %5D%0A%0A%0A

fix more key errors