# Copyright (c) 2017 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import platform
from shutil import copyfile
import sys

SKIP_TOO_LONG = "        raise SkipTest(\"{}\")\n"
NO_SKIP_TOO_LONG = "        # raise SkipTest(\"{}\")\n"
WARNING_LONG = "        # Warning this test takes {}.\n" \
               "        # raise skiptest is uncommented on branch tests\n"


class RootScriptBuilder(object):

    def add_script(self, test_file, name, local_path, skip_imports):
        """
        :param io.TextIOBase test_file:
        :param str name:
        :param str local_path:
        :param list(str) skip_imports:
        """
        test_file.write("\n    def test_")
        test_file.write(name)
        test_file.write("(self):\n")
        if skip_imports:
            if isinstance(skip_imports, str):
                skip_imports = [skip_imports]
            for skip_import in skip_imports:
                test_file.write("        ")
                test_file.write(skip_import)
                test_file.write("\n")
        test_file.write("        self.check_script(\"")
        test_file.write(local_path)
        test_file.write("\"")
        if skip_imports:
            test_file.write(", skip_exceptions=[")
            test_file.write(
                ",".join(map(lambda x: x.split()[-1], skip_imports)))
            test_file.write("]")
        test_file.write(")\n")

    def add_scripts(self, a_dir, prefix_len, test_file, too_long, exceptions,
                    skip_exceptions):
        """
        :param str a_dir:
        :param int prefix_len:
        :param io.TextIOBase test_file:
        :param dict(str,str) too_long:
        :param dict(str,str) exceptions:
        :param dict(str,list(str)) skip_exceptions:
        """
        for a_script in os.listdir(a_dir):
            script_path = os.path.join(a_dir, a_script)
            if os.path.isdir(script_path) and not a_script.startswith("."):
                self.add_scripts(
                    script_path, prefix_len, test_file, too_long, exceptions,
                    skip_exceptions)
            if a_script.endswith(".py") and a_script != "__init__.py":
                local_path = script_path[prefix_len:]
                # As the paths are written to strings in files
                # Windows needs help!
                if platform.system() == "Windows":
                    local_path = local_path.replace("\\", "/")
                if a_script in too_long and len(sys.argv) > 1:
                    # Lazy boolean distinction based on presence of parameter
                    test_file.write("\n    # Not testing file due to: ")
                    test_file.write(too_long[a_script])
                    test_file.write("\n    # ")
                    test_file.write(local_path)
                    test_file.write("\n")
                elif a_script in exceptions:
                    test_file.write("\n    # Not testing file due to: ")
                    test_file.write(exceptions[a_script])
                    test_file.write("\n    # ")
                    test_file.write(local_path)
                    test_file.write("\n")
                else:
                    name = local_path[:-3].replace(os.sep, "_").replace(
                        "-", "_")
                    skip_imports = skip_exceptions.get(a_script, None)
                    self.add_script(test_file, name, local_path, skip_imports)

    def create_test_scripts(self, dirs, too_long=None, exceptions=None,
                            skip_exceptions=None):
        """
        :param dirs: List of dirs to find scripts in.
            These are relative paths to the repository
        :type dirs: str or list(str)
        :param too_long: Dict of files that take too long to run and how long.
            These are just the file name including the `.py`.
            They are mapped to a skip reason.
            These are only skip tests if asked to be (currently not done).
        :type too_long: dict(str, str) or None
        :param exceptions: Dict of files that should be skipped.
            These are just the file name including the `.py`.
            They are mapped to a skip reason.
            These are always skipped.
        :type exceptions: dict(str, str) or None
        :param dict(str,list(str)) skip_exceptions:
            Dict of files and exceptions to skip on.
            These are just the file name including the `.py`.
            They are mapped to a list of INDIVIUAL import statements
            in the::

                from xyz import Abc

            format.
        """
        if too_long is None:
            too_long = {}
        if exceptions is None:
            exceptions = {}
        if skip_exceptions is None:
            skip_exceptions = {}
        if isinstance(dirs, str):
            dirs = [dirs]

        class_file = sys.modules[self.__module__].__file__
        integration_dir = os.path.dirname(class_file)
        repository_dir = os.path.dirname(integration_dir)
        test_base_directory = os.path.dirname(__file__)

        test_script = os.path.join(integration_dir, "test_scripts.py")
        header = os.path.join(test_base_directory, "test_scripts_header")
        copyfile(header, test_script)

        with open(test_script, "a", encoding="utf-8") as test_file:
            for script_dir in dirs:
                a_dir = os.path.join(repository_dir, script_dir)
                self.add_scripts(a_dir, len(repository_dir)+1, test_file,
                                 too_long, exceptions, skip_exceptions)
