from __future__ import print_function
import glob
import os
import platform
import re
import struct
import sys

from distutils.command.build_ext import build_ext
from distutils import sysconfig
from setuptools import Extension, setup, find_packages


_IMAGING = (
    "decode", "encode", "map", "display", "outline", "path",
    )

_LIB_IMAGING = (
    "Access", "AlphaComposite", "Antialias", "Bands", "BitDecode", "Blend",
    "Chops", "Convert", "ConvertYCbCr", "Copy", "Crc32", "Crop", "Dib", "Draw",
    "Effects", "EpsEncode", "File", "Fill", "Filter", "FliDecode",
    "Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode",
    "Histo", "JpegDecode", "JpegEncode", "LzwDecode", "Matrix",
    "ModeFilter", "MspDecode", "Negative", "Offset", "Pack",
    "PackDecode", "Palette", "Paste", "Quant", "QuantOctree", "QuantHash",
    "QuantHeap", "PcdDecode", "PcxDecode", "PcxEncode", "Point",
    "RankFilter", "RawDecode", "RawEncode", "Storage", "SunRleDecode",
    "TgaRleDecode", "Unpack", "UnpackYCC", "UnsharpMask", "XbmDecode",
    "XbmEncode", "ZipDecode", "ZipEncode", "TiffDecode")


def _add_directory(path, dir, where=None):
    if dir and os.path.isdir(dir) and dir not in path:
        if where is None:
            path.append(dir)
        else:
            path.insert(where, dir)


def _find_include_file(self, include):
    for directory in self.compiler.include_dirs:
        if os.path.isfile(os.path.join(directory, include)):
            return 1
    return 0


def _find_library_file(self, library):
    return self.compiler.find_library_file(self.compiler.library_dirs, library)


def _find_version(filename):
    for line in open(filename).readlines():
        m = re.search("VERSION\s*=\s*\"([^\"]+)\"", line)
        if m:
            return m.group(1)
    return None


def _lib_include(root):
    # map root to (root/lib, root/include)
    return os.path.join(root, "lib"), os.path.join(root, "include")


def _read(file):
    return open(file, 'rb').read()

try:
    import _tkinter
except ImportError:
    _tkinter = None


NAME = 'Pillow'
VERSION = '2.0.0'
PIL_VERSION = '1.1.7'
TCL_ROOT = None
JPEG_ROOT = None
ZLIB_ROOT = None
TIFF_ROOT = None
FREETYPE_ROOT = None
LCMS_ROOT = None


class pil_build_ext(build_ext):

    def build_extensions(self):

        global TCL_ROOT

        library_dirs = []
        include_dirs = []

        _add_directory(include_dirs, "libImaging")

        #
        # add configured kits

        for root in (TCL_ROOT, JPEG_ROOT, TIFF_ROOT, ZLIB_ROOT,
                     FREETYPE_ROOT, LCMS_ROOT):
            if isinstance(root, type(())):
                lib_root, include_root = root
            else:
                lib_root = include_root = root
            _add_directory(library_dirs, lib_root)
            _add_directory(include_dirs, include_root)

        #
        # add platform directories

        if sys.platform == "cygwin":
            # pythonX.Y.dll.a is in the /usr/lib/pythonX.Y/config directory
            _add_directory(library_dirs, os.path.join(
                "/usr/lib", "python%s" % sys.version[:3], "config"))

        elif sys.platform == "darwin":
            # attempt to make sure we pick freetype2 over other versions
            _add_directory(include_dirs, "/sw/include/freetype2")
            _add_directory(include_dirs, "/sw/lib/freetype2/include")
            # fink installation directories
            _add_directory(library_dirs, "/sw/lib")
            _add_directory(include_dirs, "/sw/include")
            # darwin ports installation directories
            _add_directory(library_dirs, "/opt/local/lib")
            _add_directory(include_dirs, "/opt/local/include")
            # freetype2 ships with X11
            _add_directory(library_dirs, "/usr/X11/lib")
            _add_directory(include_dirs, "/usr/X11/include")

        elif sys.platform.startswith("linux"):
            for platform_ in (platform.processor(),platform.architecture()[0]):
                if not platform_: continue

                if platform_ in ["x86_64", "64bit"]:
                    _add_directory(library_dirs, "/lib64")
                    _add_directory(library_dirs, "/usr/lib64")
                    _add_directory(library_dirs, "/usr/lib/x86_64-linux-gnu")
                    break
                elif platform_ in ["i386", "i686", "32bit"]:
                    _add_directory(library_dirs, "/usr/lib/i386-linux-gnu")
                    break
            else:
                raise ValueError("Unable to identify Linux platform: `%s`" % platform_)

            # XXX Kludge. Above /\ we brute force support multiarch. Here we
            # try Barry's more general approach. Afterward, something should
            # work ;-)
            self.add_multiarch_paths()

        _add_directory(library_dirs, "/usr/local/lib")
        # FIXME: check /opt/stuff directories here?

        prefix = sysconfig.get_config_var("prefix")
        if prefix:
            _add_directory(library_dirs, os.path.join(prefix, "lib"))
            _add_directory(include_dirs, os.path.join(prefix, "include"))

        #
        # locate tkinter libraries


        if _tkinter:
            TCL_VERSION = _tkinter.TCL_VERSION[:3]

        if _tkinter and not TCL_ROOT:
            # we have Tkinter but the TCL_ROOT variable was not set;
            # try to locate appropriate Tcl/Tk libraries
            PYVERSION = sys.version[0] + sys.version[2]
            TCLVERSION = TCL_VERSION[0] + TCL_VERSION[2]
            roots = [
                # common installation directories, mostly for Windows
                # (for Unix-style platforms, we'll check in well-known
                # locations later)
                os.path.join("/py" + PYVERSION, "Tcl"),
                os.path.join("/python" + PYVERSION, "Tcl"),
                "/Tcl", "/Tcl" + TCLVERSION, "/Tcl" + TCL_VERSION,
                os.path.join(os.environ.get("ProgramFiles", ""), "Tcl"),
                ]
            for TCL_ROOT in roots:
                TCL_ROOT = os.path.abspath(TCL_ROOT)
                if os.path.isfile(os.path.join(TCL_ROOT, "include", "tk.h")):
                    # FIXME: use distutils logging (?)
                    print("--- using Tcl/Tk libraries at", TCL_ROOT)
                    print("--- using Tcl/Tk version", TCL_VERSION)
                    TCL_ROOT = _lib_include(TCL_ROOT)
                    break
            else:
                TCL_ROOT = None


        #
        # add standard directories

        # look for tcl specific subdirectory (e.g debian)
        if _tkinter:
            tcl_dir = "/usr/include/tcl" + TCL_VERSION
            if os.path.isfile(os.path.join(tcl_dir, "tk.h")):
                _add_directory(include_dirs, tcl_dir)

        # standard locations
        _add_directory(library_dirs, "/usr/local/lib")
        _add_directory(include_dirs, "/usr/local/include")

        _add_directory(library_dirs, "/usr/lib")
        _add_directory(include_dirs, "/usr/include")

        #
        # insert new dirs *before* default libs, to avoid conflicts
        # between Python PYD stub libs and real libraries

        self.compiler.library_dirs = library_dirs + self.compiler.library_dirs
        self.compiler.include_dirs = include_dirs + self.compiler.include_dirs

        #
        # look for available libraries

        class feature:
            zlib = jpeg = tiff = freetype = tcl = tk = lcms = webp = None
        feature = feature()

        if _find_include_file(self, "zlib.h"):
            if _find_library_file(self, "z"):
                feature.zlib = "z"
            elif sys.platform == "win32" and _find_library_file(self, "zlib"):
                feature.zlib = "zlib"  # alternative name

        if _find_include_file(self, "jpeglib.h"):
            if _find_library_file(self, "jpeg"):
                feature.jpeg = "jpeg"
            elif sys.platform == "win32" and _find_library_file(self,
                    "libjpeg"):
                feature.jpeg = "libjpeg"  # alternative name

        if _find_library_file(self, "tiff"):
            feature.tiff = "tiff"
        if sys.platform == "win32" and _find_library_file(self, "libtiff"):
            feature.tiff = "libtiff"
        if sys.platform == "darwin" and _find_library_file(self, "libtiff"):
            feature.tiff = "libtiff"

        if _find_library_file(self, "freetype"):
            # look for freetype2 include files
            freetype_version = 0
            for dir in self.compiler.include_dirs:
                if os.path.isfile(os.path.join(dir, "ft2build.h")):
                    freetype_version = 21
                    dir = os.path.join(dir, "freetype2")
                    break
                dir = os.path.join(dir, "freetype2")
                if os.path.isfile(os.path.join(dir, "ft2build.h")):
                    freetype_version = 21
                    break
                if os.path.isdir(os.path.join(dir, "freetype")):
                    freetype_version = 20
                    break
            if freetype_version:
                feature.freetype = "freetype"
                feature.freetype_version = freetype_version
                if dir:
                    _add_directory(self.compiler.include_dirs, dir, 0)

        if _find_include_file(self, "lcms.h"):
            if _find_library_file(self, "lcms"):
                feature.lcms = "lcms"

        if _tkinter and _find_include_file(self, "tk.h"):
            # the library names may vary somewhat (e.g. tcl84 or tcl8.4)
            version = TCL_VERSION[0] + TCL_VERSION[2]
            if _find_library_file(self, "tcl" + version):
                feature.tcl = "tcl" + version
            elif _find_library_file(self, "tcl" + TCL_VERSION):
                feature.tcl = "tcl" + TCL_VERSION
            if _find_library_file(self, "tk" + version):
                feature.tk = "tk" + version
            elif _find_library_file(self, "tk" + TCL_VERSION):
                feature.tk = "tk" + TCL_VERSION

        if _find_include_file(self, "webp/encode.h") and _find_include_file(self, "webp/decode.h"):
            if _find_library_file(self, "webp"):
                feature.webp = "webp"

        #
        # core library

        files = ["_imaging.c"]
        for file in _IMAGING:
            files.append(file + ".c")
        for file in _LIB_IMAGING:
            files.append(os.path.join("libImaging", file + ".c"))

        libs = []
        defs = []
        if feature.jpeg:
            libs.append(feature.jpeg)
            defs.append(("HAVE_LIBJPEG", None))
        if feature.zlib:
            libs.append(feature.zlib)
            defs.append(("HAVE_LIBZ", None))
        if feature.tiff:
            libs.append(feature.tiff)
            defs.append(("HAVE_LIBTIFF", None))
        if sys.platform == "win32":
            libs.extend(["kernel32", "user32", "gdi32"])
        if struct.unpack("h", "\0\1".encode('ascii'))[0] == 1:
            defs.append(("WORDS_BIGENDIAN", None))

        exts = [(Extension(
            "_imaging", files, libraries=libs, define_macros=defs))]

        #
        # additional libraries

        if feature.freetype:
            defs = []
            if feature.freetype_version == 20:
                defs.append(("USE_FREETYPE_2_0", None))
            exts.append(Extension(
                "_imagingft", ["_imagingft.c"], libraries=["freetype"],
                define_macros=defs))

        if os.path.isfile("_imagingtiff.c") and feature.tiff:
            exts.append(Extension(
                "_imagingtiff", ["_imagingtiff.c"], libraries=["tiff"]))

        if os.path.isfile("_imagingcms.c") and feature.lcms:
            extra = []
            if sys.platform == "win32":
                extra.extend(["user32", "gdi32"])
            exts.append(Extension(
                "_imagingcms", ["_imagingcms.c"], libraries=["lcms"] + extra))

        if os.path.isfile("_webp.c") and feature.webp:
            exts.append(Extension(
                "_webp", ["_webp.c"], libraries=["webp"]))


        if sys.platform == "darwin":
            # locate Tcl/Tk frameworks
            frameworks = []
            framework_roots = [
                "/Library/Frameworks",
                "/System/Library/Frameworks"]
            for root in framework_roots:
                if (os.path.exists(os.path.join(root, "Tcl.framework")) and
                    os.path.exists(os.path.join(root, "Tk.framework"))):
                    print("--- using frameworks at %s" % root)
                    frameworks = ["-framework", "Tcl", "-framework", "Tk"]
                    dir = os.path.join(root, "Tcl.framework", "Headers")
                    _add_directory(self.compiler.include_dirs, dir, 0)
                    dir = os.path.join(root, "Tk.framework", "Headers")
                    _add_directory(self.compiler.include_dirs, dir, 1)
                    break
            if frameworks:
                exts.append(Extension(
                    "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
                    extra_compile_args=frameworks, extra_link_args=frameworks))
                feature.tcl = feature.tk = 1  # mark as present
        elif feature.tcl and feature.tk:
            exts.append(Extension(
                "_imagingtk", ["_imagingtk.c", "Tk/tkImaging.c"],
                libraries=[feature.tcl, feature.tk]))

        if os.path.isfile("_imagingmath.c"):
            exts.append(Extension("_imagingmath", ["_imagingmath.c"]))

        self.extensions[:] = exts

        build_ext.build_extensions(self)

        #
        # sanity and security checks

        unsafe_zlib = None

        if feature.zlib:
            unsafe_zlib = self.check_zlib_version(self.compiler.include_dirs)

        self.summary_report(feature, unsafe_zlib)

    def summary_report(self, feature, unsafe_zlib):

        print("-" * 68)
        print("SETUP SUMMARY (Pillow %s fork, originally based on PIL %s)" % (VERSION, PIL_VERSION))
        print("-" * 68)
        print("version      %s (Pillow)" % VERSION)
        v = sys.version.split("[")
        print("platform     %s %s" % (sys.platform, v[0].strip()))
        for v in v[1:]:
            print("             [%s" % v.strip())
        print("-" * 68)

        options = [
            (feature.tcl and feature.tk, "TKINTER"),
            (feature.jpeg, "JPEG"),
            (feature.zlib, "ZLIB (PNG/ZIP)"),
            (feature.tiff, "TIFF G3/G4 (experimental)"),
            (feature.freetype, "FREETYPE2"),
            (feature.lcms, "LITTLECMS"),
            (feature.webp, "WEBP"),
            ]

        all = 1
        for option in options:
            if option[0]:
                print("--- %s support available" % option[1])
            else:
                print("*** %s support not available" % option[1])
                if option[1] == "TKINTER" and _tkinter:
                    version = _tkinter.TCL_VERSION
                    print("(Tcl/Tk %s libraries needed)" % version)
                all = 0

        if feature.zlib and unsafe_zlib:
            print("")
            print("*** Warning: zlib", unsafe_zlib)
            print("may contain a security vulnerability.")
            print("*** Consider upgrading to zlib 1.2.3 or newer.")
            print("*** See: http://www.kb.cert.org/vuls/id/238678")
            print(" http://www.kb.cert.org/vuls/id/680620")
            print(" http://www.gzip.org/zlib/advisory-2002-03-11.txt")
            print("")

        print("-" * 68)

        if not all:
            print("To add a missing option, make sure you have the required")
            print("library, and set the corresponding ROOT variable in the")
            print("setup.py script.")
            print("")

        print("To check the build, run the selftest.py script.")
        print("")

    def check_zlib_version(self, include_dirs):
        # look for unsafe versions of zlib
        for dir in include_dirs:
            zlibfile = os.path.join(dir, "zlib.h")
            if os.path.isfile(zlibfile):
                break
        else:
            return
        for line in open(zlibfile).readlines():
            m = re.match('#define\s+ZLIB_VERSION\s+"([^"]*)"', line)
            if not m:
                continue
            if m.group(1) < "1.2.3":
                return m.group(1)

    # http://hg.python.org/users/barry/rev/7e8deab93d5a
    def add_multiarch_paths(self):
        # Debian/Ubuntu multiarch support.
        # https://wiki.ubuntu.com/MultiarchSpec
        # self.build_temp
        tmpfile = os.path.join(self.build_temp, 'multiarch')
        if not os.path.exists(self.build_temp):
            os.makedirs(self.build_temp)
        ret = os.system('dpkg-architecture -qDEB_HOST_MULTIARCH > %s' %
                        tmpfile)
        ret = os.system(
            'dpkg-architecture -qDEB_HOST_MULTIARCH > %s 2> /dev/null' %
            tmpfile)
        try:
            if ret >> 8 == 0:
                fp = open(tmpfile, 'r')
                multiarch_path_component = fp.readline().strip()
                _add_directory(self.compiler.library_dirs,
                    '/usr/lib/' + multiarch_path_component)
                _add_directory(self.compiler.include_dirs,
                    '/usr/include/' + multiarch_path_component)
        finally:
            os.unlink(tmpfile)

setup(
    name=NAME,
    version=VERSION,
    description='Python Imaging Library (fork)',
    long_description=(
        _read('README.rst') + b'\n' +
        _read('docs/HISTORY.txt') + b'\n' +
        _read('docs/CONTRIBUTORS.txt')).decode('utf-8'),
    author='Alex Clark (fork author)',
    author_email='aclark@aclark.net',
    url='http://github.com/python-imaging/Pillow',
    classifiers=[
        "Development Status :: 6 - Mature",
        "Topic :: Multimedia :: Graphics",
        "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera",
        "Topic :: Multimedia :: Graphics :: Capture :: Scanners",
        "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture",
        "Topic :: Multimedia :: Graphics :: Graphics Conversion",
        "Topic :: Multimedia :: Graphics :: Viewers",
        "Programming Language :: Python :: 2",
        "Programming Language :: Python :: 2.6",
        "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: 3.2",
        "Programming Language :: Python :: 3.3",
        ],
    cmdclass={"build_ext": pil_build_ext},
    ext_modules=[Extension("_imaging", ["_imaging.c"])],
    packages=find_packages(),
    scripts=glob.glob("Scripts/pil*.py"),
    keywords=["Imaging",],
    license='Standard PIL License',
    )
