from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
from conan.tools.files import copy, load
from conan.tools.gnu import PkgConfigDeps
from conan.tools.env import VirtualBuildEnv

import os
import re


class Lib3MFConan(ConanFile):
    name = "lib3mf"
    license = "BSD-2-Clause"
    url = "https://github.com/3MFConsortium/lib3mf"
    description = "An implementation of the 3D Manufacturing Format file standard"
    topics = ("3mf", "additive-manufacturing", "cad", "serialization")
    package_type = "library"

    settings = "os", "arch", "compiler", "build_type"
    options = {
        "shared": [True, False],
        "fPIC": [True, False],
        "use_platform_uuid": [True, False],
        "with_vendored_zlib": [True, False],
        "with_vendored_libzip": [True, False],
        "with_vendored_ssl": [True, False],
    }
    default_options = {
        "shared": True,
        "fPIC": True,
        "use_platform_uuid": False,
        "with_vendored_zlib": False,
        "with_vendored_libzip": False,
        "with_vendored_ssl": False,
    }

    exports_sources = (
        "Autogenerated/*",
        "AutomaticComponentToolkit/*",
        "Include/*",
        "Libraries/*",
        "Source/*",
        "submodules/*",
        "cmake/*",
        "CMakeLists.txt",
        "lib3mf.pc.in",
        "LICENSE",
        "README.md",
    )

    def set_version(self):
        cmakelists = load(self, os.path.join(self.recipe_folder, "CMakeLists.txt"))
        major = re.search(r"set\(LIB3MF_VERSION_MAJOR\s+(\d+)\)", cmakelists)
        minor = re.search(r"set\(LIB3MF_VERSION_MINOR\s+(\d+)\)", cmakelists)
        patch = re.search(r"set\(LIB3MF_VERSION_MICRO\s+(\d+)\)", cmakelists)
        prerelease = re.search(r'set\(LIB3MF_VERSION_PRERELEASE\s+"([^"]*)"\)', cmakelists)
        if not (major and minor and patch):
            raise ConanInvalidConfiguration("Could not parse lib3mf version from CMakeLists.txt")
        version = f"{major.group(1)}.{minor.group(1)}.{patch.group(1)}"
        if prerelease and prerelease.group(1):
            version = f"{version}-{prerelease.group(1)}"
        self.version = version

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

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

    def layout(self):
        cmake_layout(self)

    def requirements(self):
        if not self.options.with_vendored_zlib:
            self.requires("zlib/[>=1.3 <2]")
        if not self.options.with_vendored_libzip:
            self.requires("libzip/[>=1.10 <2]")
        if not self.options.with_vendored_ssl:
            # Optional direct exposure to enable explicit non-vendored SSL selection from consumer profiles.
            self.requires("openssl/[>=1.1 <4]")

    def build_requirements(self):
        if not self.options.with_vendored_zlib or not self.options.with_vendored_libzip:
            self.tool_requires("pkgconf/[>=2.0 <3]")

    def generate(self):
        if not self.options.with_vendored_zlib or not self.options.with_vendored_libzip:
            pkg = PkgConfigDeps(self)
            pkg.generate()

            buildenv = VirtualBuildEnv(self)
            buildenv.generate()

        tc = CMakeToolchain(self)
        tc.variables["LIB3MF_BUILD_SHARED"] = bool(self.options.shared)
        tc.variables["LIB3MF_TESTS"] = False
        tc.variables["USE_INCLUDED_ZLIB"] = bool(self.options.with_vendored_zlib)
        tc.variables["USE_INCLUDED_LIBZIP"] = bool(self.options.with_vendored_libzip)
        tc.variables["USE_INCLUDED_SSL"] = bool(self.options.with_vendored_ssl)
        tc.variables["USE_PLATFORM_UUID"] = bool(self.options.use_platform_uuid)
        tc.variables["LIB3MF_BUILD_WASM"] = False
        tc.generate()

    def build(self):
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def package(self):
        cmake = CMake(self)
        cmake.install()
        copy(self, "LICENSE", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))

    def package_info(self):
        self.cpp_info.set_property("cmake_file_name", "lib3mf")
        self.cpp_info.set_property("cmake_target_name", "lib3mf::lib3mf")
        if self.settings.os == "Windows":
            self.cpp_info.libs = ["lib3mf"]
        else:
            self.cpp_info.libs = ["3mf"]
        self.cpp_info.includedirs = [os.path.join("include", "Bindings", "Cpp")]
        self.cpp_info.builddirs = [os.path.join("lib", "cmake", "lib3mf")]

        # Keep legacy generators working.
        self.cpp_info.names["cmake_find_package"] = "lib3mf"
        self.cpp_info.names["cmake_find_package_multi"] = "lib3mf"
