#!/usr/bin/env python

from setuptools import setup
from setuptools.command.build_ext import build_ext
from setuptools import Extension, Command
from distutils import sysconfig

import os

try:
    from sphinx.setup_command import BuildDoc as _BuildDoc

    class BuildDoc(_BuildDoc):
        def finalize_options(self):
            super().finalize_options()
            if not self.project:
                self.project = self.distribution.name
            if not self.version:
                self.version = self.distribution.version

except ImportError:
    BuildDoc = None


class CmakeRun(Command):
    description = 'Run cmake to build external library'

    user_options = [
        ('source-dir=', 'S', 'the source dir'),
        ('libname=', 'N', 'the project name'),
        ('define=', 'D', 'define options'),
        ('cmake=', None, 'the cmake executable [default: cmake]'),
        ('make=', None, 'the make executable [default: make]'),
    ]

    boolean_options = ['debug', 'trace']

    def initialize_options(self):
        """Set default values for all the options."""
        self.debug = None
        self.trace = None
        self.define = False
        self.source_dir = None
        self.build_dir = None
        self.libname = None
        self.cmake = 'cmake'
        self.make = 'make'

    def finalize_options(self):
        """Set final values for all the options."""
        self.ensure_dirname('source_dir')
        self.source_dir = os.path.abspath(self.source_dir)
        if self.build_dir is None:
            build = self.get_finalized_command('build')
            self.build_dir = os.path.join(os.path.abspath(build.build_temp), self.libname)
            self.mkpath(self.build_dir)
        self.build_dir = os.path.abspath(self.build_dir)
        if self.define:
            self.define = ['-D{0}'.format(v) for v in self.define.split(',') if v]

    def command(self, cmd):
        """announce command and spawn"""
        self.announce(cmd)
        self.spawn(cmd)

    @staticmethod
    def set_env():
        for k in ("CFLAGS", "LDFLAGS", "CXXFLAGS"):
            os.environ[k] = sysconfig.get_config_var(k) or ""

    def run(self):
        """build target by cmake"""
        pwd = os.getcwd()
        os.chdir(self.build_dir)
        cmake_cmd = [self.cmake, self.source_dir] + self.define
        make_cmd = [self.make]
        if self.debug:
            cmake_cmd.append('--debug-output')
            make_cmd.append('-d')
        if self.trace:
            cmake_cmd.append('--trace')

        try:
            self.set_env()
            self.command(cmake_cmd)
            self.command(make_cmd)
        finally:
            os.chdir(pwd)


class GenErrors(Command):
    description = 'Generate Error Codes'

    def initialize_options(self):
        """Set default values for all the options."""
        self.sources = None
        self.source_dir = None
        self.target_dir = None

    def finalize_options(self):
        """Set final values for all the options."""
        self.ensure_dirname('source_dir')
        if self.source_dir is None:
            cmake = self.get_finalized_command('build_client')
            self.source_dir = os.path.join(cmake.build_dir, 'include')
            self.mkpath(self.source_dir)
        self.ensure_dirname('target_dir')
        if self.target_dir is None:
            build = self.get_finalized_command('build')
            self.target_dir = os.path.join(build.build_temp, 'gen_errors')
            self.target_dir = os.path.abspath(self.target_dir)
            self.mkpath(self.target_dir)

        if isinstance(self.sources, str):
            self.sources = self.sources.split(',')
        else:
            self.sources = []

    def run(self):
        """generate c-code for python based on error-codes in h file"""
        for src in self.sources:
            self.make_er(os.path.join(self.source_dir, src + '.h'), os.path.join(self.target_dir, src + '.c'))

    @staticmethod
    def make_er(source, target):
        with open(source, 'rb') as source:
            with open(target, 'wb') as target:
                target.write(b"/* Autogenerated file, please don't edit */\n")
                target.write(b"#include <mysqld_error.h>\n")
                target.write(b"#define PY_SSIZE_T_CLEAN 1\n#include <Python.h>\n\n")
                target.write(b"int wsql_constants_add_errors(PyObject* module) {\n")
                for line in source:
                    if not line.startswith(b'#define'):
                        continue
                    name = line.split()[1]
                    target.write(b"    if (PyModule_AddIntMacro(module, " + name + b") < 0) return -1;\n")
                target.write(b"    return 0;\n")
                target.write(b"}\n")


