#!/usr/bin/env python
"""Bootstrap virutalenv installation

If you want to use virutalenv in your bootstap script, just include this
file in the same directory with it, and add this to the top::

    from ve_setup import use_virtualenv
    use_virtualenv()

If you want to require a specific version of virtualenv, set a download
mirror, or use an alternate installation directory, you can do so by supplying
the appropriate options to ``use_virtualenv()``.
"""

import os
import shutil
import subprocess
import sys
import tempfile
import urllib


# Defaults
VIRTUALENV_VERSION = '1.7.1.2'
VIRTUALENV_ARGS = ['python']

#
# Working around error with PYTHONHOME
#
if 'PYTHONHOME' in os.environ:
    del os.environ['PYTHONHOME']
    print "WARNING: ignoring the value of the PYTHONHOME environment " \
          " variable! This value can corrupt the virtual python installation."

def use_virtualenv(argv, version=VIRTUALENV_VERSION, activate=True,
        requirements=None):
    """Install and use virtualenv environment."""

    virtualenv = VirtualEnv(argv, version=version)
    if not virtualenv.is_exists:
        virtualenv.create()
        if requirements:
            virtualenv.install_requirements(requirements)
    if activate:
        virtualenv.activate()
    return virtualenv

def log(message):
    """Log message"""

    sys.stderr.write(": %s\n" % message)


class VirtualEnv(object):
    """Virtual environment"""

    def __init__(self, argv, version=None):
        self.python_name = os.path.basename(sys.executable)
        self.version = version or VIRTUALENV_VERSION
        self.path = os.path.abspath(argv[-1])
        self.argv = argv

    @property
    def scripts_dir(self):
        """Return path where scripts directory is located."""

        return os.path.join(self.path, 'Scripts' if sys.platform == 'win32'
                else 'bin')

    @property
    def is_exists(self):
        """Check this environment is installed."""

        return os.path.isfile(os.path.join(self.scripts_dir, self.python_name))
    
    @property
    def is_activated(self):
        """Check this environment is activated."""

        return 'VIRTUAL_ENV' in os.environ and \
                os.environ['VIRTUAL_ENV'] == self.path

    def create(self):
        """Create this environment."""

        tmpdir = tempfile.mkdtemp()
        virtualenv_requirement = 'virtualenv==%s' % self.version
        try:
            log("using virtualenv version %s" % self.version)
            installer = EZSetupInstaller(tmpdir)
            installer.install(virtualenv_requirement)
            virtualenv_py = os.path.join(tmpdir, 'virtualenv', 'virtualenv.py')
            virtualenv_cmd = [sys.executable, virtualenv_py] + self.argv
            log("execute %s" % " ".join(virtualenv_cmd))
            subprocess.call(virtualenv_cmd)
        finally:
            shutil.rmtree(tmpdir)

    def activate(self):
        """Activate this environment."""

        if self.is_activated:
            return # this environment is activated
        activate_this = os.path.join(self.scripts_dir, 'activate_this.py')
        execfile(activate_this, dict(__file__=activate_this))
        os.environ['VIRTUAL_ENV'] = self.path
        if not self.scripts_dir in os.getenv('PATH', ''):
            os.environ['PATH'] = os.pathsep.join(
                    [self.scripts_dir, os.getenv('PATH', '')])

    def install_requirements(self, requirements, extra_pip_args=None):
        """Install requirements from the `requirements` file."""

        pip_exe = os.path.join(self.scripts_dir, "pip")
        cmd = [pip_exe, "install", "-r", requirements] + (extra_pip_args or [])
        out = subprocess.Popen(cmd).communicate()


class EZSetupInstaller(object):
    """Installer"""

    EZ_SETUP_PY = 'ez_setup.py'
    EZ_SETUP_URL = 'http://peak.telecommunity.com/dist/ez_setup.py'

    def __init__(self, install_dir, ez_setup_py=None):
        self.install_dir = install_dir
        self.ez_setup_py = ez_setup_py or (
                os.path.join(os.getcwd(), self.EZ_SETUP_PY) if os.path.isfile(
                    os.path.join(os.getcwd(), self.EZ_SETUP_PY)) else
                os.path.join(self.install_dir, self.EZ_SETUP_PY))
        self._fetch_ez_setup_py()

    def install(self, requirement):
        """Install given requirement."""

        env = os.environ.copy()
        env['PYTHONPATH'] = self.install_dir
        ez_setup_cmd = [sys.executable, self.ez_setup_py,
                '-q', '--upgrade', '--editable', '--build-directory',  self.install_dir,
                requirement]
        log("download %s with %s" % (requirement, " ".join(ez_setup_cmd)))
        subprocess.call(ez_setup_cmd, env=env)

    def _fetch_ez_setup_py(self):
        """Fetch ez_setup.py."""

        if not os.path.isfile(self.ez_setup_py):
            ez_setup_url = self.EZ_SETUP_URL
            log("download %s to %s" % (ez_setup_url, self.ez_setup_py))
            urllib.urlretrieve(ez_setup_url, self.ez_setup_py)


if __name__ == '__main__':
    import traceback

    def main():
        """Main function."""

        version = VIRTUALENV_VERSION
        args = []
        if len(sys.argv) > 1:
            args = sys.argv[1:]
            if '--venv_version' == args[0]:
                version = args[1]
                args = args[2:]
        if len(args) == 0:
            args = VIRTUALENV_ARGS
        use_virtualenv(args, version)

    try:
        main()
        sys.exit(0)
    except Exception, e: # Catch all exceptions. pylint: disable=W0703
        sys.stderr.write(traceback.format_exc() if __debug__ else str(e))
        sys.exit(1)

