#! /usr/bin/env python

import os
import os.path
import re
import shutil
import subprocess
import sys
from distutils import sysconfig
from ctypes.util import find_library

from setuptools import Command, Extension, setup

PYTHON3 = sys.version_info.major == 3


# Utils
def check_output(args):
    output = subprocess.check_output(args)
    if PYTHON3:
        # check_output returns bytes in PYTHON3.
        output = output.decode()
    return output.rstrip('\n')


def clean_boost_name(name):
    name = name.split('.')[0]
    if name.startswith('lib'):
        name = name[3:]
    return name


def find_boost_library(_id):
    suffixes = [
        "",  # standard naming
        "-mt"  # former naming schema for multithreading build
    ]
    if "python" in _id:
        # Debian naming convention for versions installed in parallel
        suffixes.insert(0, "-py%d%d" % (sys.version_info.major,
                                        sys.version_info.minor))
        # standard suffix for Python3
        suffixes.insert(1, sys.version_info.major)
    for suf in suffixes:
        name = "%s%s" % (_id, suf)
        lib = find_library(name)
        if lib is not None:
            return name


def get_boost_library_names():
    wanted = ['boost_python', 'boost_system', 'boost_thread']
    found = []
    missing = []
    for _id in wanted:
        name = os.environ.get("%s_LIB" % _id.upper(), find_boost_library(_id))
        if name:
            found.append(name)
        else:
            missing.append(_id)
    if missing:
        msg = ""
        for name in missing:
            msg += ("\nMissing {} boost library, try to add its name with "
                    "{}_LIB environment var.").format(name, name.upper())
        raise EnvironmentError(msg)
    return found


class WhichBoostCommand(Command):
    description = 'Output found boost names. Useful for debug.'
    user_options = []

    def initialize_options(self):
        pass

    def finalize_options(self):
        pass

    def run(self):
        print("\n".join(get_boost_library_names()))


cflags = sysconfig.get_config_var('CFLAGS')
sysconfig._config_vars['CFLAGS'] = re.sub(
    ' +', ' ', cflags.replace('-g', '').replace('-Os', '').replace('-arch i386', ''))
opt = sysconfig.get_config_var('OPT')
sysconfig._config_vars['OPT'] = re.sub(
    ' +', ' ', opt.replace('-g', '').replace('-Os', ''))
ldshared = sysconfig.get_config_var('LDSHARED')
sysconfig._config_vars['LDSHARED'] = re.sub(
    ' +', ' ', ldshared.replace('-g', '').replace('-Os', '').replace('-arch i386', ''))
ldflags = sysconfig.get_config_var('LDFLAGS')
sysconfig._config_vars['LDFLAGS'] = re.sub(
    ' +', ' ', ldflags.replace('-g', '').replace('-Os', '').replace('-arch i386', ''))
pycflags = sysconfig.get_config_var('PY_CFLAGS')
sysconfig._config_vars['PY_CFLAGS'] = re.sub(
    ' +', ' ', pycflags.replace('-g', '').replace('-Os', '').replace('-arch i386', ''))
sysconfig._config_vars['CFLAGSFORSHARED'] = ''
os.environ['ARCHFLAGS'] = ''

if os.environ.get("MASON_BUILD", "false") == "true":
    # run bootstrap.sh to get mason builds
    subprocess.call(['./bootstrap.sh'])
    mapnik_config = 'mason_packages/.link/bin/mapnik-config'
    mason_build = True
else:
    mapnik_config = 'mapnik-config'
    mason_build = False


try:
    linkflags = check_output([mapnik_config, '--libs']).split(' ')
    lib_path = linkflags[0][2:]
    linkflags.extend(check_output([mapnik_config, '--ldflags']).split(' '))
except:
    raise Exception("Failed to find proper linking flags from mapnik config")

# Dynamically make the mapnik/paths.py file if it doesn't exist.
if os.path.isfile('mapnik/paths.py'):
    create_paths = False
