"""
=====================
Triqs magic
=====================

{TRIQS_DOC}

"""
from builtins import str
from past.utils import old_div
import imp,os,sys,subprocess, hashlib,re
from IPython.core.error import UsageError
from IPython.core.magic import Magics, magics_class, line_magic, cell_magic
from IPython.core import display, magic_arguments
from IPython.utils import py3compat
from IPython.utils.io import capture_output
#from IPython.utils.path import get_ipython_cache_dir
from IPython.paths import get_ipython_cache_dir

__version__ = '0.2.0'

triqs_path = "/home/conda/feedstock_root/build_artifacts/triqs_1602613454827/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_place"
cxx_compiler = "/home/conda/feedstock_root/build_artifacts/triqs_1602613454827/_h_env_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_placehold_place/bin/x86_64-conda-linux-gnu-c++"
converter_include_path = triqs_path + "/include/triqs/py_converters/"
generator_path = triqs_path + "/share/triqs/cpp2py"
cpp2py_bin = triqs_path + "/bin/c++2py.py"

cmakelist = """
list(APPEND CMAKE_MODULE_PATH %s/share/cmake/Modules)
cmake_minimum_required(VERSION 2.8.12)
project(triqs_magic CXX)
set(CMAKE_BUILD_TYPE Release)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
find_package(TRIQS REQUIRED)
add_library(ext MODULE ext_wrap.cpp)
set_target_properties(ext PROPERTIES PREFIX "") #eliminate the lib in front of the module name
target_link_libraries(ext PRIVATE triqs)
triqs_set_rpath_for_target(ext)
"""%triqs_path

@magics_class
class TriqsMagics(Magics):

    def __init__(self, shell):
        super(TriqsMagics, self).__init__(shell=shell)
        self._reloads = {}
        self._code_cache = {}
        self._lib_dir = os.path.join(get_ipython_cache_dir(), 'triqs')
        if not os.path.exists(self._lib_dir):
            os.makedirs(self._lib_dir)

    @magic_arguments.magic_arguments()
    @magic_arguments.argument(
            "-v", "--verbosity", action="count", default=0,
            help="increase output verbosity"
        )
    @magic_arguments.argument(
            '-o', "--only", action='append', default=[],
            help="""Which object to wrap"""
        )

    @cell_magic
    def triqs(self, line, cell=None):
        """Compile and import everything from a Triqs code cell.

        Takes the c++ code, call c++2py on it and compile the whole thing
        into a module which is then loaded and 
        all of its symbols are injected into the user's namespace.

        Usage
        =====
        Prepend ``%%triqs`` to your triqs code in a cell::

        ``%%triqs

        ! put your code here.
        ``
        """
        try:
            # custom saved arguments
            saved_defaults = vars(
                magic_arguments.parse_argstring(self.triqs,
                                                self.shell.db['triqs']))
            self.triqs.parser.set_defaults(**saved_defaults)
        except KeyError:
            saved_defaults = {'verbosity': 0}

        if '-v' in line:
            self.triqs.parser.set_defaults(verbosity=0)

        ## Add GIL argument ?
        use_GIL = False
        args = magic_arguments.parse_argstring(self.triqs, line)

        code = cell if cell.endswith('\n') else cell + '\n'

        #if not GIL, we replace std::cout by triqs::py_out for capture in the notebook
        if not use_GIL :
            code = re.sub("std::cout", "triqs::py_stream()", code)

        key = code, line, sys.version_info, sys.executable

        module_name = "ext"
        module_dirname = os.path.join(self._lib_dir, "_triqs_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest())

        if args.verbosity > 0:
            print("Workdir = ", module_dirname)

        module_path = os.path.join(module_dirname, 'ext.so')
        try :
            os.mkdir(module_dirname)
        except :
            pass

        def print_out (m, out) : 
           l = (70 - len(m))/2
           print(l*'-' + m + l*'-' + '\n' + out)

        old_cwd = os.getcwd()
        try:
            os.chdir(module_dirname)

            with open('CMakeLists.txt', 'w') as f:
                f.write(cmakelist)

            with open('ext.cpp', 'w') as f:
                f.write("""
#include <triqs/utility/cpp2py_macros.hpp>
#include <triqs/python_tools/py_stream.hpp>
                """)
                f.write(code)

            def execute(command, message):
                try:
                   out = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
                except subprocess.CalledProcessError as E :
                   print_out ( message + " error ", E.output)
                   raise RuntimeError("Error")
                if args.verbosity>0: 
                   print_out(message, out)

            # Call cpp2py
            only_list = ','.join(args.only)
            only_list = (" --only " + only_list) if only_list else '' 
            execute(cpp2py_bin + " ./ext.cpp -p -m ext -o ext" + only_list, "c++2py")

            # Call the wrapper generator
            execute ("""
            PYTHONPATH={generator_path} python ext_desc.py {generator_path}/mako/xxx_wrap.cpp ext_wrap.cpp {generator_path}/mako/py_converter.hpp conv.hpp {triqs_path}
            """.format(**globals()), "Wrapper generator")

            # Call cmake
            execute("cmake . -Wno-dev  -DCMAKE_CXX_COMPILER="+ cxx_compiler+ " -DTRIQS_PATH=" + triqs_path, "cmake")

            # Call make
            execute ("make -j2", "make")

        finally:
            os.chdir(old_cwd)

        self._code_cache[key] = module_path
        module = imp.load_dynamic(module_name, module_path)
        # import all object and function in the main namespace
        imported = []
        for k, v in list(module.__dict__.items()):
            if not k.startswith('_'):
                self.shell.push({k: v})
                imported.append(k)
        if args.verbosity > 0 and imported:
            print_out("Success", "The following objects are ready to use: %s" % ", ".join(imported))

__doc__ = __doc__.format(TRIQS_DOC=' ' * 8 + TriqsMagics.triqs.__doc__)

def load_ipython_extension(ip):
    """Load the extension in IPython."""
    ip.register_magics(TriqsMagics)

