#!/bin/sh

##
## Installs package dependencies and builds the application.
##

# Don't exit if some command fails.
set +e
# Disable file globbing.
set -f

# Boolean variables are true if non-empty and false otherwise.

# Command to install packages.
PKGCMD=
# Args to pass to $PKGCMD.
PKGARGS=
# Required packages.
PACKAGES=
# Whether package installation requires root permissions.
PKG_INSTALL_AS_ROOT=true
# Whether to skip package installation altogether.
PKG_INSTALL_SKIP=
# Whether to force package installation, even if it does not seem
# necessary.
PKG_INSTALL_FORCE=
# Only test if the OS is handled by this script.
DRY_RUN=
# If and where to install the program.
INSTALL_DIR=
# Whether we can install packages.
OS_IS_HANDLED=true
# Which OSs installer to use
OS=

## +-----------------------------------------------------------+
## * Utility Functions
## +-----------------------------------------------------------+

usage()
{
    cat <<EOF
usage:$(basename "$0") [--help|-n|-i DIR|[-d -D]|[--os OS]]

    -n       Don't do anything, but check if this OS is handled.

    -i DIR   Install the program in the given directory.

    -d       Force dependency installattion.

    -D       Skip dependency installattion.

    --os OS  Use the given OS's installer

    --help   Display this message.

EOF
    exit "$1"
}

# Search for command $1 in PATH. Print its absolute filename.
which()
{
    if [ -z "$1" ]; then
        return 1
    fi
    IFS=:
    for dir in $PATH; do
        if [ -x "$dir/$1" ]; then
            printf "%s" "$dir/$1"
            unset IFS
            return 0
        fi
    done
    unset IFS
    return 1
}

# Quote $@ for the shell.
quote()
{
    quoted=
    for arg; do
        qarg=$(printf "%s" "$arg" | sed -e 's/[|&;<>()$\`"'\'' 	]/\\&/g')
        if [ -z "$quoted" ]; then
            quoted=$qarg
        else
            quoted="$quoted $qarg"
        fi
    done
    printf "%s" "$quoted"
}

# Attempt to exec $@ as root.
exec_privileged() {
    if [ -z "$1" ]; then
        echo "internal error: command is empty"
        exit 2
    fi
    if [ -w / ]; then
        "$@"
    elif which sudo >/dev/null 2>&1; then
        sudo -- "$@"
        retval=$?
        sudo -k
        return $retval
    elif which su >/dev/null 2>&1; then
        su -c "$(quote "$@")"
    else
        echo "No such program: sudo or su"
        exit 1
    fi
}

# Test if $1 is in PATH or exit with a failure status.
assert_program()
{
    if ! which "$1" >/dev/null 2>&1; then
        echo "No such program: $1"
        exit 1
    fi
}

# Source filename $1 and echo variable $2.
source_var()
{
    if ! [ -f "$1" ] || ! [ -r "$1" ] || [ -z "$2" ]; then
        return 1
    fi
    # shellcheck source=/dev/null
    . "$1"
    eval "printf '%s\n' \$$2"
    return 0
}

exit_success()
{
    echo "==========================="
    echo "   Build succeeded. :O)    "
    echo "==========================="
    exit 0
}

exit_fail()
{
    echo "==========================="
    echo "     Build failed.  ;o(    "
    echo "==========================="
    if [ -z "$PKG_INSTALL_FORCE" ]; then
        echo "Note: maybe try the '-d' option."
    fi
    exit 1
}

# Return 0, if all required packages seem to be installed.
have_packages_installed()
{
    {
        which pkg-config || return 1
        if ! [ -f configure ];then
            which autoreconf || return 1
            which automake || return 1
        fi
        for lib in libpng glib-2.0 poppler poppler-glib zlib; do
            pkg-config --exists $lib || return 1
        done
        which make || return 1
        which gcc || which cc || return 1
        which g++ || which c++ || return 1
        cc $(pkg-config --cflags poppler) -o /dev/null -E - 2>/dev/null <<EOF
        #include <PDFDocEncoding.h>
EOF
        [ $? -eq 0 ] || return 1
        return 0
    } >/dev/null 2>&1
}

handle_options()
{
    while [ $# -gt 0 ]; do
        case $1 in
            --help) usage 0;;
            -n) DRY_RUN=true;;
            -d) PKG_INSTALL_FORCE=true ;;
            -D) PKG_INSTALL_SKIP=true ;;
            -i)
                shift
                [ $# -gt 0 ] || usage 1
                if [ "${1%%/}" != "${PWD%%/}" ]; then
                    INSTALL_DIR=$1
                fi ;;
            --os)
                shift
                [ $# -gt 0 ] || usage 1
                OS="$1"
                ;;
            *) usage 1 ;;
        esac
        shift
    done
    if [ -n "$PKG_INSTALL_SKIP" ] && [ -n "$PKG_INSTALL_FORCE" ]; then
        usage 1
    fi
}

