#!/usr/bin/env python
"""
install.py

General installation script for the segtools package. Interacts with
the user to configure the environment to download and install all
dependencies and the segtools package.

This script is designed to be downloaded and run independently, and
will guide the process of downloading and installing all other source
code.

(c) 2009: Orion Buske <stasis {at} uw {dot} edu>

"""
PKG_VERSION = "1.1.8"

${COMMON_CODE_HEADER}

import re

MIN_R_VERSION = "2.10.0"
MIN_RPY2_VERSION = "2.1.3"

R_URL = "http://cran.r-project.org/src/base/R-2/R-2.11.1.tar.gz"

# List of R package pre-requisites
R_PACKAGES = ["latticeExtra", "reshape"]

# One command per line
R_INSTALL_SCRIPT = """
cd $tmpdir
wget $url -O $file
if [ ! -d $filebase ]; then tar -xzf $file; fi
cd $filebase
./configure --prefix=$dir --enable-R-shlib
make
make install
"""

${COMMON_CODE_BODY}


class RInstaller(ScriptInstaller):
    name = "R"
    min_version = MIN_R_VERSION
    url = R_URL
    install_script = R_INSTALL_SCRIPT

    def __init__(self, env):
        self.env = env
        super(self.__class__, self).__init__()

    def get_version(self):
        """Returns R version as a string or None if not found or installed."""
        try:
            cmd = Popen(["which", "R"], stdout=PIPE, stderr=PIPE)
            resp = cmd.stdout.readlines()[0].strip()
            if resp:
                r_cmd = resp
            else:
                r_cmd = "R"

            cmd = Popen([r_cmd, "--version"], stdout=PIPE, stderr=PIPE)
            resp = cmd.stdout.readlines()[0].strip()
            matched = re.search("R version ([.0-9a-zA-Z]*) .*", resp)
            if matched:
                # R Version found. Check compilation configuration
                cmd = Popen([r_cmd, "CMD", "config", "--cppflags"],
                            stdout=PIPE, stderr=PIPE)
                resp = cmd.stdout.readlines()[0].strip()
                if resp == "R was not built as a library":
                    print >>sys.stderr, ("""\
You already have R installed as a program, but in order to work with
Segtools, it must be installed as a library."""),
                    return None
                else:
                    return matched.group(1)
            else:
                return None
        except (OSError, IndexError):
            return None

    def install(self):
        """Install R to arch_home.

        Set R_PROFILE_USER to /dev/null for the installation,
        else, ~/.Rprofile code might crash, causing installation to fail/

        """
        if "R_PROFILE_USER" in os.environ:
            old = os.environ["R_PROFILE_USER"]
            os.environ["R_PROFILE_USER"] = "/dev/null"
        else:
            old = None

        try:
            self.script_install(dir=self.env.arch_home)
        finally:
            if old is not None:
                os.environ["R_PROFILE_USER"] = old

    def cleanup(self, success):
        if success:
            print >>sys.stderr, ("\nRPy2 uses R_HOME to locate R")
            r_home = self.env.arch_home
            self.env.shell.save_var("R_HOME", r_home)
            lib_path = os.path.join(r_home, "lib")
            self.env.shell.save_to_var("LD_LIBRARY_PATH", lib_path)
        else:
            die("Error installing R")

        super(self.__class__, self).cleanup(success)

class RlibsInstaller(Installer):
    name = "R libraries"
    install_prompt = "\nMay I download and install the necessary %s?"

    def __init__(self, packages=R_PACKAGES):
        self.packages = packages
        super(self.__class__, self).__init__()

    def is_package_installed(self, package):
        try:
            from rpy2.robjects.packages import importr
            from rpy2.rinterface import (consolePrint, set_writeconsole,
                                         RRuntimeError)
        except ImportError:
            raise InstallationError("RPy2 required to install R libs"
                                    " (or tell if they are installed)")

        try:
            # Mask R from printing to console
            def consoleMask(msg):
                pass  # Do nothing with message (don't print)

            set_writeconsole(consoleMask)
            try:
                importr(package)
            except RRuntimeError:
                # Import fails if package is not installed
                return False
        finally:
            set_writeconsole(consolePrint)  # Restore R printing

        return True

    def get_version(self):
        print >>sys.stderr, ""  # Skip line for prints
        all_found = True
        for package in self.packages:
            if self.is_package_installed(package):
                print >>sys.stderr, "\tFound R package: %s" % package
            else:
                print >>sys.stderr, "\tMissing R package: %s" % package
                all_found = False

        return all_found

    def install_package(self, package):
        """Install R package using CRAN.

        Package only installed if not installed already.
        Temporary unsets DISPLAY to try to not open X-window for ssh-ers.

        """
        if self.is_package_installed(package):
            return

        if "DISPLAY" in os.environ:
            old = os.environ["DISPLAY"]
            del os.environ["DISPLAY"]
        else:
            old = None
        try:
            try:
                from rpy2.robjects import r, numpy2ri, StrVector
                # numpy2ri imported for side-effects
            except ImportError, e:
                raise InstallationError("Could not import RPy2 (%s)" % e)

            r["install.packages"](package,
                                  dep=StrVector(["Depends", "Imports"]))
        finally:
            if old is not None:
                os.environ["DISPLAY"] = old

    def install(self):
        """Install all R packages, one at a time"""
        for package in self.packages:
            print "Installing package: %s" % package
            self.install_package(package)

class Rpy2Installer(EasyInstaller):
    name = "RPy2"
    min_version = MIN_RPY2_VERSION
    get_version = EasyInstaller.get_egg_version

class SegtoolsInstaller(EasyInstaller):
    name = "Segtools"
    min_version = PKG_VERSION
    get_version = EasyInstaller.get_egg_version

############################## MAIN #########################
def main(args=sys.argv[1:]):
    env = Environment()
    env.initialize()

    installers = [SetuptoolsInstaller(),
                  Hdf5Installer(env),
                  NumpyInstaller(),
                  RInstaller(env),
                  Rpy2Installer(),
                  RlibsInstaller(),
                  PytablesInstaller(),
                  SegtoolsInstaller()]

    for installer in installers:
        installer.run()
        # Next step may need just-installed eggs, so update site list
        env.refresh_packages()

    # DONE: Test package installations?
    TestSuite().run([PytablesTester()])

    env.close()

if __name__ == "__main__":
    sys.exit(main())
