#!/usr/bin/perl -w
#
# Regina - A Normal Surface Theory Calculator
# Python Startup Script
#
# Copyright (c) 2002-2009, Ben Burton
# For further details contact Ben Burton (bab@debian.org).
#
# This script simply starts the Python interpreter and imports the Regina
# calculation engine, i.e., the module 'regina'.
#
# DO NOT EDIT THIS FILE DIRECTLY.  It has been automatically generated
# by configure and any changes will be overwritten.  Try editing
# regina-python.in instead.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
# MA 02110-1301, USA.

use strict;
use File::Basename;

# --- Constants. ---

# The program name.
my $prog_name = $0;

# Address to which questions should be emailed.
my $regina_support = "regina-user\@lists.sourceforge.net";

# Default verbosity level.
my $default_verbosity = 1;

# Local configuration files.
my $home = $ENV{HOME};
if (! $home) {
    $home = `echo ~`;
}
my $python_conf = $home . "/.regina-python";
my $libs_conf = $home . "/.regina-libs";

# --- Variables. ---

my $quiet = 0;
my $verbose = 0;
my $interactive = 0;
my $nolibs = 0;
my $regina_home = '';
my $runscript_py = '';
my $from_source = 0;
my $python_lib_dir = '';
my $python_cmd = '';
my @python_libs;
my $script = '';
my @script_args;

# --- Extract variables from the configuration file.

if (open(CONF, $python_conf)) {
    my @lines = <CONF>;
    chomp @lines;
    close(CONF);

    foreach my $line (@lines) {
        if ($line =~ /^\s*$/ or $line =~ /^\s*#/) {
            next;
        }
        if ($line =~ /^\s*([A-Za-z_]+)\s*=\s*$/) {
            &warn("W: Variable $1 is not assigned a value in $python_conf.\n");
            next;
        }
        if ($line =~ /^\s*([A-Za-z_]+)\s*=\s*(\S(.*\S)?)\s*$/) {
            # We have a configuration variable.
            if ($1 eq 'REGINA_VERBOSITY') {
                &setVerbosity($2);
            } elsif ($1 eq 'REGINA_PYTHON') {
                &setPythonCmd($2);
            } elsif ($1 eq 'REGINA_PYLIBDIR') {
                &setPythonLibDir($2);
            } elsif ($1 eq 'REGINA_HOME') {
                &setReginaHome($2);
            } else {
                &warn("W: Unknown variable $1 in $python_conf.\n");
            }
            next;
        }
        &warn("W: Bad line in $python_conf:\n$line\n");
    }
}

# --- Extract variables from the environment. ---

if ($ENV{REGINA_VERBOSITY}) {
    &setVerbosity($ENV{REGINA_VERBOSITY});
}
if ($ENV{REGINA_HOME}) {
    &setReginaHome($ENV{REGINA_HOME});
}
if ($ENV{REGINA_PYTHON}) {
    &setPythonCmd($ENV{REGINA_PYTHON});
}
if ($ENV{REGINA_PYLIBDIR}) {
    &setPythonLibDir($ENV{REGINA_PYLIBDIR});
}

# --- Parse the command-line options. ---

my $readingScriptArgs = 0;
foreach my $arg (@ARGV) {
    if ($readingScriptArgs) {
        push @script_args, $arg;
        next;
    }

    if ($arg eq '-q' or $arg eq '--quiet') {
        $quiet = 1;
        $verbose = 0;
        next;
    }

    if ($arg eq '-v' or $arg eq '--verbose') {
        $quiet = 0;
        $verbose = 1;
        next;
    }

    if ($arg eq '-i' or $arg eq '--interactive') {
        $interactive = 1;
        next;
    }

    if ($arg eq '-n' or $arg eq '--nolibs') {
        $nolibs = 1;
        next;
    }

    if ($arg =~ /^-/) {
        &usage("Unrecognised option: $arg");
    }

    # We must be onto the script.
    $script = $arg;
    $readingScriptArgs = 1;
}

