#!/usr/bin/env python

# Two environmental variables influence this script.
#
# GDAL_CONFIG: the path to a gdal-config program that points to GDAL headers,
# libraries, and data files.
#
# PACKAGE_DATA: if defined, GDAL and PROJ4 data files will be copied into the
# source or binary distribution. This is essential when creating self-contained
# binary wheels.

import copy
import logging
import os
import platform
import pprint
import re
import shutil
from subprocess import check_output
import sys

from setuptools import setup
from setuptools.extension import Extension

logging.basicConfig(stream=sys.stderr, level=logging.INFO)
log = logging.getLogger()


def copy_data_tree(datadir, destdir):
    try:
        shutil.rmtree(destdir)
    except OSError:
        pass
    shutil.copytree(datadir, destdir)


# python -W all setup.py ...
if "all" in sys.warnoptions:
    log.level = logging.DEBUG

# Use Cython if available.
try:
    from Cython.Build import cythonize
except ImportError:
    raise SystemExit(
        "ERROR: Cython.Build.cythonize not found. "
        "Cython is required to build rasterio.")

# By default we'll try to get options via gdal-config. On systems without,
# options will need to be set in setup.cfg or on the setup command line.
include_dirs = []
library_dirs = []
libraries = []
extra_link_args = []
gdal2plus = False
gdal_output = [None] * 4
gdalversion = None
gdal_major_version = 0
gdal_minor_version = 0
gdal_patch_version = 0

try:
    import numpy as np

    include_dirs.append(np.get_include())
except ImportError:
    raise SystemExit("ERROR: Numpy and its headers are required to run setup().")

if "clean" not in sys.argv:
    try:
        gdal_config = os.environ.get('GDAL_CONFIG', 'gdal-config')
        for i, flag in enumerate(("--cflags", "--libs", "--datadir", "--version")):
            gdal_output[i] = check_output([gdal_config, flag]).decode("utf-8").strip()

        for item in gdal_output[0].split():
            if item.startswith("-I"):
                include_dirs.extend(item[2:].split(":"))
        for item in gdal_output[1].split():
            if item.startswith("-L"):
                library_dirs.extend(item[2:].split(":"))
            elif item.startswith("-l"):
                libraries.append(item[2:])
            else:
                # e.g. -framework GDAL
                extra_link_args.append(item)
        # datadir, gdal_output[2] handled below

        gdalversion = gdal_output[3]
        if gdalversion:
            log.info("GDAL API version obtained from gdal-config: %s",
                     gdalversion)

    except Exception as e:
        if os.name == "nt":
            log.info("Building on Windows requires extra options to setup.py "
                     "to locate needed GDAL files. More information is available "
                     "in the README.")
        else:
            log.warning("Failed to get options via gdal-config: %s", str(e))

    # Get GDAL API version from environment variable.
    if 'GDAL_VERSION' in os.environ:
        gdalversion = os.environ['GDAL_VERSION']
        log.info("GDAL API version obtained from environment: %s", gdalversion)

    # Get GDAL API version from the command line if specified there.
    if '--gdalversion' in sys.argv:
        index = sys.argv.index('--gdalversion')
        sys.argv.pop(index)
        gdalversion = sys.argv.pop(index)
        log.info("GDAL API version obtained from command line option: %s",
                 gdalversion)

    if not gdalversion:
        raise SystemExit("ERROR: A GDAL API version must be specified. Provide a path "
                 "to gdal-config using a GDAL_CONFIG environment variable "
                 "or use a GDAL_VERSION environment variable.")

    gdal_major_version, gdal_minor_version, gdal_patch_version = map(
        int, re.findall("[0-9]+", gdalversion)[:3]
    )

    if (gdal_major_version, gdal_minor_version) < (3, 8):
        raise SystemExit("ERROR: GDAL >= 3.8 is required for rasterio. "
                 "Please upgrade GDAL.")

# Conditionally copy the GDAL data. To be used in conjunction with
# the bdist_wheel command to make self-contained binary wheels.
if os.environ.get('PACKAGE_DATA'):
    destdir = 'rasterio/gdal_data'
    if gdal_output[2]:
        log.info("Copying gdal data from %s" % gdal_output[2])
        copy_data_tree(gdal_output[2], destdir)
    else:
        # check to see if GDAL_DATA is defined
        gdal_data = os.environ.get('GDAL_DATA', None)
        if gdal_data:
            log.info("Copying gdal_data from %s" % gdal_data)
            copy_data_tree(gdal_data, destdir)

    # Conditionally copy PROJ DATA.
    projdatadir = os.environ.get('PROJ_DATA', os.environ.get('PROJ_LIB', '/usr/local/share/proj'))
    if os.path.exists(projdatadir):
        log.info("Copying proj_data from %s" % projdatadir)
        copy_data_tree(projdatadir, 'rasterio/proj_data')

