#!/usr/bin/env python
from __future__ import print_function

from setuptools import setup
from setuptools import Distribution
from setuptools.command.sdist import sdist
from setuptools.extension import Extension
import platform
import re
import sys
import os


SKIP_CYTHON_FILE = '__dont_use_cython__.txt'

if os.path.exists(SKIP_CYTHON_FILE):
    print("In distributed package, building from C files...", file=sys.stderr)
    SOURCE_EXT = 'c'
else:
    try:
        from Cython.Build import cythonize
        print("Building from Cython files...", file=sys.stderr)
        SOURCE_EXT = 'pyx'
    except ImportError:
        print("Cython not found, building from C files...",
              file=sys.stderr)
        SOURCE_EXT = 'c'

get_output = None

try:
    import commands
    get_output = commands.getoutput
except ImportError:
    import subprocess

    def _get_output(*args, **kwargs):
        res = subprocess.check_output(*args, shell=True, **kwargs)
        decoded = res.decode('utf-8')
        return decoded.strip()

    get_output = _get_output

# get the compile and link args
link_args = os.environ.get('GSSAPI_LINKER_ARGS', None)
compile_args = os.environ.get('GSSAPI_COMPILER_ARGS', None)
osx_has_gss_framework = False
if sys.platform == 'darwin':
    mac_ver = [int(v) for v in platform.mac_ver()[0].split('.')]
    osx_has_gss_framework = (mac_ver >= [10, 7, 0])

if link_args is None:
    if osx_has_gss_framework:
        link_args = '-framework GSS'
    else:
        link_args = get_output('krb5-config --libs gssapi')

if compile_args is None:
    if osx_has_gss_framework:
        compile_args = '-framework GSS -DOSX_HAS_GSS_FRAMEWORK'
    else:
        compile_args = get_output('krb5-config --cflags gssapi')

link_args = link_args.split()
compile_args = compile_args.split()

# add in the extra workarounds for different include structures
prefix = get_output('krb5-config gssapi --prefix')
gssapi_ext_h = os.path.join(prefix, 'include/gssapi/gssapi_ext.h')
if os.path.exists(gssapi_ext_h):
    compile_args.append("-DHAS_GSSAPI_EXT_H")

# ensure that any specific directories are listed before any generic system
# directories inserted by setuptools
library_dirs = [arg[2:] for arg in link_args if arg.startswith('-L')]
link_args = [arg for arg in link_args if not arg.startswith('-L')]

ENABLE_SUPPORT_DETECTION = \
    (os.environ.get('GSSAPI_SUPPORT_DETECT', 'true').lower() == 'true')

if ENABLE_SUPPORT_DETECTION:
    import ctypes.util

    main_lib = os.environ.get('GSSAPI_MAIN_LIB', None)
    main_path = ""
    if main_lib is None and osx_has_gss_framework:
        main_lib = ctypes.util.find_library('GSS')
    elif main_lib is None:
        for opt in link_args:
            if opt.startswith('-lgssapi'):
                main_lib = 'lib%s.so' % opt[2:]

            # To support Heimdal on Debian, read the linker path.
            if opt.startswith('-Wl,/'):
                main_path = opt[4:] + "/"

    if main_lib is None:
        raise Exception("Could not find main GSSAPI shared library.  Please "
                        "try setting GSSAPI_MAIN_LIB yourself or setting "
                        "ENABLE_SUPPORT_DETECTION to 'false'")

    GSSAPI_LIB = ctypes.CDLL(main_path + main_lib)


# add in the flag that causes us not to compile from Cython when
# installing from an sdist
class sdist_gssapi(sdist):
    def run(self):
        if not self.dry_run:
            with open(SKIP_CYTHON_FILE, 'w') as flag_file:
                flag_file.write('COMPILE_FROM_C_ONLY')

            sdist.run(self)

            os.remove(SKIP_CYTHON_FILE)


DONT_CYTHONIZE_FOR = ('clean',)


class GSSAPIDistribution(Distribution, object):
    def run_command(self, command):
        self._last_run_command = command
        Distribution.run_command(self, command)

    @property
    def ext_modules(self):
        if SOURCE_EXT != 'pyx':
            return getattr(self, '_ext_modules', None)

        if getattr(self, '_ext_modules', None) is None:
            return None

        if getattr(self, '_last_run_command', None) in DONT_CYTHONIZE_FOR:
            return self._ext_modules

        if getattr(self, '_cythonized_ext_modules', None) is None:
            self._cythonized_ext_modules = cythonize(self._ext_modules)

        return self._cythonized_ext_modules

    @ext_modules.setter
    def ext_modules(self, mods):
        self._cythonized_ext_modules = None
        self._ext_modules = mods

    @ext_modules.deleter
    def ext_modules(self):
        del self._ext_modules
        del self._cythonized_ext_modules