if ($interactive and not $script) {
    &usage("The interactive option (-i, --interactive) can only be used with a script.");
}

# --- Check that the current options are reasonable. ---

my $prefix = "/usr";
my $exec_prefix = "${prefix}";
my $datadir = "${prefix}/share";
my $libdir = "${exec_prefix}/lib";
my $package = "regina-normal";

if (! $regina_home) {
    my $prog_dir = dirname($prog_name);
    if ( -f "$prog_dir/regina.la" and -f "$prog_dir/../config.log") {
        &info("I: Running from the source tree.\n");
        $from_source = 1;

        # Setting $regina_home is interesting, because we want things
        # from both the builddir and the srcdir.  Use both.
        my $builddir = "$prog_dir/..";
        my $srcdir = "..";
        $srcdir =~ /^\// or $srcdir = "$prog_dir/$srcdir";

        &setReginaHome($builddir, $srcdir);
    } else {
        &info("I: Running from an installation.\n");
        &setReginaHome("$datadir/$package");
    }
}

if (! $python_cmd) {
    my $which_python;
    chomp($which_python = "/usr/bin/python2.6");
    if (! $which_python) {
        &err("E: A Python interpreter could not be found.\n");
        &err("E: Set \$REGINA_PYTHON to a Python interpreter (such as /usr/bin/python)\n");
        &err("E: and try again.\n");
        exit 1;
    }
    &setPythonCmd($which_python);
}

if (! $python_lib_dir) {
    &setPythonLibDir($from_source ? "$regina_home/python/.libs" :
        "$libdir/$package/python");
}

# --- Find the default charset to use for filename encodings. ---

my $codeset = undef;
eval {
    require I18N::Langinfo;
    $codeset = I18N::Langinfo::langinfo(I18N::Langinfo::CODESET());
};
if (not defined $codeset) {
    &warn("W: Could not determine default character encoding for filenames.\n");
    &warn("W: Try setting \$LANG if this is causing problems.\n");
}

# --- Read the list of libraries to load. ---

if ($nolibs) {
    &info("I: Not loading user libraries, as requested on the command-line.\n");
} elsif (open(LIBS, $libs_conf)) {
    my @lines = <LIBS>;
    chomp @lines;
    close(LIBS);

    my $lib;
    foreach my $line (@lines) {
        if ($line =~ /^\s*#/) {
            next;
        }
        if ($line =~ /^\s*(\S(.*\S)?)\s*$/) {
            # Treat this as a library file.
            $lib = $1;

            # Libraries are stored in UTF-8; convert to a local encoding
            # if we know how.
            if (defined $codeset) {
                if (not defined eval {
                    require Encode;
                    Encode::from_to($lib = $1, 'utf8', $codeset);
                }) {
                    &warn("W: Could not convert filename $1 from UTF-8 to $codeset.\n");
                    $lib = $1;
                }
            }
            if (-e $lib) {
                &info("I: Adding library $lib.\n");
                push @python_libs, $lib;
            } else {
                &err("E: Python library $lib does not exist (found in $libs_conf).\n");
            }
        }
    }
}

# --- Go ahead and run the application. ---

&extendEnvVar('PYTHONPATH', $python_lib_dir);

if ($from_source) {
    # Since we're running from the source tree, we need to ensure that
    # the calculation engine library can be found.
    &extendEnvVar('LD_LIBRARY_PATH', "$regina_home/engine/.libs");

    # MacOS users will need DYLD_LIBRARY_PATH also.
    &extendEnvVar('DYLD_LIBRARY_PATH', "$regina_home/engine/.libs");
}

my @fullCommandLine = ( $python_cmd );

if ($script) {
    if (! -e $script) {
        &usage("E: The script $script could not be found.");
    }
    my @fullCommandLine = ( $python_cmd );
    $interactive and push @fullCommandLine, '-i';
    push @fullCommandLine, $runscript_py;
    push @fullCommandLine, @python_libs;
    push @fullCommandLine, ( '--', $script );
    push @fullCommandLine, @script_args;
    exec @fullCommandLine or &execError();
} else {
    my @fullCommandLine = ( $python_cmd, '-i', $runscript_py );
    push @fullCommandLine, @python_libs;
    exec @fullCommandLine or &execError();
}