else:
    create_paths = True
    f_paths = open('mapnik/paths.py', 'w')
    f_paths.write('import os\n')
    f_paths.write('\n')

if mason_build:
    try:
        if sys.platform == 'darwin':
            base_f = 'libmapnik.dylib'
        else:
            base_f = 'libmapnik.so.3.0'
        f = os.path.join(lib_path, base_f)
        shutil.copyfile(f, os.path.join('mapnik', base_f))
    except shutil.Error:
        pass
    input_plugin_path = check_output([mapnik_config, '--input-plugins'])
    input_plugin_files = os.listdir(input_plugin_path)
    input_plugin_files = [os.path.join(
        input_plugin_path, f) for f in input_plugin_files]
    if not os.path.exists(os.path.join('mapnik', 'plugins', 'input')):
        os.makedirs(os.path.join('mapnik', 'plugins', 'input'))
    for f in input_plugin_files:
        try:
            shutil.copyfile(f, os.path.join(
                'mapnik', 'plugins', 'input', os.path.basename(f)))
        except shutil.Error:
            pass
    font_path = check_output([mapnik_config, '--fonts'])
    font_files = os.listdir(font_path)
    font_files = [os.path.join(font_path, f) for f in font_files]
    if not os.path.exists(os.path.join('mapnik', 'plugins', 'fonts')):
        os.makedirs(os.path.join('mapnik', 'plugins', 'fonts'))
    for f in font_files:
        try:
            shutil.copyfile(f, os.path.join(
                'mapnik', 'plugins', 'fonts', os.path.basename(f)))
        except shutil.Error:
            pass
    if create_paths:
        f_paths.write(
            'mapniklibpath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "plugins")\n')
elif create_paths:
    f_paths.write("mapniklibpath = '" + lib_path + "/mapnik'\n")
    f_paths.write('mapniklibpath = os.path.normpath(mapniklibpath)\n')

if create_paths:
    f_paths.write("inputpluginspath = os.path.join(mapniklibpath,'input')\n")
    if os.environ.get('SYSTEM_FONTS'):
        font_path = "os.path.normpath('{path}')".format(path=os.environ.get('SYSTEM_FONTS'))
    else:
        font_path = "os.path.join(mapniklibpath,'fonts')"
    f_paths.write(
        "fontscollectionpath = {path}\n".format(path=font_path))
    f_paths.write(
        "__all__ = [mapniklibpath,inputpluginspath,fontscollectionpath]\n")
    f_paths.close()


if not mason_build:
    icu_path = check_output([mapnik_config, '--icu-data'])
else:
    icu_path = 'mason_packages/.link/share/icu/'
if icu_path:
    icu_files = os.listdir(icu_path)
    icu_files = [os.path.join(icu_path, f) for f in icu_files]
    if not os.path.exists(os.path.join('mapnik', 'plugins', 'icu')):
        os.makedirs(os.path.join('mapnik', 'plugins', 'icu'))
    for f in icu_files:
        try:
            shutil.copyfile(f, os.path.join(
                'mapnik', 'plugins', 'icu', os.path.basename(f)))
        except shutil.Error:
            pass

if not mason_build:
    gdal_path = check_output([mapnik_config, '--gdal-data'])
else:
    gdal_path = 'mason_packages/.link/share/gdal/'
    if os.path.exists('mason_packages/.link/share/gdal/gdal/'):
        gdal_path = 'mason_packages/.link/share/gdal/gdal/'
if gdal_path:
    gdal_files = os.listdir(gdal_path)
    gdal_files = [os.path.join(gdal_path, f) for f in gdal_files]
    if not os.path.exists(os.path.join('mapnik', 'plugins', 'gdal')):
        os.makedirs(os.path.join('mapnik', 'plugins', 'gdal'))
    for f in gdal_files:
        try:
            shutil.copyfile(f, os.path.join(
                'mapnik', 'plugins', 'gdal', os.path.basename(f)))
        except shutil.Error:
            pass

if not mason_build:
    proj_path = check_output([mapnik_config, '--proj-lib'])
