import os
import platform
import textwrap

import pytest

from conan.test.assets.genconanfile import GenConanfile
from conan.test.assets.sources import gen_function_cpp
from conan.test.utils.tools import TestClient

from conan.internal.util.files import save


@pytest.mark.tool("cmake")
@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
def test_custom_config_generator_multi():
    conanfile = textwrap.dedent("""
        import os
        from conan import ConanFile
        from conan.tools.cmake import CMakeDeps
        from conan.tools.files import copy
        class App(ConanFile):
            settings = "os", "arch", "compiler", "build_type"
            requires = "hello/0.1"

            def generate(self):
                cmake = CMakeDeps(self)
                if self.dependencies["hello"].options.shared:
                    cmake.configuration = "ReleaseShared"
                cmake.generate()

                config = str(self.settings.build_type)
                if self.dependencies["hello"].options.shared:
                    config = "ReleaseShared"
                src = os.path.join(self.dependencies["hello"].package_folder, "bin")
                dst = os.path.join(self.build_folder, config)
                copy(self, "*.dll", src, dst, keep_path=False)
        """)

    app = gen_function_cpp(name="main", includes=["hello"], calls=["hello"])

    cmakelist = textwrap.dedent("""
        set(CMAKE_CONFIGURATION_TYPES Debug Release ReleaseShared CACHE STRING
            "Available build-types: Debug, Release and ReleaseShared")

        set(CMAKE_CXX_COMPILER_WORKS 1)
        set(CMAKE_CXX_ABI_COMPILED 1)
        cmake_minimum_required(VERSION 2.8)
        project(App C CXX)

        set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR} ${CMAKE_PREFIX_PATH})
        set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR} ${CMAKE_MODULE_PATH})

        set(CMAKE_CXX_FLAGS_RELEASESHARED ${CMAKE_CXX_FLAGS_RELEASE})
        set(CMAKE_C_FLAGS_RELEASESHARED ${CMAKE_C_FLAGS_RELEASE})
        set(CMAKE_EXE_LINKER_FLAGS_RELEASESHARED ${CMAKE_EXE_LINKER_FLAGS_RELEASE})

        find_package(hello REQUIRED)
        add_executable(app app.cpp)
        target_link_libraries(app PRIVATE hello::hello)
        """)

    c = TestClient(path_with_spaces=False)
    c.run("new cmake_lib -d name=hello -d version=0.1")
    c.run("create . -s compiler.version=191 "
          "-s build_type=Release -o hello/*:shared=True -tf=\"\"")
    c.run("create . --name=hello --version=0.1 -s compiler.version=191 "
          "-s build_type=Release -tf=\"\"")

    # Prepare the actual consumer package
    c.save({"conanfile.py": conanfile,
            "CMakeLists.txt": cmakelist,
            "app.cpp": app})

    settings = {"compiler": "msvc",
                "compiler.version": "191",
                "compiler.runtime": "dynamic",
                "arch": "x86_64",
                "build_type": "Release",
                }

    settings = " ".join('-s %s="%s"' % (k, v) for k, v in settings.items() if v)

    # Run the configure corresponding to this test case
    with c.chdir('build'):
        c.run("install .. %s -o hello/*:shared=True -of=." % settings)
        c.run("install .. %s -o hello/*:shared=False -of=." % settings)
        assert os.path.isfile(os.path.join(c.current_folder,
                                           "hello-Target-releaseshared.cmake"))
        assert os.path.isfile(os.path.join(c.current_folder,
                                           "hello-Target-release.cmake"))

        c.run_command('cmake .. -G "Visual Studio 15 Win64" --loglevel=DEBUG')
        assert "Found DLL and STATIC" in c.out
        c.run_command('cmake --build . --config Release')
        c.run_command(r"Release\\app.exe")
        assert "hello/0.1: Hello World Release!" in c.out
        assert "main: Release!" in c.out
        c.run_command('cmake --build . --config ReleaseShared')
        c.run_command(r"ReleaseShared\\app.exe")
        assert "hello/0.1: Hello World Release!" in c.out
        assert "main: Release!" in c.out