class BuildExt(build_ext):
    sub_commands = [('build_client', None), ('gen_errors', None)]

    def run(self):
        """run logic"""
        for sub_command in self.get_sub_commands():
            self.distribution.run_command(sub_command)

        cmake = self.get_finalized_command('build_client')
        self.include_dirs.extend(os.path.join(x, 'include') for x in (cmake.build_dir, cmake.source_dir))
        self.libraries.append(cmake.libname)

        self.add_library_dirs(os.path.join(cmake.build_dir, x) for x in ('libmysql', 'zlib'))
        self.add_library_dirs(os.path.join(cmake.build_dir, 'extra', 'yassl', x) for x in ('', 'taocrypt'))
        self.parse_libraries(os.path.join(cmake.build_dir, 'libmysql', 'libraries.txt'))

        gen_errors = self.get_finalized_command('gen_errors')
        target_dir = gen_errors.target_dir
        sources = gen_errors.sources
        self.extensions[0].sources.extend(os.path.join(target_dir, x + '.c') for x in sources)
        super().run()

        if BuildDoc is not None and not os.getenv('CI_BUILD', ''):
            import sys
            sys.path.append(self.build_lib)
            self.distribution.run_command('build_doc')

    def add_library_dirs(self, paths):
        for p in paths:
            if os.path.exists(p):
                self.library_dirs.append(p)

    def parse_libraries(self, source_file):
        with open(source_file, 'r') as source:
            libraries = source.read().split(';')

        for library in libraries:
            path = os.path.dirname(library)
            basename = os.path.basename(library)
            if path:
                self.library_dirs.append(os.path.abspath(path))
            if basename:
                end = basename.rfind('.')
                if basename.startswith('lib'):
                    start = 3
                else:
                    start = 0
                if end == -1:
                    end = None
                self.libraries.append(basename[start:end])

__name__ = "wsql"
__version__ = "1.2.8"

module1 = Extension('_' + __name__,
                    sources=["./src/connection.c",
                             "./src/constants.c",
                             "./src/exceptions.c",
                             "./src/fields.c",
                             "./src/format.c",
                             "./src/module.c",
                             "./src/results.c"],
                    extra_compile_args=["-Os", "-g", "-std=c99", "-fno-strict-aliasing",
                                        "-Wno-error=declaration-after-statement"],
                    extra_link_args=["-lstdc++"],
                    define_macros=[
                        ("MODULE_NAME", '_' + __name__),
                        ("MODULE_VERSION_INFO", "(%d, %d, %d, 'beta', 0)" % tuple(map(int, __version__.split('.')))),
                        ("MODULE_VERSION", __version__),
                    ])


def readme():
    with open('README') as r:
        return r.read()


cmdclass = {'build_ext': BuildExt,
            'build_client': CmakeRun,
            'build_doc': BuildDoc,
            'gen_errors': GenErrors}

setup(
    name=__name__,
    version=__version__,
    description='Asynchronous Python interface to MySQL',
    cmdclass=cmdclass,
    ext_modules=[module1],
    packages=["wsql", "wsql.cluster"],
    author="@bg",
    author_email='gaifullinbf@gmail.com',
    maintainer='Bulat Gaifullin',
    maintainer_email='gaifullinbf@gmail.com',
    url='https://github.com/WSQL/wsql',
    download_url="https://github.com/WebSQL/wsql/archive/v%s.tar.gz" % __version__,
    license='GPL',
    long_description=readme(),
    keywords=["mysql", "asyncio", "asyncsql"],
    classifiers=[
        "Development Status :: 5 - Production/Stable",
        "Environment :: Other Environment",
        "License :: OSI Approved :: GNU General Public License (GPL)",
        "Operating System :: MacOS :: MacOS X",
        "Operating System :: OS Independent",
        "Operating System :: POSIX",
        "Operating System :: POSIX :: Linux",
        "Operating System :: Unix",
        "Programming Language :: C",
        "Programming Language :: Python :: 3.4",
        "Topic :: Database",
        "Topic :: Database :: Database Engines/Servers",
    ]
)

# to build debug
# CFLAGS='-Wall -O0 -g' python3 setup.py build
