import sys
import os
import glob
import setuptools

from setuptools import setup

# Ensure optimized compilation flags
if 'CXXFLAGS' not in os.environ:
    os.environ['CXXFLAGS'] = '-O3 -DNDEBUG -s'
if 'CFLAGS' not in os.environ:
    os.environ['CFLAGS'] = '-O3 -DNDEBUG -s'

__version__ = "1.0.0"

from distutils.core import setup, Extension
from distutils.command.build_ext import build_ext

class get_pybind_include(object):
    def __str__(self):
        import pybind11
        return pybind11.get_include()

class get_numpy_include(object):
    def __str__(self):
        import numpy
        return numpy.get_include()

root = os.path.abspath(os.path.dirname(__file__))
platform = os.getenv("ROUTIO_PYTHON_PLATFORM", sys.platform)

if platform.startswith('linux'):
    library_suffix = '.so'
elif platform in ['darwin']:
    library_suffix = '.dylib'
elif platform.startswith('win'):
    library_suffix = '.dll'

class build_ext_ctypes(build_ext):

    def build_extension(self, ext):
        self._ctypes = isinstance(ext, CTypes)

        def _realize(c):
            from typing import Callable
            if isinstance(c, Callable):
                return c()
            else:
                return c

        ct = self.compiler.compiler_type
        opts = ["-std=c++20"]

        ext.include_dirs = [_realize(x) for x in ext.include_dirs]
        ext.extra_compile_args = opts

        return super().build_extension(ext)

    def get_export_symbols(self, ext):
        if self._ctypes:
            return ext.export_symbols
        return super().get_export_symbols(ext)

    def get_ext_filename(self, ext_name):
        if self._ctypes:
            return ext_name + library_suffix
        return super().get_ext_filename(ext_name)

class CTypes(Extension): 
    pass

varargs = dict()
varargs["package_data"] = {}
varargs["cmdclass"] = {}

try:
    from wheel.bdist_wheel import bdist_wheel as _bdist_wheel

    class bdist_wheel(_bdist_wheel):

        def finalize_options(self):
            _bdist_wheel.finalize_options(self)
            # Mark us as not a pure python package
            self.root_is_pure = False

        def get_tag(self):
            python, abi, plat = _bdist_wheel.get_tag(self)
            # We don't contain any python source
            python, abi = 'py2.py3', 'none'
            return python, abi, plat

        def run(self):
            # Force rebuild of extensions with optimization flags
            self.run_command('build_ext')
            
            # Temporarily exclude C++ source files from wheels
            original_exclude = varargs.get("exclude_package_data", {})
            varargs["exclude_package_data"] = {'routio.source': ['*.cpp']}
            varargs["exclude_packages"] = ['routio.source']

            try:
                _bdist_wheel.run(self)
            finally:
                # Restore original exclude_package_data
                if original_exclude:
                    varargs["exclude_package_data"] = original_exclude
                else:
                    varargs.pop("exclude_package_data", None)

    varargs["cmdclass"] = {'bdist_wheel': bdist_wheel, 'build_ext': build_ext_ctypes}
    varargs["setup_requires"] = ['wheel']

except ImportError:
    pass

# Detect which command is being run
if os.path.isfile(os.path.join("routio", "pyroutio" + library_suffix)):
    # Binary exists - include it for wheel builds, exclude for source distributions
    varargs["package_data"]["routio"] = ["pyroutio" + library_suffix]
    # Exclude the routio.source package (contains C++ source files)

elif os.path.isfile(os.path.join("routio", "source", "loop.cpp")):
    # Source files exist - for sdist, compile from source; for wheels, build the extension
    sources = glob.glob("routio/source/*.cpp")
    args = dict(sources=sources, include_dirs=[os.path.join("routio", "source"), get_numpy_include(), get_pybind_include()], define_macros=[("ROUTIO_EXPORTS", "1")])
    args["language"] = "c++"
    varargs["ext_modules"] = [CTypes("routio.pyroutio", **args)]
    varargs["cmdclass"] = {'build_ext': build_ext_ctypes}
    varargs["setup_requires"] = ["pybind11>=2.5.0", "numpy>=1.16"]
    # Include headers and source for source distributions (sdist)
    varargs["package_data"]['routio.source'] = ['*.h', '*.cpp']
    varargs["package_data"]['routio.source.routio'] = ['*.h']

varargs["package_data"]['routio.messages.library'] = ['*.msg']
varargs["package_data"]['routio.messages.templates'] = ['*.tpl']

setup(
    name='routio',
    version=__version__,
    author='Luka Cehovin Zajc',
    author_email='luka.cehovin@protonmail.com',
    url='https://github.com/lukacu/routio',
    project_urls={
        'Documentation': 'https://github.com/lukacu/routio',
        'Source Code': 'https://github.com/lukacu/routio',
        'Bug Tracker': 'https://github.com/lukacu/routio/issues',
    },
    description='Python package for routio IPC library',
    long_description='A simple and efficient interprocess communication (IPC) library for Linux written in C++11.',
    long_description_content_type='text/plain',
    keywords=['ipc', 'interprocess', 'communication', 'messaging', 'rpc'],
    classifiers=[
        'Development Status :: 4 - Beta',
        'Intended Audience :: Developers',
        'Operating System :: POSIX :: Linux',
        'Programming Language :: C++',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.10',
        'Programming Language :: Python :: 3.11',
        'Programming Language :: Python :: 3.12',
        'Programming Language :: Python :: 3.13',
        'Topic :: Software Development :: Libraries',
        'Topic :: System :: Distributed Computing',
    ],
    packages=setuptools.find_packages(),
    include_package_data=True,
    python_requires='>=3.10',
    zip_safe=False,
    entry_points={
        'console_scripts': [
            'routio-daemon = routio.__main__:main',
            'routio-router = routio.__main__:main',
            'routio-generate = routio.messages.cli:main',
        ],
        'ignition': [
            'routio_remap = routio.ignition:Mapping',
        ],
    },
    **varargs
)
