import copy
import os

from conans.client.build.compiler_flags import build_type_define, build_type_flags, format_defines, \
    include_path_option, parallel_compiler_cl_flag, visual_runtime
from conans.client.build.cppstd_flags import cppstd_from_settings, \
    cppstd_flag_new as cppstd_flag
from conans.client.tools.files import VALID_LIB_EXTENSIONS


class VisualStudioBuildEnvironment(object):
    """
    - LIB: library paths with semicolon separator
    - CL: /I (include paths)
    - _LINK_: linker options and libraries
    - UseEnv: True https://github.com/conan-io/conan/pull/4583

    https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
    https://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx
    https://msdn.microsoft.com/en-us/library/9s7c9wdw.aspx
    https://msdn.microsoft.com/en-us/library/6y6t9esh.aspx

    """
    def __init__(self, conanfile, with_build_type_flags=True):
        """
        :param conanfile: ConanFile instance
        """
        self._with_build_type_flags = with_build_type_flags

        self._conanfile = conanfile
        self._settings = conanfile.settings
        self._deps_cpp_info = conanfile.deps_cpp_info
        self._runtime = self._settings.get_safe("compiler.runtime")

        self.include_paths = conanfile.deps_cpp_info.include_paths
        self.lib_paths = conanfile.deps_cpp_info.lib_paths
        self.defines = copy.copy(conanfile.deps_cpp_info.defines)
        self.flags = self._configure_flags()
        self.cxx_flags = copy.copy(self._deps_cpp_info.cxxflags)
        self.link_flags = self._configure_link_flags()
        self.libs = conanfile.deps_cpp_info.libs
        self.std = self._std_cpp()
        self.parallel = False

    def _configure_link_flags(self):
        ret = copy.copy(self._deps_cpp_info.exelinkflags)
        ret.extend(self._deps_cpp_info.sharedlinkflags)
        return ret

    def _configure_flags(self):
        ret = copy.copy(self._deps_cpp_info.cflags)
        ret.extend(vs_build_type_flags(self._settings, with_flags=self._with_build_type_flags))
        return ret

    def _get_cl_list(self, quotes=True):
        # FIXME: It should be managed with the compiler_flags module
        # But need further investigation about the quotes and so on, so better to not break anything
        if quotes:
            ret = ['%s"%s"' % (include_path_option, lib) for lib in self.include_paths]
        else:
            ret = ['%s%s' % (include_path_option, lib) for lib in self.include_paths]

        runtime = visual_runtime(self._runtime)
        if runtime:
            ret.append(runtime)

        ret.extend(format_defines(self.defines))
        ret.extend(self.flags)
        ret.extend(self.cxx_flags)

        if self.parallel:  # Build source in parallel
            ret.append(parallel_compiler_cl_flag(output=self._conanfile.output))

        if self.std:
            ret.append(self.std)

        return ret

    def _get_link_list(self):
        # FIXME: Conan 2.0. The libs are being added twice to visual_studio
        # one in the conanbuildinfo.props, and the other in the env-vars
        def format_lib(lib):
            ext = os.path.splitext(lib)[1]
            return lib if ext in VALID_LIB_EXTENSIONS else '%s.lib' % lib

        ret = [flag for flag in self.link_flags]  # copy
        ret.extend([format_lib(lib) for lib in self.libs])

        return ret

    @property
    def vars(self):
        """Used in conanfile with environment_append"""
        flags = self._get_cl_list()
        link_flags = self._get_link_list()

        cl_args = " ".join(flags) + _environ_value_prefix("CL")
        link_args = " ".join(link_flags)
        lib_paths = (";".join(['%s' % lib for lib in self.lib_paths]) +
                     _environ_value_prefix("LIB", ";"))
        return {"CL": cl_args,
                "LIB": lib_paths,
                "_LINK_": link_args,
                "UseEnv": "True"}

    @property
    def vars_dict(self):
        """Used in virtualbuildenvironment"""
        # Here we do not quote the include paths, it's going to be used by virtual environment
        cl = self._get_cl_list(quotes=False)
        link = self._get_link_list()

        lib = [lib for lib in self.lib_paths]  # copy

        if os.environ.get("CL", None):
            cl.append(os.environ.get("CL"))

        if os.environ.get("LIB", None):
            lib.append(os.environ.get("LIB"))

        if os.environ.get("_LINK_", None):
            link.append(os.environ.get("_LINK_"))

        ret = {"CL": cl,
               "LIB": lib,
               "_LINK_": link,
               "UseEnv": "True"}
        return ret

    def _std_cpp(self):
        return vs_std_cpp(self._settings)


def vs_build_type_flags(settings, with_flags=True):
    build_type = settings.get_safe("build_type")
    ret = []
    btd = build_type_define(build_type=build_type)
    if btd:
        ret.extend(format_defines([btd]))
    if with_flags:
        # When using to build a vs project we don't want to adjust these flags
        btfs = build_type_flags(settings)
        if btfs:
            ret.extend(btfs)

    return ret


def vs_std_cpp(settings):
    cppstd = cppstd_from_settings(settings)
    if settings.get_safe("compiler") == "Visual Studio" and cppstd:
        flag = cppstd_flag(settings)
        return flag
    return None


def _environ_value_prefix(var_name, prefix=" "):
    if os.environ.get(var_name, ""):
        return "%s%s" % (prefix, os.environ.get(var_name, ""))
    else:
        return ""