@pytest.mark.tool("cmake")
@pytest.mark.skipif(platform.system() != "Windows", reason="Only for windows")
def test_custom_config_settings():
    conanfile = textwrap.dedent("""
        from conan import ConanFile
        from conan.tools.cmake import CMakeDeps

        class App(ConanFile):
            settings = "os", "arch", "compiler", "build_type"
            requires = "hello/0.1"
            generators = "CMakeToolchain"

            def generate(self):
                cmake = CMakeDeps(self)
                #cmake.configurations.append("MyRelease") # NOT NECESSARY!!!
                cmake.generate()
        """)

    app = gen_function_cpp(name="main", includes=["hello"], calls=["hello"])

    cmakelist = textwrap.dedent("""
        set(CMAKE_CONFIGURATION_TYPES Debug Release MyRelease CACHE STRING
            "Available build-types: Debug, Release and MyRelease")

        set(CMAKE_CXX_COMPILER_WORKS 1)
        set(CMAKE_CXX_ABI_COMPILED 1)
        set(CMAKE_C_COMPILER_WORKS 1)
        set(CMAKE_C_ABI_COMPILED 1)
        cmake_minimum_required(VERSION 3.15)
        project(App C CXX)

        set(CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR} ${CMAKE_PREFIX_PATH})
        set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR} ${CMAKE_MODULE_PATH})

        set(CMAKE_CXX_FLAGS_MYRELEASE ${CMAKE_CXX_FLAGS_RELEASE})
        set(CMAKE_C_FLAGS_MYRELEASE ${CMAKE_C_FLAGS_RELEASE})
        set(CMAKE_EXE_LINKER_FLAGS_MYRELEASE ${CMAKE_EXE_LINKER_FLAGS_RELEASE})

        find_package(hello REQUIRED)
        add_executable(app app.cpp)
        target_link_libraries(app PRIVATE hello::hello)
        """)

    c = TestClient(path_with_spaces=False)
    settings = "build_type: [MyRelease]"
    save(c.paths.settings_path_user, settings)
    c.run("new cmake_lib -d name=hello -d version=0.1")
    cmake = c.load("CMakeLists.txt")

    cmake = cmake.replace("cmake_minimum_required", """
        set(CMAKE_CONFIGURATION_TYPES Debug MyRelease Release CACHE STRING "Types")

        cmake_minimum_required""")
    cmake = cmake.replace("add_library", textwrap.dedent("""
        set(CMAKE_CXX_FLAGS_MYRELEASE ${CMAKE_CXX_FLAGS_RELEASE})
        set(CMAKE_C_FLAGS_MYRELEASE ${CMAKE_C_FLAGS_RELEASE})
        set(CMAKE_EXE_LINKER_FLAGS_MYRELEASE ${CMAKE_EXE_LINKER_FLAGS_RELEASE})
        add_library"""))
    cmake = cmake.replace("PUBLIC_HEADER", "CONFIGURATIONS MyRelease\nPUBLIC_HEADER")
    c.save({"CMakeLists.txt": cmake})
    c.run("create . --name=hello --version=0.1 -s compiler.version=191 "
          "-s build_type=MyRelease -s:b build_type=MyRelease -tf=\"\"")

    # Prepare the actual consumer package
    c.save({"conanfile.py": conanfile,
            "CMakeLists.txt": cmakelist,
            "app.cpp": app})

    settings = {"compiler": "msvc",
                "compiler.version": "191",
                "compiler.runtime": "dynamic",
                "arch": "x86_64",
                "build_type": "MyRelease",
                }

    settings_h = " ".join('-s %s="%s"' % (k, v) for k, v in settings.items())
    settings_b = " ".join('-s:b %s="%s"' % (k, v) for k, v in settings.items())

    # Run the configure corresponding to this test case
    build_directory = os.path.join(c.current_folder, "build").replace("\\", "/")
    with c.chdir(build_directory):
        c.run("install .. %s %s -of=." % (settings_h, settings_b))
        toolchain = c.load("conan_toolchain.cmake")
        # As it is a custom configuration, the TRY_COMPILE_CONFIFURATION not defined
        assert "CMAKE_TRY_COMPILE_CONFIGURATION" not in toolchain
        assert os.path.isfile(os.path.join(c.current_folder,
                                           "hello-Target-myrelease.cmake"))

        c.run_command('cmake .. -G "Visual Studio 15" '
                      '-DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake')
        c.run_command('cmake --build . --config MyRelease')
        c.run_command(r"MyRelease\\app.exe")
        assert "hello/0.1: Hello World Release!" in c.out
        assert "main: Release!" in c.out


@pytest.mark.tool("cmake")
def test_changing_build_type():
    client = TestClient(path_with_spaces=False)
    dep_conanfile = textwrap.dedent(r"""
       import os
       from conan import ConanFile
       from conan.tools.files import copy, save

       class Dep(ConanFile):
           settings = "build_type"
           def build(self):
               save(self, "hello.h",
               '# include <iostream>\n'
               'void hello(){{std::cout<<"BUILD_TYPE={}!!";}}'.format(self.settings.build_type))
           def package(self):
               copy(self, "*.h", self.source_folder, os.path.join(self.package_folder, "include"))
           """)
    client.save({"conanfile.py": dep_conanfile})
    client.run("create . --name=dep --version=0.1 -s build_type=Release")
    client.run("create . --name=dep --version=0.1 -s build_type=Debug")

    cmakelists = textwrap.dedent("""
        set(CMAKE_CXX_COMPILER_WORKS 1)
        set(CMAKE_CXX_ABI_COMPILED 1)
        cmake_minimum_required(VERSION 3.15)
        project(App CXX)

        # NOTE: NO MAP necessary!!!
        find_package(dep REQUIRED)
        add_executable(app app.cpp)
        target_link_libraries(app PRIVATE dep::dep)
        """)
    app = gen_function_cpp(name="main", includes=["hello"], calls=["hello"])
    pkg_conanfile = GenConanfile("pkg", "0.1").with_requires("dep/0.1").\
        with_generator("CMakeDeps").with_generator("CMakeToolchain").\
        with_settings("os", "compiler", "arch", "build_type")
    client.save({"conanfile.py": pkg_conanfile,
                 "CMakeLists.txt": cmakelists,
                 "app.cpp": app}, clean_first=True)

    # in MSVC multi-config -s pkg/*:build_type=Debug is not really necesary, toolchain do nothing
    # TODO: Challenge how to define consumer build_type for conanfile.txt
    client.run("install . -s pkg*:build_type=Debug -s build_type=Release")
    client.run_command("cmake . -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake "
                       "-DCMAKE_BUILD_TYPE=Debug")
    client.run_command("cmake --build . --config Debug")
    cmd = os.path.join(".", "Debug", "app") if platform.system() == "Windows" else "./app"
    client.run_command(cmd)
    assert "main: Debug!" in client.out
    assert "BUILD_TYPE=Release!!" in client.out
