"""Script to build C extensions."""

import os
import shutil
import subprocess

import numpy as np
from setuptools import Extension
from setuptools.command.build_clib import build_clib
from setuptools.command.build_ext import build_ext
from setuptools.command.develop import develop
from setuptools.errors import CompileError

extensions = [
    Extension(
        "speakeasy2._speakeasy2",
        sources=["speakeasy2/_speakeasy2.c"],
        include_dirs=[
            np.get_include(),
            "vendor/speakeasy2/include",
            "vendor/speakeasy2/vendor/igraph/include",
            "vendor/python-igraph/src/_igraph",
        ],
    )
]


class CMakeCLibBuilder(build_clib):
    """C library builder that uses CMake.

    Note the libraries sent to build_libraries are from the setup_kwargs
    dictionary. And the source key holds the location of the CMake project
    rather than a list of sources like in the default C library builders.
    """

    _cmake_cmd = shutil.which("cmake")

    def build_libraries(self, libraries):
        if not self._cmake_cmd:
            raise CompileError("CMake must be installed to compile extension")

        for lib_name, lib_dict in libraries:
            lib_name = f"lib{lib_name}"
            if not os.path.exists(os.path.join(self.build_temp, lib_name)):
                self.cmake_config(lib_name, lib_dict["source"])

            self.cmake_build(lib_name)

    def _cmake(self, args):
        args.insert(0, self._cmake_cmd)
        subprocess.call(args)

    def cmake_config(self, name, source):
        args = [f"-B {os.path.join(self.build_temp, name)}", f"-S {source}"]

        if shutil.which("ninja"):
            args.append("-G Ninja")

        self._cmake(args)

    def cmake_build(self, name):
        self._cmake(["--build", os.path.join(self.build_temp, name)])


class ExtBuilder(build_ext):
    def run(self):
        # On Windows, the extension builder's temp build directory is appended
        # with Release. Elsewhere C lib's temp build directory and the
        # extension builder's are the same.
        if os.path.split(self.build_temp)[1] == "Release":
            clib_build_dir = os.path.split(self.build_temp)[0]
        else:
            clib_build_dir = self.build_temp

        self.include_dirs.append(
            os.path.join(clib_build_dir, "libSpeakEasy2", "include")
        )
        self.library_dirs.append(
            os.path.join(clib_build_dir, "libSpeakEasy2", "src", "speakeasy2")
        )

        build_ext.run(self)


class CustomDevelop(develop):
    def run(self):
        self.run_command("build_ext")
        super().run()


def build(setup_kwargs):
    """Modify the dictionary used by setuptools when calling build stages.

    Called by the setup.py script generated by poetry.
    """
    setup_kwargs.update(
        {
            "libraries": [("SpeakEasy2", {"source": "vendor/speakeasy2"})],
            "ext_modules": extensions,
            "cmdclass": {
                "develop": CustomDevelop,
                "build_clib": CMakeCLibBuilder,
                "build_ext": ExtBuilder,
            },
        }
    )
