#
#  Copyright (c) 2024 Infineon Technologies AG.
#
#  This file is part of TAS Client, an API for device access for Infineon's
#  automotive MCUs.
#
#  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
#
#     http://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.
#  ****************************************************************************************************************#
from conan import ConanFile
from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout
from conan.tools.build import check_min_cppstd
from conan.errors import ConanInvalidConfiguration

import os
import re
import sys
import subprocess
from pathlib import Path

class ProjectTemplateRecipe(ConanFile):
    name = "mcds_libs"

    def set_version(self):
        cmakeFile = Path(os.path.join(self.recipe_folder, "CMakeLists.txt"))
        pkgVersion = "x.x.x"
        with cmakeFile.open("r") as f:
            content = f.read()
            pkgVersion = re.search(r"project((.|\n)*)VERSION (.*)", content).group(3)

        self.version = pkgVersion.strip()

    package_type = "library"

    # Binary configuration
    settings = "os", "compiler", "build_type", "arch"
    options = {
        "shared": [True, False],
        "fPIC": [True, False],
        "mtv": [True, False],
        "python": [True, False],
        "demos": [True, False],
        "tests": [True, False],
        "docs": [True, False]
        }
    default_options = {
        "shared": False,
        "fPIC": True,
        "mtv": False,
        "python": False,
        "demos": False,
        "tests": False,
        "docs": False
        }

    # Source in same place as this recipe
    exports_sources = "include/*", "apps/*", "cmake/*", "data/*", "docs/*", "mcds/*", "mcds_other/*", "python/*", "src/*", "test/*", "tests/*", "CMakeLists.txt", "LICENSE"

    def config_options(self):
        if self.settings.os == "Windows":
            self.options.rm_safe("fPIC")

    def configure(self):
        if self.options.shared:
            self.options.rm_safe("fPIC")

    def validate(self):
        check_min_cppstd(self, "23")
        if self.settings.os == "Windows" and self.settings.arch != "x86_64":
            raise ConanInvalidConfiguration("Only 64-bit architecture supported on Windows.")

    def system_requirements(self):
        # define system dependencies
        packages = ["setuptools", "virtualenv", "wheel", "build"]
        subprocess.check_call([sys.executable, "-m", "pip", "install"] + packages)

    def requirements(self):
        # define dependencies
        self.requires("tas_client_api/1.2.2@ifx/stable")
        self.requires("elf_disasm/[>=1.0.0 <2.0.0]@ifx/dev")
        self.requires("nlohmann_json/3.11.3")
        self.requires("json-schema-validator/2.3.0")
        self.requires("capstone/6.0.0-alpha5")
        self.requires("spdlog/1.14.1")
        if self.options.mtv:
            self.requires("fltk/1.3.8")
            self.requires("libpng/1.6.40", override=True) # solving version conflict
        if self.options.python:
            self.requires("pybind11/2.13.5")
        if self.options.tests:
            self.requires("cli11/2.4.2")

    def build_requirements(self):
        # define build tools dependencies
        self.tool_requires("cmake/3.27.0")
        self.test_requires("catch2/3.7.1")

    def layout(self):
        cmake_layout(self)
        # This "includedirs" starts in the source folder, which is "." by default setting in cmake_layout
        self.cpp.source.components["mcds_core"].includedirs = ["include", "include/mcds_libs"]
        self.cpp.source.components["mcds_recorder"].includedirs = ["include", "include/mcds_libs"]
        self.cpp.source.components["mcds_configurator"].includedirs = ["include", "include/mcds_libs"]
        self.cpp.source.components["mcds_decoder"].includedirs = ["include", "include/mcds_libs"]
        # compiled libraries "libdirs" will be inside the "build" folder, depending
        # on the platform they will be in "build/Release" or directly in "build" folder
        if self.settings.os != "Windows":
            self.cpp.build.components["mcds_core"].libdirs = ["mcds"]
            self.cpp.build.components["mcds_recorder"].libdirs = ["mcds"]
            self.cpp.build.components["mcds_configurator"].libdirs = ["mcds"]
            self.cpp.build.components["mcds_decoder"].libdirs = ["mcds"]
        else:
            self.cpp.build.components["mcds_core"].libdirs = ["mcds\\"+str(self.settings.build_type)]

            self.cpp.build.components["mcds_recorder"].bindirs = ["mcds\\"+str(self.settings.build_type)]
            self.cpp.build.components["mcds_configurator"].bindirs = ["mcds\\"+str(self.settings.build_type)]
            self.cpp.build.components["mcds_decoder"].bindirs = ["mcds\\"+str(self.settings.build_type)]

            self.cpp.build.components["mcds_recorder"].libdirs = ["mcds\\"+str(self.settings.build_type)]
            self.cpp.build.components["mcds_configurator"].libdirs = ["mcds\\"+str(self.settings.build_type)]
            self.cpp.build.components["mcds_decoder"].libdirs = ["mcds\\"+str(self.settings.build_type)]

    def generate(self):
        # convert conan variables into build-system files
        deps = CMakeDeps(self)          # -> creates FindXXX.cmake (sets paths to XXX lib files in conan cache)
        deps.generate()
        tc = CMakeToolchain(self)       # -> conantoolchain.cmake (variables translated from conan settings)
        tc.cache_variables["BUILD_SHARED_LIBS"] = bool(self.options.shared)
        tc.cache_variables["MCDS_BUILD_MTV"] = self.options.mtv
        tc.cache_variables["MCDS_BUILD_PYTHON"] = self.options.python
        tc.cache_variables["MCDS_BUILD_DEMOS"] = self.options.demos
        tc.cache_variables["MCDS_BUILD_TEST"] = self.options.tests
        tc.cache_variables["MCDS_BUILD_DOCS"] = self.options.docs
        tc.generate()

    def build(self):
        # invoke the build system, reading generated files
        cmake = CMake(self)                 # CMake helper auto-formats CLI arguments for CMake
        cmake.configure()                   # cmake -DCMAKE_TOOLCHAIN_FILE=conantoolchain.cmake
        cmake.build()                       # cmake --build .

    def package(self):
        # copy artifacts from "build" to "package" directory
        cmake = CMake(self)                 # For CMake projects which define an install target, leverage it
        cmake.install()                     # cmake --build . --target=install
                                            # sets CMAKE_INSTALL_PREFIX to appropriate directory in conan cache

    def package_info(self):
        # declare what's in the package for consumers
        self.cpp_info.components["mcds_core"].libs = ["mcds_core"]
        self.cpp_info.components["mcds_core"].includedirs = ["include"]

        self.cpp_info.components["mcds_recorder"].libs = ["mcds_recorder"]
        self.cpp_info.components["mcds_recorder"].includedirs = ["include"]
        self.cpp_info.components["mcds_recorder"].requires = ["mcds_core", "tas_client_api::tas_client", "spdlog::spdlog"]

        self.cpp_info.components["mcds_configurator"].libs = ["mcds_configurator"]
        self.cpp_info.components["mcds_configurator"].includedirs = ["include"]
        self.cpp_info.components["mcds_configurator"].requires = [
            "mcds_core",
            "nlohmann_json::nlohmann_json",
            "json-schema-validator::json-schema-validator",
            "spdlog::spdlog"
        ]

        self.cpp_info.components["mcds_decoder"].libs = ["mcds_decoder"]
        self.cpp_info.components["mcds_decoder"].includedirs = ["include"]
        self.cpp_info.components["mcds_decoder"].requires = [
            "mcds_core",
            "elf_disasm::elf_disasm",
            "capstone::capstone",
            "spdlog::spdlog"
        ]

        if not self.options.shared:
            self.cpp_info.components["mcds_recorder"].defines = ["MCDS_STATIC_DEFINE"]
            self.cpp_info.components["mcds_configurator"].defines = ["MCDS_STATIC_DEFINE"]
            self.cpp_info.components["mcds_decoder"].defines = ["MCDS_STATIC_DEFINE"]
