#!/usr/bin/env python
# Python binding for Unicorn engine. Nguyen Anh Quynh <aquynh@gmail.com>

import glob
import os
import platform
import shutil
import stat
import sys

from distutils import log
from distutils import dir_util
from distutils.command.build_clib import build_clib
from distutils.command.sdist import sdist
from distutils.core import setup
from distutils.sysconfig import get_python_lib

# prebuilt libraries for Windows - for sdist
PATH_LIB64 = "prebuilt/win64/unicorn.dll"
PATH_LIB32 = "prebuilt/win32/unicorn.dll"

# package name can be 'unicorn' or 'unicorn-windows'
PKG_NAME = 'unicorn'
if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32):
    PKG_NAME = 'unicorn-windows'

VERSION = '0.9'
SYSTEM = sys.platform

# virtualenv breaks import, but get_python_lib() will work.
SITE_PACKAGES = os.path.join(get_python_lib(), "unicorn")
if "--user" in sys.argv:
    try:
        from site import getusersitepackages
        SITE_PACKAGES = os.path.join(getusersitepackages(), "unicorn")
    except ImportError:
        pass


SETUP_DATA_FILES = []

# adapted from commit e504b81 of Nguyen Tan Cong
# Reference: https://docs.python.org/2/library/platform.html#cross-platform
is_64bits = sys.maxsize > 2**32

def copy_sources():
    """Copy the C sources into the source directory.
    This rearranges the source files under the python distribution
    directory.
    """
    src = []

    try:
        dir_util.remove_tree("src/")
    except (IOError, OSError):
        pass

    dir_util.copy_tree("../../arch", "src/arch/")
    dir_util.copy_tree("../../include", "src/include/")

    src.extend(glob.glob("../../*.[ch]"))
    src.extend(glob.glob("../../*.mk"))

    src.extend(glob.glob("../../Makefile"))
    src.extend(glob.glob("../../LICENSE*"))
    src.extend(glob.glob("../../README"))
    src.extend(glob.glob("../../*.TXT"))
    src.extend(glob.glob("../../RELEASE_NOTES"))
    src.extend(glob.glob("../../make.sh"))
    src.extend(glob.glob("../../CMakeLists.txt"))

    for filename in src:
        outpath = os.path.join("./src/", os.path.basename(filename))
        log.info("%s -> %s" % (filename, outpath))
        shutil.copy(filename, outpath)


class custom_sdist(sdist):
    """Reshuffle files for distribution."""

    def run(self):
        # if prebuilt libraries are existent, then do not copy source
        if os.path.exists(PATH_LIB64) and os.path.exists(PATH_LIB32):
            return sdist.run(self)
        copy_sources()
        return sdist.run(self)


class custom_build_clib(build_clib):
    """Customized build_clib command."""

    def run(self):
        log.info('running custom_build_clib')
        build_clib.run(self)

    def finalize_options(self):
        # We want build-clib to default to build-lib as defined by the "build"
        # command.  This is so the compiled library will be put in the right
        # place along side the python code.
        self.set_undefined_options('build',
                                   ('build_lib', 'build_clib'),
                                   ('build_temp', 'build_temp'),
                                   ('compiler', 'compiler'),
                                   ('debug', 'debug'),
                                   ('force', 'force'))

        build_clib.finalize_options(self)

    def build_libraries(self, libraries):
        if SYSTEM in ("win32", "cygwin"):
            # if Windows prebuilt library is available, then include it
            if is_64bits and os.path.exists(PATH_LIB64):
                SETUP_DATA_FILES.append(PATH_LIB64)
                return
            elif os.path.exists(PATH_LIB32):
                SETUP_DATA_FILES.append(PATH_LIB32)
                return

        # build library from source if src/ is existent
        if not os.path.exists('src'):
            return

        try:
            for (lib_name, build_info) in libraries:
                log.info("building '%s' library", lib_name)

                os.chdir("src")

                # platform description refers at https://docs.python.org/2/library/sys.html#sys.platform
                if SYSTEM == "cygwin":
                    os.chmod("make.sh", stat.S_IREAD|stat.S_IEXEC)
                    if is_64bits:
                        os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh cygwin-mingw64")
                    else:
                        os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh cygwin-mingw32")
                    SETUP_DATA_FILES.append("src/unicorn.dll")
                else:   # Unix
                    os.chmod("make.sh", stat.S_IREAD|stat.S_IEXEC)
                    os.system("UNICORN_BUILD_CORE_ONLY=yes ./make.sh")
                    if SYSTEM == "darwin":
                        SETUP_DATA_FILES.append("src/libunicorn.dylib")
                    else:   # Non-OSX
                        SETUP_DATA_FILES.append("src/libunicorn.so")

                os.chdir("..")
        except:
            pass


def dummy_src():
    return []


setup(
    provides=['unicorn'],
    packages=['unicorn'],
    name=PKG_NAME,
    version=VERSION,
    author='Nguyen Anh Quynh',
    author_email='aquynh@gmail.com',
    description='Unicorn CPU emulator engine',
    url='http://www.unicorn-engine.org',
    classifiers=[
        'License :: OSI Approved :: BSD License',
        'Programming Language :: Python :: 2',
        'Programming Language :: Python :: 3',
    ],
    requires=['ctypes'],
    cmdclass=dict(
        build_clib=custom_build_clib,
        sdist=custom_sdist,
    ),

    libraries=[(
        'unicorn', dict(
            package='unicorn',
            sources=dummy_src()
        ),
    )],

    data_files=[(SITE_PACKAGES, SETUP_DATA_FILES)],
)