# detect support
def main_file(module):
    return Extension('gssapi.raw.%s' % module,
                     extra_link_args=link_args,
                     extra_compile_args=compile_args,
                     library_dirs=library_dirs,
                     sources=['gssapi/raw/%s.%s' % (module, SOURCE_EXT)])


ENUM_EXTS = []


def extension_file(module, canary):
    if ENABLE_SUPPORT_DETECTION and not hasattr(GSSAPI_LIB, canary):
        print('Skipping the %s extension because it '
              'is not supported by your GSSAPI implementation...' % module)
        return None
    else:
        enum_ext_path = 'gssapi/raw/_enum_extensions/ext_%s.%s' % (module,
                                                                   SOURCE_EXT)
        if os.path.exists(enum_ext_path):
            ENUM_EXTS.append(
                Extension('gssapi.raw._enum_extensions.ext_%s' % module,
                          extra_link_args=link_args,
                          extra_compile_args=compile_args,
                          sources=[enum_ext_path],
                          library_dirs=library_dirs,
                          include_dirs=['gssapi/raw/']))

        return Extension('gssapi.raw.ext_%s' % module,
                         extra_link_args=link_args,
                         extra_compile_args=compile_args,
                         library_dirs=library_dirs,
                         sources=['gssapi/raw/ext_%s.%s' % (module,
                                                            SOURCE_EXT)])


def gssapi_modules(lst):
    # filter out missing files
    res = [mod for mod in lst if mod is not None]

    # add in supported mech files
    MECHS_SUPPORTED = os.environ.get('GSSAPI_MECHS', 'krb5').split(',')
    for mech in MECHS_SUPPORTED:
        res.append(Extension('gssapi.raw.mech_%s' % mech,
                             extra_link_args=link_args,
                             extra_compile_args=compile_args,
                             library_dirs=library_dirs,
                             sources=['gssapi/raw/mech_%s.%s' % (mech,
                                                                 SOURCE_EXT)]))

    # add in any present enum extension files
    res.extend(ENUM_EXTS)

    return res


long_desc = re.sub('\.\. role:: \w+\(code\)\s*\n\s*.+', '',
                   re.sub(r':(python|bash|code):', '',
                          re.sub(r'\.\. code-block:: \w+', '::',
                                 open('README.txt').read())))

install_requires = [
    'decorator',
    'six >= 1.4.0'
]
if sys.version_info < (3, 4):
    install_requires.append('enum34')

setup(
    name='gssapi',
    version='1.4.1',
    author='The Python GSSAPI Team',
    author_email='sross@redhat.com',
    packages=['gssapi', 'gssapi.raw', 'gssapi.raw._enum_extensions',
              'gssapi.tests'],
    description='Python GSSAPI Wrapper',
    long_description=long_desc,
    license='LICENSE.txt',
    url="https://github.com/pythongssapi/python-gssapi",
    classifiers=[
        'Development Status :: 4 - Beta',
        'Programming Language :: Python',
        'Programming Language :: Python :: 2.7',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.3',
        'Intended Audience :: Developers',
        'License :: OSI Approved :: ISC License (ISCL)',
        'Programming Language :: Python :: Implementation :: CPython',
        'Programming Language :: Cython',
        'Topic :: Security',
        'Topic :: Software Development :: Libraries :: Python Modules'
    ],
    distclass=GSSAPIDistribution,
    cmdclass={'sdist': sdist_gssapi},
    ext_modules=gssapi_modules([
        main_file('misc'),
        main_file('exceptions'),
        main_file('creds'),
        main_file('names'),
        main_file('sec_contexts'),
        main_file('types'),
        main_file('message'),
        main_file('oids'),
        main_file('cython_converters'),
        main_file('chan_bindings'),
        extension_file('s4u', 'gss_acquire_cred_impersonate_name'),
        extension_file('cred_store', 'gss_store_cred_into'),
        extension_file('rfc5587', 'gss_indicate_mechs_by_attrs'),
        extension_file('rfc5588', 'gss_store_cred'),
        extension_file('rfc5801', 'gss_inquire_saslname_for_mech'),
        extension_file('cred_imp_exp', 'gss_import_cred'),
        extension_file('dce', 'gss_wrap_iov'),
        extension_file('iov_mic', 'gss_get_mic_iov'),
        extension_file('ggf', 'gss_inquire_sec_context_by_oid'),

        # see ext_rfc6680_comp_oid for more information on this split
        extension_file('rfc6680', 'gss_display_name_ext'),
        extension_file('rfc6680_comp_oid', 'GSS_C_NT_COMPOSITE_EXPORT'),

        # see ext_password{,_add}.pyx for more information on this split
        extension_file('password', 'gss_acquire_cred_with_password'),
        extension_file('password_add', 'gss_add_cred_with_password'),
    ]),
    keywords=['gssapi', 'security'],
    install_requires=install_requires
)