# --- Helper routines. ---

# Set various configuration options.
#
sub setVerbosity() {
    &info("I: Setting verbosity to $_[0].\n");
    if ($_[0] eq '0') {
        $quiet = 1;
        $verbose = 0;
    } elsif ($_[0] eq '1') {
        $quiet = 0;
        $verbose = 0;
    } elsif ($_[0] eq '2') {
        $quiet = 0;
        $verbose = 1;
    } else {
        &err("E: The verbosity level $_[0] is not recognised.\n");
        &err("E: This should be 0 (errors), 1 (errors/warnings) or 2 (everything).\n");
    }
}
sub setPythonCmd() {
    &info("I: Setting python command to $_[0].\n");
    $python_cmd = $_[0];
}
sub setPythonLibDir() {
    &info("I: Setting python module directory to $_[0].\n");
    if ( ! -e "$_[0]/regina.so") {
        &err("E: The calculation engine module 'regina.so' could not be \n");
        &err("   found in the directory $_[0].\n");
        &err("E: Please set \$REGINA_PYLIBDIR to the directory containing\n");
        &err("   the module 'regina.so' and try again.\n");
        exit 1;
    }
    $python_lib_dir = $_[0];
}
sub setReginaHome() {
    # Set both $regina_home and $runscript_py.
    if (defined $_[1]) {
        # Interpret the two arguments as a "binary" home and a "source" home.
        # This makes sense when running out of the source tree.
        &info("I: Setting Regina home directories to ($_[0], $_[1]).\n");
        $regina_home = $_[0];
        $runscript_py = "$_[1]/scripts/runscript.py";
    } else {
        # Interpret the single argument as a traditional REGINA_HOME.
        # This makes sense when running out of a standard installation.
        &info("I: Setting Regina home directory to $_[0].\n");
        $regina_home = $_[0];
        $runscript_py = "$_[0]/scripts/runscript.py";
    }

    # Sanity test.
    if ( ! -e $runscript_py) {
        &err("E: \$REGINA_HOME should be set to the Regina base directory.\n");
        &err("   This is the directory containing the 'scripts' subdirectory,\n");
        &err("   the 'icons' subdirectory and so on.\n");
        &err("E: It is currently set to $regina_home .\n");
        exit 1;
    }
}
sub extendEnvVar() {
    &info("I: Appending $_[1] to \$$_[0].\n");
    if ($ENV{$_[0]}) {
        $ENV{$_[0]} = "$_[1]:$ENV{$_[0]}";
    } else {
        $ENV{$_[0]} = $_[1];
    }
}

# Write the given message to stderr if verbose mode is on.
#
sub info {
    if ($verbose) {
        print STDERR $_[0];
    }
}
# Write the given message to stderr if quiet mode is off.
#
sub warn {
    if (! $quiet) {
        print STDERR $_[0];
    }
}

# Write the given message to stderr.
#
sub err {
    print STDERR $_[0];
}

# Display usage information and exit.
#
sub usage {
    my $msg = shift;
    defined($msg) and print STDERR "$msg\n\n";
    print STDERR "Usage: 1. $prog_name [ -q, --quiet | -v, --verbose ]\n";
    print STDERR "       2. $prog_name [ -q, --quiet | -v, --verbose ]\n";
    print STDERR "               [ -i, --interactive ] script [ script_args ... ]\n";
    exit 1;
}

# The python interpreter could not be started.
#
sub execError() {
    &err("E: An error occurred whilst trying to start the Python interpreter.\n");
    &err("E: Run '$prog_name --verbose' to see more detailed diagnostic output,\n");
    &err("   including the runtime options being used.\n");
    &err("E: If you still cannot resolve the problem, please mail\n");
    &err("   $regina_support for assistance.\n");
}