## +-----------------------------------------------------------+
## * OS Functions
## +-----------------------------------------------------------+

# Archlinux
os_arch() {
    if ! [ -e "/etc/arch-release" ]; then
        return 1;
    fi
    PKGCMD=pacman
    PKGARGS="-S --needed"
    PACKAGES="base-devel libpng zlib poppler-glib"
    return 0;
}

# CentOS
os_centos() {
    if ! [ -e "/etc/centos-release" ]; then
        return 1
    fi
    PKGCMD=yum
    if yum help install-n >/dev/null 2>&1; then
        PKGARGS=install-n
    else
        PKGARGS=install
    fi
    PACKAGES="autoconf
              automake
              gcc
              gcc-c++
              libpng-devel
              make
              pkgconfig
              poppler-devel
              poppler-glib-devel
              zlib-devel"
    return 0
}

# FreeBSD
os_freebsd() {
    if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "FreeBSD" ]; then
        return 1
    fi
    PKGCMD=pkg
    PKGARGS=install
    PACKAGES="autotools poppler-glib png pkgconf"
    return 0
}

# OpenBSD
os_openbsd() {
    if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "OpenBSD" ]; then
        return 1
    fi
    PKGCMD=pkg_add
    PKGARGS="-uU"
    PACKAGES="autoconf-2.69p2 automake-1.15.1 poppler poppler-utils png"
    export AUTOCONF_VERSION=2.69
    export AUTOMAKE_VERSION=1.15
    if whereis clang++ ;then
        export CXX=clang++
    elif whereis eg++ ;then
        export CXX=eg++
    else
        export CXX=eg++
        PACKAGES="${PACKAGES} g++"
    fi
    export CXXFLAGS="-std=c++11 -I/usr/local/include/poppler -I/usr/local/include"
    return 0
}

# Fedora
os_fedora() {
    if ! [ -e "/etc/fedora-release" ]; then
        return 1
    fi
    PKGCMD=dnf
    PKGARGS=install
    PACKAGES="autoconf
              automake
              gcc
              gcc-c++
              libpng-devel
              make
              poppler-devel
              poppler-glib-devel
              zlib-devel"
    VERSION=$(source_var /etc/os-release VERSION_ID)
    if [ -n "$VERSION" ] && [ "$VERSION" -ge 26 ]; then
        PACKAGES="$PACKAGES pkgconf"
    else
        PACKAGES="$PACKAGES pkgconfig"
    fi
    return 0
}

# Debian/Ubuntu
os_debian() {
    if ! [ -e "/etc/debian_version" ]; then
        return 1
    fi
    PACKAGES="autoconf
              automake
              g++
              gcc
              libpng-dev
              libpoppler-dev
              libpoppler-glib-dev
              libpoppler-private-dev
              libz-dev
              make
              pkg-config"
    PKGCMD=apt-get
    PKGARGS=install
    return 0
}

# Msys2
os_msys2() {
    if [ -z "$MSYSTEM" ] || ! [ -r "/etc/profile" ]; then
        return 1
    fi
    case $MSYSTEM in
        MINGW64)
            PACKAGES="base-devel
                      mingw-w64-x86_64-libpng
                      mingw-w64-x86_64-poppler
                      mingw-w64-x86_64-toolchain
                      mingw-w64-x86_64-zlib" ;;
        MINGW32)
            PACKAGES="base-devel
                      mingw-w64-i686-libpng
                      mingw-w64-i686-poppler
                      mingw-w64-i686-toolchain
                      mingw-w64-i686-zlib" ;;
        MSYS)
            case $(uname -m) in
                x86_64)
                    MSYSTEM=MINGW64 ;;
                *)
                    MSYSTEM=MINGW32 ;;
            esac
            export MSYSTEM
            # shellcheck source=/dev/null
            . /etc/profile
            eval "exec $(quote "$0" "$@")" ;;
        *)
            echo "Unrecognized MSYSTEM value: $MSYSTEM"
            exit 1 ;;
    esac
    PKGCMD=pacman
    PKGARGS="-S --needed"
    PKG_INSTALL_AS_ROOT=
    return 0
}

# MacOS
os_macos() {
    if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "Darwin" ]; then
        return 1
    elif which brew >/dev/null 2>&1; then
        PKGCMD=brew
        PKGARGS=install
        PACKAGES="pkg-config poppler automake"
        PKG_INSTALL_AS_ROOT=
        # homebrew install libffi as keg-only, meaning we need to set
        # PKG_CONFIG_PATH manually so configure can find it
        export PKG_CONFIG_PATH="$(brew --prefix libffi)/lib/pkgconfig/"
    elif which port >/dev/null 2>&1; then
        PKGCMD=port
        PKGARGS=install
        PACKAGES="pkgconfig poppler automake libpng"
    else
        return 1
    fi
    return 0
}