compile_time_env = {
    "CTE_GDAL_MAJOR_VERSION": gdal_major_version,
    "CTE_GDAL_MINOR_VERSION": gdal_minor_version,
    "CTE_GDAL_PATCH_VERSION": gdal_patch_version,
}

ext_options = {
    'include_dirs': include_dirs,
    'library_dirs': library_dirs,
    'libraries': libraries,
    'extra_link_args': extra_link_args,
    'define_macros': [],
    'cython_compile_time_env': compile_time_env
}

if not os.name == "nt":
    # These options fail on Windows if using Visual Studio
    ext_options['extra_compile_args'] = ['-Wno-unused-parameter',
                                         '-Wno-unused-function']

# Copy extension options for cpp extension modules.
cpp_ext_options = copy.deepcopy(ext_options)

# Remove -std=c++11 from C extension options.
try:
    ext_options['extra_link_args'].remove('-std=c++11')
    ext_options['extra_compile_args'].remove('-std=c++11')
except Exception:
    pass

cpp11_flag = '-std=c++11'

# 'extra_compile_args' may not be defined
eca = cpp_ext_options.get('extra_compile_args', [])

if platform.system() == 'Darwin':

    if cpp11_flag not in eca:
        eca.append(cpp11_flag)

    eca += [cpp11_flag, '-mmacosx-version-min=10.9', '-stdlib=libc++']

# TODO: Windows

elif cpp11_flag not in eca:
    eca.append(cpp11_flag)

cpp_ext_options['extra_compile_args'] = eca

# Configure optional Cython coverage.
cythonize_options = {"language_level": sys.version_info[0], "compiler_directives": {"freethreading_compatible": True}}
if os.environ.get('CYTHON_COVERAGE'):
    cythonize_options['compiler_directives'].update(linetrace=True)
    cythonize_options['annotate'] = True
    ext_options['define_macros'].extend(
        [('CYTHON_TRACE', '1'), ('CYTHON_TRACE_NOGIL', '1')])

log.debug('ext_options:\n%s', pprint.pformat(ext_options))

ext_modules = None
if "clean" not in sys.argv:
    extensions = [
        Extension("rasterio._base", ["rasterio/_base.pyx"], **ext_options),
        Extension("rasterio._io", ["rasterio/_io.pyx"], **ext_options),
        Extension("rasterio._features", ["rasterio/_features.pyx"], **ext_options),
        Extension("rasterio._env", ["rasterio/_env.pyx"], **ext_options),
        Extension("rasterio._warp", ["rasterio/_warp.pyx"], **cpp_ext_options),
        Extension("rasterio._fill", ["rasterio/_fill.pyx"], **cpp_ext_options),
        Extension("rasterio._err", ["rasterio/_err.pyx"], **ext_options),
        Extension("rasterio._example", ["rasterio/_example.pyx"], **ext_options),
        Extension("rasterio._version", ["rasterio/_version.pyx"], **ext_options),
        Extension("rasterio.cache", ["rasterio/cache.pyx"], **ext_options),
        Extension("rasterio.crs", ["rasterio/crs.pyx"], **ext_options),
        Extension("rasterio.shutil", ["rasterio/shutil.pyx"], **ext_options),
        Extension("rasterio._transform", ["rasterio/_transform.pyx"], **ext_options),
        Extension("rasterio._filepath", ["rasterio/_filepath.pyx"], **cpp_ext_options),
        Extension(
            "rasterio._vsiopener", ["rasterio/_vsiopener.pyx"], **ext_options
        ),
    ]
    ext_modules = cythonize(
        extensions, quiet=True, compile_time_env=compile_time_env, **cythonize_options
    )

setup_args = {}
if os.environ.get('PACKAGE_DATA'):
    setup_args['package_data'] = {'rasterio': ['gdal_data/*', 'proj_data/*']}

# See pyproject.toml for project metadata
setup(
    name="rasterio",  # need by GitHub dependency graph
    ext_modules=ext_modules,
    **setup_args,
)
