import os
import sys

from setuptools import Extension, setup
from setuptools.command.bdist_wheel import bdist_wheel
from setuptools.command.build_ext import build_ext
from setuptools.errors import CCompilerError, ExecError, PlatformError

cmdclass = {}
PYPY = hasattr(sys, "pypy_version_info")

if os.name == "nt":
    # Disable unknown pragma warning
    compile_args = ["-wd4068"]
    libraries = ["Ws2_32"]
else:
    compile_args = ["-Wall", "-Wextra", "-Wno-unknown-pragmas"]
    libraries = []


if os.getenv("MAXMINDDB_USE_SYSTEM_LIBMAXMINDDB"):
    ext_module = [
        Extension(
            "maxminddb.extension",
            libraries=["maxminddb", *libraries],
            sources=["extension/maxminddb.c"],
            extra_compile_args=compile_args,
        ),
    ]
else:
    ext_module = [
        Extension(
            "maxminddb.extension",
            libraries=libraries,
            sources=[
                "extension/maxminddb.c",
                "extension/libmaxminddb/src/data-pool.c",
                "extension/libmaxminddb/src/maxminddb.c",
            ],
            define_macros=[
                ("HAVE_CONFIG_H", 0),
                ("MMDB_LITTLE_ENDIAN", 1 if sys.byteorder == "little" else 0),
                # We define these for maximum compatibility. The extension
                # itself supports all variations currently, but probing to
                # see what the compiler supports is a bit annoying to do
                # here, and we aren't using uint128 for much.
                ("MMDB_UINT128_USING_MODE", 0),
                ("MMDB_UINT128_IS_BYTE_ARRAY", 1),
                ("PACKAGE_VERSION", '"maxminddb-python"'),
            ],
            include_dirs=[
                "extension",
                "extension/libmaxminddb/include",
                "extension/libmaxminddb/src",
            ],
            extra_compile_args=compile_args,
        ),
    ]

# Cargo cult code for installing extension with pure Python fallback.
# Taken from SQLAlchemy, but this same basic code exists in many modules.
ext_errors = (CCompilerError, ExecError, PlatformError)


class BuildFailed(Exception):
    def __init__(self, cause: Exception) -> None:
        super().__init__()
        self.cause = cause


class ve_build_ext(build_ext):
    # This class allows C extension building to fail.

    def run(self) -> None:
        try:
            build_ext.run(self)
        except PlatformError as ex:
            raise BuildFailed(ex) from ex

    def build_extension(self, ext) -> None:
        try:
            build_ext.build_extension(self, ext)
        except ext_errors as ex:
            raise BuildFailed(ex) from ex
        except ValueError as ex:
            # this can happen on Windows 64 bit, see Python issue 7511
            if "'path'" in str(ex):
                raise BuildFailed(ex) from ex
            raise


cmdclass["build_ext"] = ve_build_ext


def status_msgs(*msgs):
    print("*" * 75)
    for msg in msgs:
        print(msg)
    print("*" * 75)


def find_packages(location):
    packages = []
    for pkg in ["maxminddb"]:
        for _dir, _subdirectories, files in os.walk(os.path.join(location, pkg)):
            if "__init__.py" in files:
                tokens = _dir.split(os.sep)[len(location.split(os.sep)) :]
                packages.append(".".join(tokens))
    return packages


def run_setup(with_cext) -> None:
    kwargs = {}
    loc_cmdclass = cmdclass.copy()
    if with_cext:
        kwargs["ext_modules"] = ext_module
        loc_cmdclass["bdist_wheel"] = bdist_wheel

    setup(cmdclass=loc_cmdclass, **kwargs)


try:
    run_setup(True)
except BuildFailed as exc:
    if os.getenv("MAXMINDDB_REQUIRE_EXTENSION"):
        raise
    status_msgs(
        exc.cause,
        "WARNING: The C extension could not be compiled, "
        + "speedups are not enabled.",
        "Failure information, if any, is above.",
        "Retrying the build without the C extension now.",
    )

    run_setup(False)

    status_msgs(
        "WARNING: The C extension could not be compiled, "
        + "speedups are not enabled.",
        "Plain-Python build succeeded.",
    )
