# (C) Copyright 2007-2019 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only
# under the conditions described in the aforementioned license.  The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
# Thanks for using Enthought open source!

import io
import os
import runpy
import subprocess

from setuptools import setup, find_packages

# Version information; update this by hand when making a new bugfix or feature
# release. The actual package version is autogenerated from this information
# together with information from the version control system, and then injected
# into the package source.
MAJOR = 4
MINOR = 9
MICRO = 0
IS_RELEASED = True

# If this file is part of a Git export (for example created with "git archive",
# or downloaded from GitHub), ARCHIVE_COMMIT_HASH gives the full hash of the
# commit that was exported.
ARCHIVE_COMMIT_HASH = "$Format:%H$"

# Templates for version strings.
RELEASED_VERSION = u"{major}.{minor}.{micro}"
UNRELEASED_VERSION = u"{major}.{minor}.{micro}.dev{dev}"

# Paths to the autogenerated version file and the Git directory.
HERE = os.path.abspath(os.path.dirname(__file__))
VERSION_FILE = os.path.join(HERE, "envisage", "version.py")
GIT_DIRECTORY = os.path.join(HERE, ".git")

# Template for the autogenerated version file.
VERSION_FILE_TEMPLATE = u'''\
# (C) Copyright 2007-2019 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only
# under the conditions described in the aforementioned license.  The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
# Thanks for using Enthought open source!

"""
Version information for this Envisage distribution.

This file is autogenerated by the Envisage setup.py script.
"""
from __future__ import unicode_literals

#: The full version of the package, including a development suffix
#: for unreleased versions of the package.
version = "{version}"

#: The Git revision from which this release was made.
git_revision = "{git_revision}"
'''

# Git executable to use to get revision information.
GIT = "git"


def _git_output(args):
    """
    Call Git with the given arguments and return the output as (Unicode) text.
    """
    return subprocess.check_output([GIT] + args).decode("utf-8")


def _git_info(commit="HEAD"):
    """
    Get information about the given commit from Git.

    Parameters
    ----------
    commit : str, optional
        Commit to provide information for. Defaults to "HEAD".

    Returns
    -------
    git_count : int
        Number of revisions from this commit to the initial commit.
    git_revision : unicode
        Commit hash for HEAD.

    Raises
    ------
    EnvironmentError
        If Git is not available.
    subprocess.CalledProcessError
        If Git is available, but the version command fails (most likely
        because there's no Git repository here).
    """
    count_args = ["rev-list", "--count", "--first-parent", commit]
    git_count = int(_git_output(count_args))

    revision_args = ["rev-list", "--max-count", "1", commit]
    git_revision = _git_output(revision_args).rstrip()

    return git_count, git_revision


def write_version_file(version, git_revision):
    """
    Write version information to the version file.

    Overwrites any existing version file.

    Parameters
    ----------
    version : unicode
        Package version.
    git_revision : unicode
        The full commit hash for the current Git revision.
    """
    with io.open(VERSION_FILE, "w", encoding="ascii") as version_file:
        version_file.write(
            VERSION_FILE_TEMPLATE.format(
                version=version, git_revision=git_revision
            )
        )


def read_version_file():
    """
    Read version information from the version file, if it exists.

    Returns
    -------
    version : unicode
        The full version, including any development suffix.
    git_revision : unicode
        The full commit hash for the current Git revision.

    Raises
    ------
    EnvironmentError
        If the version file does not exist.
    """
    version_info = runpy.run_path(VERSION_FILE)
    return (version_info["version"], version_info["git_revision"])


def git_version():
    """
    Construct version information from local variables and Git.

    Returns
    -------
    version : unicode
        Package version.
    git_revision : unicode
        The full commit hash for the current Git revision.

    Raises
    ------
    EnvironmentError
        If Git is not available.
    subprocess.CalledProcessError
        If Git is available, but the version command fails (most likely
        because there's no Git repository here).
    """
    git_count, git_revision = _git_info()
    version_template = RELEASED_VERSION if IS_RELEASED else UNRELEASED_VERSION
    version = version_template.format(
        major=MAJOR, minor=MINOR, micro=MICRO, dev=git_count
    )
    return version, git_revision


def archive_version():
    """
    Construct version information for an archive.

    Returns
    -------
    version : unicode
        Package version.
    git_revision : unicode
        The full commit hash for the current Git revision.

    Raises
    ------
    ValueError
        If this does not appear to be an archive.
    """
    if "$" in ARCHIVE_COMMIT_HASH:
        raise ValueError("This does not appear to be an archive.")

    version_template = RELEASED_VERSION if IS_RELEASED else UNRELEASED_VERSION
    version = version_template.format(
        major=MAJOR, minor=MINOR, micro=MICRO, dev="-unknown"
    )
    return version, ARCHIVE_COMMIT_HASH


def resolve_version():
    """
    Process version information and write a version file if necessary.

    Returns the current version information.

    Returns
    -------
    version : unicode
        Package version.
    git_revision : unicode
        The full commit hash for the current Git revision.
    """
    if os.path.isdir(GIT_DIRECTORY):
        # This is a local clone; compute version information and write
        # it to the version file, overwriting any existing information.
        version = git_version()
        print(u"Computed package version: {}".format(version))
        print(u"Writing version to version file {}.".format(VERSION_FILE))
        write_version_file(*version)
    elif "$" not in ARCHIVE_COMMIT_HASH:
        # This is a source archive.
        version = archive_version()
        print(u"Archive package version: {}".format(version))
        print(u"Writing version to version file {}.".format(VERSION_FILE))
        write_version_file(*version)
    elif os.path.isfile(VERSION_FILE):
        # This is a source distribution. Read the version information.
        print(u"Reading version file {}".format(VERSION_FILE))
        version = read_version_file()
        print(u"Package version from version file: {}".format(version))
    else:
        # This is a source archive for an unreleased version.
        raise RuntimeError(
            u"Unable to determine package version. No local Git clone "
            u"detected, and no version file found at {}."
            u"Please use a source dist or a git clone.".format(VERSION_FILE)
        )

    return version


def get_long_description():
    """ Read long description from README.txt. """
    with io.open("README.rst", "r", encoding="utf-8") as readme:
        return readme.read()


if __name__ == "__main__":
    version, git_revision = resolve_version()

    setup(
        name="envisage",
        version=version,
        url="http://docs.enthought.com/envisage",
        author="Enthought",
        author_email="info@enthought.com",
        maintainer="ETS Developers",
        maintainer_email="enthought-dev@enthought.com",
        download_url="https://github.com/enthought/envisage",
        classifiers=[
            c.strip()
            for c in """\
            Development Status :: 5 - Production/Stable
            Intended Audience :: Developers
            Intended Audience :: Science/Research
            License :: OSI Approved :: BSD License
            Operating System :: MacOS :: MacOS X
            Operating System :: Microsoft :: Windows
            Operating System :: POSIX :: Linux
            Programming Language :: Python
            Programming Language :: Python :: 2
            Programming Language :: Python :: 2.7
            Programming Language :: Python :: 3
            Programming Language :: Python :: 3.5
            Programming Language :: Python :: 3.6
            Programming Language :: Python :: 3.7
            Programming Language :: Python :: Implementation :: CPython
            Topic :: Scientific/Engineering
            Topic :: Software Development
            Topic :: Software Development :: Libraries
            Topic :: Software Development :: User Interfaces
            """.splitlines()
            if len(c.strip()) > 0
        ],
        description="Extensible application framework",
        long_description=get_long_description(),
        long_description_content_type="text/x-rst",
        entry_points={
            "envisage.plugins": [
                "envisage.core = envisage.core_plugin:CorePlugin"
            ]
        },
        install_requires=["apptools", "setuptools", "six", "traits"],
        license="BSD",
        packages=find_packages(),
        package_data={
            "": ["images/*", "*.ini"],
            "envisage.tests": [
                "bad_eggs/*.egg",
                "eggs/*.egg",
                "plugins/pear/*.py",
                "plugins/banana/*.py",
                "plugins/orange/*.py",
            ],
            "envisage.ui.single_project": ["*.txt"],
            "envisage.ui.tasks.tests": ["data/*.pkl"],
        },
        python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*",
        zip_safe=False,
    )
