# This file is part of the DiscoPoP software (http://www.discopop.tu-darmstadt.de)
#
# Copyright (c) 2020, Technische Universitaet Darmstadt, Germany
#
# This software may be modified and distributed under the terms of
# the 3-Clause BSD License.  See the LICENSE file in the package base
# directory for details.

import glob
import os
import pathlib
import subprocess
import tkinter


class ProfilingContainer(object):

    def __init__(self, wizard):
        self.wizard = wizard
        self.start()


    def start(self):
        docker_context_path = os.path.join(pathlib.Path(__file__).parent.resolve(), "..", "assets",
                                           "profiling_container")
        self.__execute_command("docker kill discopop_container")
        self.__execute_command("docker rm discopop_container")
        exit_code = self.__execute_command("docker build -t discopop_container " + docker_context_path)
        assert (exit_code == 0)
        exit_code = self.__execute_command("docker run --name discopop_container -d -t discopop_container")
        assert (exit_code == 0)

    def stop(self):
        print("Stopping DiscoPoP profiling container...")
        self.__execute_command("docker kill discopop_container")
        print("Done.")

    def remove_project_folder(self):
        self.__execute_command("docker exec -it discopop_container rm -rvf /project")

    def remove_previous_results(self, target_folder):
        files = glob.glob(target_folder + "/*")
        for f in files:
            os.remove(f)

    def copy_project_folder_to_container(self, project_path: str):
        self.remove_project_folder()
        self.__execute_command("docker cp " + project_path + " discopop_container:/project")

    def copy_results_from_container(self, target_path: str, execution_view):
        result_files = [
            "FileMapping.txt",
            "Data.xml",
            "loop_counter_output.txt",
            "reduction.txt",
            execution_view.execution_configuration.value_dict["executable_name"] + "_dp_dep.txt",
            execution_view.execution_configuration.value_dict["executable_name"] + "_dp.ll",
            "patterns.txt",
            "patterns.json"
        ]
        for file in result_files:
            exit_code = self.__execute_command(
                "docker cp discopop_container:/project/.discopop/" + file + " " + target_path)
            assert (exit_code == 0)

    def analyze_project(self, execution_view):
        # copy project folder to container. Note: mounting would be nicer but requires restarting the container.
        # might be a nicer solution in the long run, especially for larger projects
        self.copy_project_folder_to_container(execution_view.execution_configuration.value_dict["project_path"])

        # settings
        command = "/discopop/build/scripts/runDiscoPoP "
        command += "--llvm-clang clang-11 "
        command += "--llvm-clang++ clang++-11 "
        command += "--llvm-ar llvm-ar-11 "
        command += "--llvm-link llvm-link-11 "
        command += "--llvm-dis llvm-dis-11 "
        command += "--llvm-opt opt-11 "
        command += "--llvm-llc llc-11 "
        command += "--gllvm /software/go/bin "
        # execution configuration
        command += "--project /project "
        command += "--linker-flags \"" + execution_view.execution_configuration.value_dict["linker_flags"] + "\" "
        command += "--executable-name \"" + execution_view.execution_configuration.value_dict["executable_name"] + "\" "
        command += "--executable-arguments \"" + execution_view.execution_configuration.value_dict["executable_arguments"] + "\" "
        command += "--make-flags \"" + execution_view.execution_configuration.value_dict["make_flags"] + "\" "
        command += "--make-target \"" + execution_view.execution_configuration.value_dict["make_target"] + "\" "
        command += "--explorer-flags \"" + execution_view.execution_configuration.value_dict["explorer_flags"] + "\" "

        self.__execute_command("docker exec -it discopop_container " + command)

        # copy results from container into working copy path
        if not os.path.exists(execution_view.execution_configuration.value_dict["working_copy_path"]):
            os.mkdir(execution_view.execution_configuration.value_dict["working_copy_path"])

        # remove previous results
        self.remove_previous_results(execution_view.execution_configuration.value_dict["working_copy_path"])

        # copy results from container
        self.copy_results_from_container(execution_view.execution_configuration.value_dict["working_copy_path"], execution_view)

        # correct paths in generated FileMapping.txt
        self.__correct_file_mapping_paths(execution_view)

    def __correct_file_mapping_paths(self, execution_view):
        file_mapping_path = os.path.join(execution_view.execution_configuration.value_dict["working_copy_path"], "FileMapping.txt")
        with open(file_mapping_path, 'r') as file:
            contents = file.read()
        contents = contents.replace('/project/.discopop', execution_view.execution_configuration.value_dict["project_path"])
        with open(file_mapping_path, 'w') as file:
            file.write(contents)

    def __execute_command(self, command: str) -> int:
        with subprocess.Popen(command, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True, shell=True) as p:
            for line in p.stdout:
                line = line.replace("\n", "")
                print(line)
                try:
                    self.wizard.console.print(line)
                except tkinter.TclError:
                    # happens when container is still shutting down but interface already closed.
                    pass
        if p.returncode != 0:
            print("An error occurred during the execution!")  # Error message
            self.wizard.console.print("An error occurred during the execution!")
            for line in str(subprocess.CalledProcessError(p.returncode, p.args)).split("\n"):
                line = line.replace("\n", "")
                print(line)
                self.wizard.console.print(line)
        return p.returncode
