"""
Setup script for binarycpython
"""
import os
import subprocess
import re
import sys

import setuptools

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

#
this_file = os.path.abspath(__file__)
this_file_dir = os.path.dirname(this_file)

###
REQUIRED_BINARY_C_VERSIONS = ["2.1.7", "2.2pre1", "2.2.0", "2.2.1"]

############################################################
# Defining functionality
############################################################

# Functions
def version():
    """
    opens VERSION and returns version number
    """

    with open("VERSION") as file:
        return file.read().strip()

def readme():
    """
    Opens readme file and returns content
    """

    with open("README.md") as file:
        return file.read()

def license():
    """
    Opens license file and returns the content
    """

    with open("LICENSE.md") as file:
        return file.read()

def requirements(directory):
    """
    Opens requirements.txt and returns content as a list
    """

    requirements_file = os.path.join(directory, 'requirements.txt')

    # Read out file and construct list
    requirements_list = []
    with open(requirements_file) as f:
        for el in f.readlines():
            requirements_list.append(el.strip())

    return requirements_list

def check_version(installed_binary_c_version, required_binary_c_versions):
    """Function to check the installed version and compare it to the required version"""
    message = """
    Something went wrong. Make sure that binary_c config exists.
    Possibly the binary_c version that is installed ({}) does not match the binary_c versions ({})
    that this release of the binary_c python module requires.
    """.format(
        installed_binary_c_version, required_binary_c_versions
    )
    assert installed_binary_c_version in required_binary_c_versions, message