# NixOS
os_nixos() {
    # Already in the nix-shell.
    if [ -n "$AUTOBUILD_NIX_SHELL" ]; then
        return 0
    fi
    if ! which nix-shell >/dev/null 2>&1; then
        return 1
    fi
    if [ -n "$DRY_RUN" ]; then
        return 0
    fi
    command="AUTOBUILD_NIX_SHELL=true"
    command="$command;export AUTOBUILD_NIX_SHELL"
    command="$command;$(quote "$0" "$@")"
    exec nix-shell --pure --command "$command" \
         -p gcc gnumake automake autoconf pkgconfig libpng zlib poppler
}

# Gentoo
os_gentoo() {
    if ! [ -e "/etc/gentoo-release" ]; then
        return 1
    fi
    PKGCMD=emerge
    PKGARGS=--noreplace
    PACKAGES="app-text/poppler
              dev-util/pkgconfig
              media-libs/libpng
              sys-devel/autoconf
              sys-devel/automake
              sys-devel/gcc
              sys-devel/make
              sys-libs/zlib"
    return 0
}

# By Parameter --os
os_argument() {
    [ -z "$OS" ] && return 1
    case $OS in
        macos)   os_macos   "$@";;
        freebsd) os_freebsd "$@";;
        arch)    os_arch    "$@";;
        centos)  os_centos  "$@";;
        openbsd) os_openbsd "$@";;
        fedora)  os_fedora  "$@";;
        debian)  os_debian  "$@";;
        gentoo)  os_gentoo  "$@";;
        msys2)   os_msys2   "$@";;
        nixos)   os_nixos   "$@";;
        *)       echo "Invalid --os argument: $OS"
                 exit 1
    esac || {
        echo "Unable to install on this system as $OS"
        exit 1
    }
}

## +-----------------------------------------------------------+
## * Figure out were we are, install deps and build the program
## +-----------------------------------------------------------+

handle_options "$@"

os_argument "$@" || \
os_macos    "$@" || \
os_freebsd  "$@" || \
os_arch     "$@" || \
os_centos   "$@" || \
os_openbsd  "$@" || \
os_fedora   "$@" || \
os_debian   "$@" || \
os_gentoo   "$@" || \
os_msys2    "$@" || \
os_nixos    "$@" || \
{
    OS_IS_HANDLED=
    if [ -z "$DRY_RUN" ]; then
        echo "Failed to recognize this system, trying to continue."
    fi
}

if [ -n "$DRY_RUN" ]; then
    [ -n "$OS_IS_HANDLED" ]
    exit $?
fi

if [ -n "$PKGCMD" ];then
    echo "---------------------------"
    echo "    Installing packages    "
    echo "---------------------------"
    if [ -n "$PKG_INSTALL_SKIP" ]; then
        echo "Skipping package installation (as requested)"
    elif [ -z "$PKG_INSTALL_FORCE" ] && have_packages_installed; then
        echo "Skipping package installation (already installed)"
    else
        assert_program "$PKGCMD"
        echo "$PKGCMD $PKGARGS $PACKAGES"
        if [ -n "$PKG_INSTALL_AS_ROOT" ]; then
            exec_privileged $PKGCMD $PKGARGS $PACKAGES
        else
            $PKGCMD $PKGARGS $PACKAGES
        fi
    fi
    echo
fi

echo "---------------------------"
echo " Configuring and compiling "
echo "---------------------------"

# Try to be in the correct directory.
if which dirname >/dev/null 2>&1; then
    cd "$(dirname "$0")" || {
        echo "Failed to change into the source directory"
        exit 1
    }
fi

# Create the configure script.
if ! [ -f ./configure ]; then
    assert_program autoreconf
    echo "autoreconf -i"
    autoreconf -i
    [ -f ./configure ] || exit_fail
fi

# Build the program.
if [ -n "$INSTALL_DIR" ]; then
    prefix=--bindir=$INSTALL_DIR
fi

echo "./configure -q $prefix && make -s"
eval "./configure -q $(quote "$prefix") && make -s || exit_fail"
echo
if [ -n "$INSTALL_DIR" ]; then
    echo "---------------------------"
    echo "       Installing          "
    echo "---------------------------"
    echo make -s install
    if mkdir -p -- "$INSTALL_DIR" && [ -w "$INSTALL_DIR" ]; then
        make install || exit_fail
    else
        exec_privileged make install || exit_fail
    fi
    # Copy dynamic libraries on windows.
    if [ -f epdfinfo.exe ]; then
        assert_program awk
        assert_program ldd
        for dll in $(ldd epdfinfo.exe | awk '/\/mingw/ {print $3}'); do
            cp -u "$dll" "$INSTALL_DIR"
        done
    fi
    echo
fi
exit_success

# Local Variables:
# compile-command: "shellcheck autobuild"
# End:
