import sys
import os
import shutil
import pathlib
import multiprocessing

from math import ceil

from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext


def get_project_name() -> str:
    return open(pathlib.Path().absolute() / "project_name.txt", "r").read().strip()


PROJECT_NAME = get_project_name()

SETUP_DIR = pathlib.Path(__file__).parent

class CMakeExtension(Extension):
    def __init__(self, name):
        super().__init__(name, sources=[])


class CMakeBuild(build_ext):
    def __init__(self, dist, *args, **kwargs):
        super().__init__(dist, *args, **kwargs)
        # Detect editable_mode for old versions of setuptools
        if not hasattr(self, "editable_mode"):
            if hasattr(dist, "commands"):
                self.editable_mode = "develop" in dist.commands
            else:
                self.editable_mode = False

    def run(self):
        # This lists the number of processors available on the machine
        # The compilation will use half of them
        max_jobs = str(ceil(multiprocessing.cpu_count() / 2))
        max_jobs = os.environ.get("AIDGE_NB_PROC", max_jobs)

        cwd = pathlib.Path().absolute()

        build_temp = cwd / "build"
        if not build_temp.exists():
            build_temp.mkdir(parents=True, exist_ok=True)

        build_lib = pathlib.Path(self.build_lib)
        if not build_lib.exists():
            build_lib.mkdir(parents=True, exist_ok=True)

        package_prefix = build_lib if not self.editable_mode else SETUP_DIR
        pybind_install_prefix = (package_prefix / PROJECT_NAME).absolute()

        install_path = (
            os.path.join(sys.prefix, "lib", "libAidge")
            if "AIDGE_INSTALL" not in os.environ
            else os.environ["AIDGE_INSTALL"]
        )

        # Read environment variables for CMake options
        c_compiler = os.environ.get("AIDGE_C_COMPILER", "gcc")
        cxx_compiler = os.environ.get("AIDGE_CXX_COMPILER", "g++")
        build_type = os.environ.get("AIDGE_BUILD_TYPE", "Release")
        asan = os.environ.get("AIDGE_ASAN", "OFF")
        cmake_arch = os.environ.get("AIDGE_CMAKE_ARCH", "")

        build_gen = os.environ.get("AIDGE_BUILD_GEN", "")
        build_gen_opts = (
            ["-G", build_gen]
            if build_gen
            else []
        )
        test_onoff = os.environ.get("AIDGE_BUILD_TEST", "OFF")

        os.chdir(str(build_temp))

        cmake_cmd = [
            "cmake",
            *build_gen_opts,
            str(cwd),
            f"-DTEST={test_onoff}",
            f"-DCMAKE_INSTALL_PREFIX:PATH={install_path}",
            f"-DCMAKE_BUILD_TYPE={build_type}",
            f"-DCMAKE_C_COMPILER={c_compiler}",
            f"-DCMAKE_CXX_COMPILER={cxx_compiler}",
            f"-DENABLE_ASAN={asan}",
            "-DPYBIND=ON",
            f"-DPYBIND_INSTALL_PREFIX:PATH={pybind_install_prefix}",
            "-DCMAKE_EXPORT_COMPILE_COMMANDS=1",
            "-DCOVERAGE=OFF",
        ]

        # Append architecture-specific arguments if provided
        if cmake_arch:
            cmake_cmd.append(cmake_arch)

        self.spawn(cmake_cmd)

        if not self.dry_run:
            self.spawn(
                ["cmake", "--build", ".", "--config", build_type, "-j", max_jobs]
            )
            self.spawn(["cmake", "--install", ".", "--config", build_type])
        os.chdir(str(cwd))


if __name__ == "__main__":
    setup(
        ext_modules=[CMakeExtension(PROJECT_NAME)],
        cmdclass={
            "build_ext": CMakeBuild,
        },
        zip_safe=False,
    )