else:
    proj_path = 'mason_packages/.link/share/proj/'
    if os.path.exists('mason_packages/.link/share/proj/proj/'):
        proj_path = 'mason_packages/.link/share/proj/proj/'
if proj_path:
    proj_files = os.listdir(proj_path)
    proj_files = [os.path.join(proj_path, f) for f in proj_files]
    if not os.path.exists(os.path.join('mapnik', 'plugins', 'proj')):
        os.makedirs(os.path.join('mapnik', 'plugins', 'proj'))
    for f in proj_files:
        try:
            shutil.copyfile(f, os.path.join(
                'mapnik', 'plugins', 'proj', os.path.basename(f)))
        except shutil.Error:
            pass

extra_comp_args = check_output([mapnik_config, '--cflags']).split(' ')

if sys.platform == 'darwin':
    extra_comp_args.append('-mmacosx-version-min=10.8')
    # silence warning coming from boost python macros which
    # would is hard to silence via pragma
    extra_comp_args.append('-Wno-parentheses-equality')
    linkflags.append('-mmacosx-version-min=10.8')
else:
    linkflags.append('-lrt')
    linkflags.append('-Wl,-z,origin')
    linkflags.append('-Wl,-rpath=$ORIGIN')

if os.environ.get("CC", False) == False:
    os.environ["CC"] = check_output([mapnik_config, '--cxx'])
if os.environ.get("CXX", False) == False:
    os.environ["CXX"] = check_output([mapnik_config, '--cxx'])

setup(
    name="mapnik",
    version="0.1",
    packages=['mapnik'],
    author="Blake Thompson",
    author_email="flippmoke@gmail.com",
    description="Python bindings for Mapnik",
    license="GNU LESSER GENERAL PUBLIC LICENSE",
    keywords="mapnik mapbox mapping carteography",
    url="http://mapnik.org/",
    tests_require=[
        'nose',
    ],
    package_data={
        'mapnik': ['libmapnik.*', 'plugins/*/*'],
    },
    test_suite='nose.collector',
    cmdclass={
        'whichboost': WhichBoostCommand,
    },
    ext_modules=[
        Extension('mapnik._mapnik', [
            'src/mapnik_color.cpp',
            'src/mapnik_coord.cpp',
            'src/mapnik_datasource.cpp',
            'src/mapnik_datasource_cache.cpp',
            'src/mapnik_envelope.cpp',
            'src/mapnik_expression.cpp',
            'src/mapnik_feature.cpp',
            'src/mapnik_featureset.cpp',
            'src/mapnik_font_engine.cpp',
            'src/mapnik_fontset.cpp',
            'src/mapnik_gamma_method.cpp',
            'src/mapnik_geometry.cpp',
            'src/mapnik_grid.cpp',
            'src/mapnik_grid_view.cpp',
            'src/mapnik_image.cpp',
            'src/mapnik_image_view.cpp',
            'src/mapnik_label_collision_detector.cpp',
            'src/mapnik_layer.cpp',
            'src/mapnik_logger.cpp',
            'src/mapnik_map.cpp',
            'src/mapnik_palette.cpp',
            'src/mapnik_parameters.cpp',
            'src/mapnik_proj_transform.cpp',
            'src/mapnik_projection.cpp',
            'src/mapnik_python.cpp',
            'src/mapnik_query.cpp',
            'src/mapnik_raster_colorizer.cpp',
            'src/mapnik_rule.cpp',
            'src/mapnik_scaling_method.cpp',
            'src/mapnik_style.cpp',
            'src/mapnik_svg_generator_grammar.cpp',
            'src/mapnik_symbolizer.cpp',
            'src/mapnik_text_placement.cpp',
            'src/mapnik_view_transform.cpp',
            'src/python_grid_utils.cpp',
        ],
            language='c++',
            libraries=[
                'mapnik',
                'mapnik-wkt',
                'mapnik-json',
            ] + get_boost_library_names(),
            extra_compile_args=extra_comp_args,
            extra_link_args=linkflags,
        )
    ]
)
