#!/usr/bin/env python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

import glob
import os
import os.path as osp
import re
import shutil
import sys

from Cython.Distutils import build_ext as _build_ext
import Cython


import pkg_resources
from setuptools import setup, Extension, Distribution

from os.path import join as pjoin

from distutils.command.clean import clean as _clean
from distutils.util import strtobool
from distutils import sysconfig

# Check if we're running 64-bit Python
is_64_bit = sys.maxsize > 2**32

if Cython.__version__ < '0.19.1':
    raise Exception('Please upgrade to Cython 0.19.1 or newer')

setup_dir = os.path.abspath(os.path.dirname(__file__))


class clean(_clean):

    def run(self):
        _clean.run(self)
        for x in []:
            try:
                os.remove(x)
            except OSError:
                pass


class build_ext(_build_ext):

    def build_extensions(self):
        numpy_incl = pkg_resources.resource_filename('numpy', 'core/include')

        self.extensions = [ext for ext in self.extensions
                           if ext.name != '__dummy__']

        for ext in self.extensions:
            if (hasattr(ext, 'include_dirs') and
                    numpy_incl not in ext.include_dirs):
                ext.include_dirs.append(numpy_incl)
        _build_ext.build_extensions(self)

    def run(self):
        self._run_cmake()
        _build_ext.run(self)

    # adapted from cmake_build_ext in dynd-python
    # github.com/libdynd/dynd-python

    description = "Build the C-extensions for arrow"
    user_options = ([('extra-cmake-args=', None, 'extra arguments for CMake'),
                     ('build-type=', None, 'build type (debug or release)'),
                     ('with-parquet', None, 'build the Parquet extension'),
                     ('with-plasma', None, 'build the Plasma extension'),
                     ('bundle-arrow-cpp', None,
                      'bundle the Arrow C++ libraries')] +
                    _build_ext.user_options)

    def initialize_options(self):
        _build_ext.initialize_options(self)
        self.extra_cmake_args = os.environ.get('PYARROW_CMAKE_OPTIONS', '')
        self.build_type = os.environ.get('PYARROW_BUILD_TYPE', 'debug').lower()

        self.cmake_cxxflags = os.environ.get('PYARROW_CXXFLAGS', '')

        if sys.platform == 'win32':
            # Cannot do debug builds in Windows unless Python itself is a debug
            # build
            if not hasattr(sys, 'gettotalrefcount'):
                self.build_type = 'release'

        self.with_parquet = strtobool(
            os.environ.get('PYARROW_WITH_PARQUET', '0'))
        self.with_plasma = strtobool(
            os.environ.get('PYARROW_WITH_PLASMA', '0'))
        self.bundle_arrow_cpp = strtobool(
            os.environ.get('PYARROW_BUNDLE_ARROW_CPP', '0'))

    CYTHON_MODULE_NAMES = [
        'lib',
        '_parquet',
        'plasma']

    def _run_cmake(self):
        # The directory containing this setup.py
        source = osp.dirname(osp.abspath(__file__))

        # The staging directory for the module being built
        build_temp = pjoin(os.getcwd(), self.build_temp)
        build_lib = os.path.join(os.getcwd(), self.build_lib)

        # Change to the build directory
        saved_cwd = os.getcwd()
        if not os.path.isdir(self.build_temp):
            self.mkpath(self.build_temp)
        os.chdir(self.build_temp)

        # Detect if we built elsewhere
        if os.path.isfile('CMakeCache.txt'):
            cachefile = open('CMakeCache.txt', 'r')
            cachedir = re.search('CMAKE_CACHEFILE_DIR:INTERNAL=(.*)',
                                 cachefile.read()).group(1)
            cachefile.close()
            if (cachedir != build_temp):
                return

        static_lib_option = ''

        cmake_options = [
            '-DPYTHON_EXECUTABLE=%s' % sys.executable,
            static_lib_option,
        ]

        if self.with_parquet:
            cmake_options.append('-DPYARROW_BUILD_PARQUET=on')

        if self.with_plasma:
            cmake_options.append('-DPYARROW_BUILD_PLASMA=on')

        if len(self.cmake_cxxflags) > 0:
            cmake_options.append('-DPYARROW_CXXFLAGS="{0}"'
                                 .format(self.cmake_cxxflags))

        if self.bundle_arrow_cpp:
            cmake_options.append('-DPYARROW_BUNDLE_ARROW_CPP=ON')
            # ARROW-1090: work around CMake rough edges
            if 'ARROW_HOME' in os.environ and sys.platform != 'win32':
                pkg_config = pjoin(os.environ['ARROW_HOME'], 'lib',
                                   'pkgconfig')
                os.environ['PKG_CONFIG_PATH'] = pkg_config
                del os.environ['ARROW_HOME']

        cmake_options.append('-DCMAKE_BUILD_TYPE={0}'
                             .format(self.build_type.lower()))

        if sys.platform != 'win32':
            cmake_command = (['cmake', self.extra_cmake_args] +
                             cmake_options + [source])

            print("-- Runnning cmake for pyarrow")
            self.spawn(cmake_command)
            print("-- Finished cmake for pyarrow")
            args = ['make']
            if os.environ.get('PYARROW_BUILD_VERBOSE', '0') == '1':
                args.append('VERBOSE=1')

            if 'PYARROW_PARALLEL' in os.environ:
                args.append('-j{0}'.format(os.environ['PYARROW_PARALLEL']))
            print("-- Running cmake --build for pyarrow")
            self.spawn(args)
            print("-- Finished cmake --build for pyarrow")
        else:
            import shlex
            cmake_generator = 'Visual Studio 14 2015 Win64'
            if not is_64_bit:
                raise RuntimeError('Not supported on 32-bit Windows')

            # Generate the build files
            extra_cmake_args = shlex.split(self.extra_cmake_args)
            cmake_command = (['cmake'] + extra_cmake_args +
                             cmake_options +
                             [source, '-G', cmake_generator])
            if "-G" in self.extra_cmake_args:
                cmake_command = cmake_command[:-2]

            print("-- Runnning cmake for pyarrow")
            self.spawn(cmake_command)