#!/usr/bin/env 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
    command -v "$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
        [ $? -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
              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
}

# NetBSD
os_netbsd() {
    if ! which uname >/dev/null 2>&1 || [ "$(uname -s)" != "NetBSD" ]; then
        return 1
    fi
    PKGCMD=pkgin
    PKGARGS=install
    PACKAGES="autoconf automake 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.69 automake%1.15 poppler poppler-utils png"
    export AUTOCONF_VERSION=2.69
    export AUTOMAKE_VERSION=1.15
    return 0
}

# Fedora
os_fedora() {
    if ! [ -e "/etc/fedora-release" ]; then
        return 1
    fi
    PKGCMD=dnf
    PKGARGS=install
    PACKAGES="autoconf
              automake
              gcc
              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
              gcc
              libpng-dev
              libpoppler-dev
              libpoppler-glib-dev
              libz-dev
              make
              pkg-config"
    PKGCMD=apt-get
    PKGARGS="install -y"
    return 0
}

# Msys2
os_msys2() {
    if [ -z "$MSYSTEM" ] || ! [ -r "/etc/profile" ]; then
        return 1
    fi
    case $MSYSTEM in
        MINGW64)
            PACKAGES="base-devel
                      autoconf
                      automake
                      mingw-w64-x86_64-libpng
                      mingw-w64-x86_64-poppler
                      mingw-w64-x86_64-imagemagick
                      mingw-w64-x86_64-toolchain
                      mingw-w64-x86_64-openssl
                      mingw-w64-x86_64-zlib" ;;
        MINGW32)
            PACKAGES="base-devel
                      autoconf
                      automake
                      mingw-w64-i686-libpng
                      mingw-w64-i686-poppler
                      mingw-w64-i686-imagemagick
                      mingw-w64-i686-toolchain
                      mingw-w64-i686-openssl
                      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 autoconf automake"
        PKG_INSTALL_AS_ROOT=
        # brew installs libffi as keg-only, meaning we need to set
        # PKG_CONFIG_PATH manually so configure can find it
        export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:$(brew --prefix libffi)/lib/pkgconfig/:$(brew --prefix zlib)/lib/pkgconfig/"
    elif which port >/dev/null 2>&1; then
        PKGCMD=port
        PKGARGS=install
        PACKAGES="pkgconfig poppler autoconf 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
    if ! nix-instantiate --eval -E "<nixpkgs>" &>/dev/null; then
      echo "File 'nixpkgs' was not found in the Nix path. Using NixOS/nixpkgs"
      NIX_PATH="nixpkgs=https://github.com/nixos/nixpkgs/archive/master.tar.gz:$NIX_PATH"
    fi
    command="AUTOBUILD_NIX_SHELL=true"
    command="$command;export AUTOBUILD_NIX_SHELL"
    command="$command;$(quote "$0" "$@")"
    exec nix-shell --pure --run "$command" \
         -p automake autoconf pkg-config 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/pkgconf
              media-libs/libpng
              sys-devel/autoconf
              sys-devel/automake
              sys-devel/gcc
              sys-devel/make
              sys-libs/glibc
              sys-libs/zlib"
    return 0
}

# Void
os_void() {
    if [ -f "/etc/os-release" ]; then
        . /etc/os-release
        if [ "$ID" != "void" ]; then
          return 1
        fi
    else
      return 1
    fi
    PACKAGES="autoconf
              automake
              libpng-devel
              poppler-devel
              poppler-glib-devel
              zlib-devel
              make
              pkgconf
              cairo-devel"
    PKGCMD=xbps-install
    PKGARGS="-Sy"
    export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:/usr/lib/pkgconfig"
    return 0
}

# openSUSE (TODO: add support for micro versions)
os_opensuse() {
    if [ -f "/etc/os-release" ]; then
        . /etc/os-release
        if [ "$ID" != "opensuse-leap" ] && [ "$ID" != "opensuse-tumbleweed" ]; then
          return 1
        fi
    else
      return 1
    fi
    PACKAGES="make
              automake
              autoconf
              gcc
              libpng16-devel
              libpng16-compat-devel
              zlib-devel
              libpoppler-devel
              libpoppler-glib-devel
              glib2-devel
              pkgconf"
    PKGCMD=zypper
    PKGARGS="install"
    return 0
}

# Alpine Linux
os_alpine() {
    if [ -f "/etc/os-release" ]; then
        . /etc/os-release
        if [ "$ID" != "alpine" ]; then
          return 1
        fi
    else
      return 1
    fi
    PACKAGES="autoconf
              automake
              libpng-dev
              poppler-dev
              glib-dev
              gcc
              build-base"
    PKGCMD=apk
    PKGARGS="add"
    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  "$@";;
        netbsd)  os_netbsd  "$@";;
        openbsd) os_openbsd "$@";;
        fedora)  os_fedora  "$@";;
        debian)  os_debian  "$@";;
        gentoo)  os_gentoo  "$@";;
        msys2)   os_msys2   "$@";;
        nixos)   os_nixos   "$@";;
        void)    os_void    "$@";;
        opensuse) os_opensuse "$@";;
        alpine)  os_alpine  "$@";;
        *)       echo "Invalid --os argument: $OS"
                 exit 1
    esac || {
        echo "Unable to install on this system as $OS"
        exit 1
    }
}

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

handle_options "$@"

os_argument "$@" || \
os_macos    "$@" || \
os_freebsd  "$@" || \
os_arch     "$@" || \
os_centos   "$@" || \
os_netbsd   "$@" || \
os_openbsd  "$@" || \
os_fedora   "$@" || \
os_debian   "$@" || \
os_gentoo   "$@" || \
os_msys2    "$@" || \
os_nixos    "$@" || \
os_void     "$@" || \
os_opensuse "$@" || \
os_alpine   "$@" || \
{
    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

# 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

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

# 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 clean && make -s"
eval "./configure -q $(quote "$prefix") && make clean && 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:
