#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""setup.py file for @PYPACKAGE_NAME@

"""
from setuptools import find_packages
from numpy.distutils.core import setup, Extension
from numpy.distutils.misc_util import Configuration
from distutils.spawn import find_executable

import os
import re
import glob
import fnmatch

# tools to deal with fortran dependencies.
import sys
sys.path.append('@CMAKE_BINARY_DIR@/')
import sort_f90

def parseCMakeVar(var, sep=';'):
    """
    Post-process cmake list-like variables.

    Example::
        a = parseCMakeVar("var1;var2  ; var3;")
        # --> a = ['var', 'var2', 'var3']
    """
    if var != "":
        # split and remove empty strings
        res = list(var.split(sep))
        return list(filter(len, map(str.strip, res)))
    else:
        return []


def parseCMakeDefines(var):
    """
    Post-process cmake variables, corresponding
    to pre-processor definitions.

    Example::

        res = parseCMakeDefines("-DVAR=VAL1:-DVAR2=VAL2:-DVAR3=VAL3")
        # --> res =  [('VAR', 'VAL1'), ('VAR2', 'VAL2'), ('VAR3', 'VAL3')]
    """
    defines = parseCMakeVar(var)
    if len(defines)==0:
        return None

    # regex to match compiler defines like -DMACRO_NAME or
    # -DMACRO_NAME = MACRO_VALUE
    p = re.compile(r'\s*(?:-D)?\s*(\w+)(?:\s*=\s*(\w+))?\s*')

    res = list()
    for d in defines:
        m = p.match(d)
        if m:
            res.append(m.group(1,2))
        else:
            print("\tWarning: Could not extract cmake define from '", d, "'.")
    return res

# --- external libraries to be linked with ---
hysop_link_libraries = parseCMakeVar("@HYSOP_LINK_LIBRARIES@")

# deal with macosx framework ...
extra_flags = []
for lib in hysop_link_libraries:
    res = lib.find('framework')
    if res >= 0:
        index    = hysop_link_libraries.index(lib)
        filename = os.path.basename(lib)
        hysop_link_libraries.pop(index)
        filename = filename.split('.')[0]
        extra_flags.append('-framework ' + filename)
hysop_link_libraries += extra_flags

def create_fortran_extension(name, pyf_file=None, src_dirs=None, sources=None,
                             libdir=None, libs=None, debug_mode=0):
    """Create a new f2py module from fortran files

    Parameters
    ----------
    name: string
        python module name
    pyf_file: string, optional
        f2py interface file (absolute path)
    src_dirs: list, optional
         all directories containing fortran sources (absolute path)
         that must be taken into account. Warning: dir. scan is not
         recursive.
    sources: list, optional
         all fortran files that must be taken into account
    libs: list of strings, optional
         names of all libraries to be linked with to build f2py module
         --> for -l option
    libdir: list of strings, optional
         list of directories, absolute path to libraries listed in libs
         --> for -L... option
    debug_mode: boolean, optional
         if true enable f2py debug mode, default=False

    Notes
    -----
    * either sources or src_dirs is required but both
    can be used simultaneously.
    * only .f90 and .f95 files are taken into account.
    """

    # --- Collect list of fortran files ---
    if sources is None:
        sources = []
        assert src_dirs is not None

    for sdir in src_dirs:
        sources += glob.glob(os.path.join(sdir, '*.f95'))
        sources += glob.glob(os.path.join(sdir, '*.f90'))

    # Reorder source list with fortran modules
    # dependencies. It seems that this is not taken into
    # account in f2py or distutils.
    if pyf_file is not None:
        sources.append(pyf_file)
    sources = sort_f90.sort(sources)
    # --- set f2py options ---
    f2py_options = ['--no-lower', '--no-wrap-functions']
    options = []
    if debug_mode == 1:
        options.append(('F2PY_REPORT_ON_ARRAY_COPY', '1'))
        if os.uname()[0] == 'Linux':
            options.append(('F2PY_REPORT_ATEXIT', '1'))

    # --- set include dir ---
    inc_dir =  parseCMakeVar("@FORTRAN_INCLUDE_DIRS@")
    inc_dir += parseCMakeVar("@MPI_Fortran_INCLUDE_PATH@")
    inc_dir += parseCMakeVar('@CMAKE_Fortran_MODULE_DIRECTORY@')

    # --- set compilation flags ---
    fortran_flags = parseCMakeVar('@Fortran_FLAGS@', sep=' ')

    # we trust cmake for external libraries and
    # add them to linker, without using libraries option
    extra_link_args = hysop_link_libraries

    ext_fort = Extension(name=name,
                         sources=sources,
                         f2py_options=f2py_options,
                         include_dirs=inc_dir,
                         define_macros=options,
                         library_dirs=libdir,
                         libraries=libs,
                         extra_f90_compile_args=fortran_flags,
                         extra_link_args=extra_link_args
                         )
    return ext_fort


# ------------ Set list of packages required to build the module -------------
# List of modules (directories) to be included
with_test = "@WITH_TESTS@" == "ON"
with_opencl = "@WITH_OPENCL@" == "ON"
exclude=[]
if not with_test:
   exclude.append('*tests*')
if not with_opencl:
   exclude.append('*opencl*')
packages = find_packages(exclude=exclude, where="@CMAKE_SOURCE_DIR@")

# Enable this to get debug info
DISTUTILS_DEBUG = 1

# ------------ Extensions (f2py, cython, ...) setup ------------

ext_modules = []

# Check if fortran interface is enabled
enable_fortran = "@USE_FORTRAN@"
ext = {}

if enable_fortran == "ON":
    fortran_root = \
        '@CMAKE_SOURCE_DIR@/hysop'
    hysop_libdir = ['@CMAKE_BINARY_DIR@/src']
    hysoplib = ['@HYSOP_LIBRARY_NAME@']
    f2py_options = ['--no-lower']
    fortran_src = set([])
    # -- list of directories which contains fortran sources --
    # path must be relative to hysop package directory
    subdirs = ['common_f', ]

    ####fortran_src.add('f2py/parameters.f90')
    # -- fftw fortran sources --
    withfftw = "@WITH_FFTW@"
    if withfftw == "ON":
        subdirs.append(os.path.join('numerics', 'fftw_f'))
        fftwdirs = parseCMakeVar('@FFTWLIB@')
        hysop_libdir.extend(fftwdirs)

    # -- scales sources --
    withscales = '@WITH_SCALES@'
    if withscales == "ON":
        subdirs.append('scales_f')
        #fortran_src.add('f2py/scales2py.f90')


    # -- other fortran sources --
    withextras = '@WITH_EXTRAS@'
    if withextras == "ON":
        subdirs.append(os.path.join('numerics', 'extras_f'))

    # -- set full path to fortran sources --
    fortran_src = list(fortran_src)
    for i in range(len(fortran_src)):
        fortran_src[i] = os.path.join(fortran_root, fortran_src[i])

    precision_file = os.path.join('@GENERATED_FORTRAN_FILES_DIR@',
                                  'precision.f90')
    fortran_src.insert(0, precision_file)

    # === Draft for future implementation of fortran interface ===
    # -- f2py signature file --
    f2hysop = "@F_2_HYSOP@"
    pyf_file = os.path.join('@GENERATED_FORTRAN_FILES_DIR@', f2hysop+'.pyf')
    # change from relative path in subdirs to absolute path in num_dirs
    num_dirs = []
    for sd in subdirs:
        num_dirs.append(os.path.join(fortran_root, sd))

    hysop_libdir = [ld.strip() for ld in hysop_libdir]
    # hysop_libdir = ' '.join([ '-L{}'.format(ld) if ld[1]!='L' else hysop_libdir])
    num_dirs.append('@GENERATED_FORTRAN_FILES_DIR@')
    # create python interface to fortran sources
    # For the moment, it includes the 'old' interface
    # to scales and fftw (in sources) and the new
    # interface, in src_dirs
    ext[f2hysop] = create_fortran_extension(
        name='hysop.'+f2hysop,
        sources=fortran_src,
        libdir=hysop_libdir,
        libs=hysoplib,
        pyf_file=pyf_file,
        src_dirs=num_dirs)

    for ex in ext:
        ext_modules.append(ext[ex])

data_files = []

descr = 'Hybrid Computation with Particles.'
authors = 'G.H Cottet, J.M Etancelin, J.B Keck, C.Mimeau, F.Pérignon, C. Picard'
config = Configuration(
    name='@PACKAGE_NAME@',
    version='@HYSOP_VERSION@',
    description=descr,
    author=authors,
    author_email='hysop-members@lists.forge.imag.fr',
    url='https://gricad-gitlab.univ-grenoble-alpes.fr/particle_methods/hysop',
    license='Apache Licence, Version 2.0 (APLv2)',
    package_dir={'': '@CMAKE_SOURCE_DIR@'},
    ext_modules=ext_modules,
    packages=packages,
    data_files=data_files,
)

setup(**config.todict())