def execute_make():
    """
    Function to execute the makefile.

    This makefile builds the binary_c_python_api library that python will use to interface wth
    """

    # Custom extra command:
    make_command = ["make"]

    p = subprocess.run(make_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout = p.stdout  # stdout = normal output
    stderr = p.stderr  # stderr = error output

    if p.returncode != 0:
        print("Something went wrong when executing the makefile:")
        print(stderr.decode("utf-8"))
        print("Aborting")
        sys.exit(-1)

    else:
        print(stdout.decode("utf-8"))
        print("Successfully built the libbinary_c_api.so")

def call_binary_c_config(binary_c_dir, command):
    """
    Function to call the binary_c config
    """


    binary_c_config = os.path.join(BINARY_C_DIR, "binary_c-config")

    command_result = (
        subprocess.run([binary_c_config, command], stdout=subprocess.PIPE, check=True)
        .stdout.decode("utf-8")
        .split()
    )

    return command_result

############################################################
# First level checks
############################################################

####
GSL_DIR = os.getenv("GSL_DIR", None)
if not GSL_DIR:
    print(
        "Warning: GSL_DIR is not set, this might lead to errors along the installation if\
        there is no other version of GSL in the include dirs"
    )
BINARY_C_DIR = os.getenv("BINARY_C", None)
if not BINARY_C_DIR:
    print(
        "\n\n****\n**** Error: the BINARY_C environment variable is not set.\n**** This environment variable should point to the root of your binary_c\n**** installation (i.e. the directory you acquired from the repository).\n**** Aborting setup.\n****\n\n"
    )
    quit(1)

############################################################
# Getting information from binary_c
############################################################

# binary_c must be installed.
BINARY_C_VERSION = call_binary_c_config(BINARY_C_DIR, "version")
check_version(BINARY_C_VERSION[0], REQUIRED_BINARY_C_VERSIONS)

BINARY_C_INCDIRS = call_binary_c_config(BINARY_C_DIR, "incdirs_list")
BINARY_C_LIBDIRS = call_binary_c_config(BINARY_C_DIR, "libdirs_list")
BINARY_C_CFLAGS = call_binary_c_config(BINARY_C_DIR, "cflags")
# BINARY_C_CFLAGS.remove('-fvisibility=hidden')
BINARY_C_LIBS = call_binary_c_config(BINARY_C_DIR, "libs_list")
BINARY_C_DEFINES = call_binary_c_config(BINARY_C_DIR, "define_macros")

# create list of tuples of defined macros
BINARY_C_DEFINE_MACROS = []
LONE = re.compile("^-D(.+)$")
PARTNER = re.compile("^-D(.+)=(.+)$")
for x in BINARY_C_DEFINES:
    y = PARTNER.match(x)
    if y:
        BINARY_C_DEFINE_MACROS.extend([(y.group(1), y.group(2))])
    else:
        y = LONE.match(x)
        if y:
            BINARY_C_DEFINE_MACROS.extend([(y.group(1), None)])

# add API header file
API_h = os.path.join(BINARY_C_DIR, "src", "API", "binary_c_API.h")

############################################################
# Determine all directories and libraries
############################################################

INCLUDE_DIRS = [
    os.path.join(BINARY_C_DIR, "src"),
    os.path.join(BINARY_C_DIR, "src", "API"),
    "include",
] + BINARY_C_INCDIRS
if GSL_DIR:
    INCLUDE_DIRS += [os.path.join(GSL_DIR, "include")]

# LIBRARIES = ["binary_c"] + BINARY_C_LIBS + ["binary_c_python_api"]
LIBRARIES = ["binary_c"] + BINARY_C_LIBS

LIBRARY_DIRS = [
    os.path.join(BINARY_C_DIR, "src"),
    "./",
    os.path.join(this_file_dir, "lib/"),
    os.path.join(this_file_dir, "binarycpython/"),
] + BINARY_C_LIBDIRS

RUNTIME_LIBRARY_DIRS = [
    os.path.join(BINARY_C_DIR, "src"),
    "./",
    os.path.join(this_file_dir, "lib/"),
] + BINARY_C_LIBDIRS

# filter out duplicates
INCLUDE_DIRS = list(dict.fromkeys(INCLUDE_DIRS))
BINARY_C_LIBS = list(dict.fromkeys(BINARY_C_LIBS))
LIBRARIES = list(dict.fromkeys(LIBRARIES))
LIBRARY_DIRS = list(dict.fromkeys(LIBRARY_DIRS))
RUNTIME_LIBRARY_DIRS = list(dict.fromkeys(RUNTIME_LIBRARY_DIRS))

############################################################
# Making the python-c binding module
############################################################

BINARY_C_PYTHON_API_MODULE = Extension(
    name="binarycpython._binary_c_bindings",
    sources=["src/binary_c_python.c"],
    include_dirs=INCLUDE_DIRS,
    libraries=LIBRARIES,
    library_dirs=LIBRARY_DIRS,
    runtime_library_dirs=RUNTIME_LIBRARY_DIRS,
    define_macros=[] + BINARY_C_DEFINE_MACROS,
    extra_objects=[],
    extra_compile_args=[],
    language="C",
)
headers = ["src/includes/header.h"]

############################################################
# Custom build command
############################################################

# Override build command
class CustomBuildCommand(distutils.command.build.build):
    def run(self):
        # Run the original build command
        distutils.command.build.build.run(self)

############################################################
# Main setup function call
############################################################

setup(
    name="binarycpython",
    version=version(),
    description="""This is a python API for binary_c (versions {}) by David Hendriks, Rob Izzard and collaborators. Based on the initial set up by Jeff andrews.""".format(
        ",".join(REQUIRED_BINARY_C_VERSIONS),
        ",".join(REQUIRED_BINARY_C_VERSIONS),
    ),
    author="David Hendriks",
    author_email="davidhendriks93@gmail.com",
    long_description=readme(),
    long_description_content_type="text/markdown",
    url="https://gitlab.eps.surrey.ac.uk/ri0005/binary_c-python",
    license="gpl",
    keywords=[
        "binary_c",
        "astrophysics",
        "stellar evolution",
        "population synthesis",
    ],  # Keywords that define your package best
    packages=[
        "binarycpython",
        "binarycpython.utils",
        "binarycpython.utils.population_extensions",
        "binarycpython.tests",
        "binarycpython.tests.tests_population_extensions",
    ],
    install_requires=requirements(this_file_dir),
    include_package_data=True,
    ext_modules=[BINARY_C_PYTHON_API_MODULE],  # binary_c must be loaded
    classifiers=[
        "Development Status :: 4 - Beta",
        "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
        "Intended Audience :: Developers",
        "Intended Audience :: Science/Research",
        "Operating System :: POSIX :: Linux",
        "Programming Language :: Python :: 3",
        "Programming Language :: C",
        "Topic :: Education",
        "Topic :: Scientific/Engineering :: Physics",
        "Topic :: Software Development :: Libraries :: Python Modules",
    ],
    cmdclass={"build": CustomBuildCommand},
)
