#!/bin/bash

# Check if apt is free to use, or wait, until timeout.
#
# [internal]
#
# usage: ynh_wait_dpkg_free
#
# Requires YunoHost version 3.3.1 or higher.
ynh_wait_dpkg_free() {
    local try
    # With seq 1 17, timeout will be almost 30 minutes
    for try in `seq 1 17`
    do
        # Check if /var/lib/dpkg/lock is used by another process
        if sudo lsof /var/lib/dpkg/lock > /dev/null
        then
            echo "apt is already in use..."
            # Sleep an exponential time at each round
            sleep $(( try * try ))
        else
            # Check if dpkg hasn't been interrupted and is fully available.
            # See this for more information: https://sources.debian.org/src/apt/1.4.9/apt-pkg/deb/debsystem.cc/#L141-L174
            local dpkg_dir="/var/lib/dpkg/updates/"

            # For each file in $dpkg_dir
            while read dpkg_file <&9
            do
                # Check if the name of this file contains only numbers.
                if echo "$dpkg_file" | grep -Pq "^[[:digit:]]+$"
                then
                    # If so, that a remaining of dpkg.
                    ynh_print_err "E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem."
                    return 1
                fi
            done 9<<< "$(ls -1 $dpkg_dir)"
            return 0
        fi
    done
    echo "apt still used, but timeout reached !"
}

# Check either a package is installed or not
#
# example: ynh_package_is_installed --package=yunohost && echo "ok"
#
# usage: ynh_package_is_installed --package=name
# | arg: -p, --package - the package name to check
#
# Requires YunoHost version 2.2.4 or higher.
ynh_package_is_installed() {
    # Declare an array to define the options of this helper.
    local legacy_args=p
    declare -Ar args_array=( [p]=package= )
    local package
    # Manage arguments with getopts
    ynh_handle_getopts_args "$@"

    ynh_wait_dpkg_free
    dpkg-query -W -f '${Status}' "$package" 2>/dev/null \
        | grep -c "ok installed" &>/dev/null
}

# Get the version of an installed package
#
# example: version=$(ynh_package_version --package=yunohost)
#
# usage: ynh_package_version --package=name
# | arg: -p, --package - the package name to get version
# | ret: the version or an empty string
#
# Requires YunoHost version 2.2.4 or higher.
ynh_package_version() {
    # Declare an array to define the options of this helper.
    local legacy_args=p
    declare -Ar args_array=( [p]=package= )
    local package
    # Manage arguments with getopts
    ynh_handle_getopts_args "$@"

    if ynh_package_is_installed "$package"; then
        dpkg-query -W -f '${Version}' "$package" 2>/dev/null
    else
        echo ''
    fi
}

# APT wrapper for non-interactive operation
#
# [internal]
#
# usage: ynh_apt update
#
# Requires YunoHost version 2.4.0.3 or higher.
ynh_apt() {
    ynh_wait_dpkg_free
    DEBIAN_FRONTEND=noninteractive apt-get -y $@
}

# Update package index files
#
# usage: ynh_package_update
#
# Requires YunoHost version 2.2.4 or higher.
ynh_package_update() {
    ynh_apt update
}

# Install package(s)
#
# usage: ynh_package_install name [name [...]]
# | arg: name - the package name to install
#
# Requires YunoHost version 2.2.4 or higher.
ynh_package_install() {
    ynh_apt --no-remove -o Dpkg::Options::=--force-confdef \
            -o Dpkg::Options::=--force-confold install $@
}

# Remove package(s)
#
# usage: ynh_package_remove name [name [...]]
# | arg: name - the package name to remove
#
# Requires YunoHost version 2.2.4 or higher.
ynh_package_remove() {
    ynh_apt remove $@
}

# Remove package(s) and their uneeded dependencies
#
# usage: ynh_package_autoremove name [name [...]]
# | arg: name - the package name to remove
#
# Requires YunoHost version 2.2.4 or higher.
ynh_package_autoremove() {
    ynh_apt autoremove $@
}

# Purge package(s) and their uneeded dependencies
#
# usage: ynh_package_autopurge name [name [...]]
# | arg: name - the package name to autoremove and purge
#
# Requires YunoHost version 2.7.2 or higher.
ynh_package_autopurge() {
    ynh_apt autoremove --purge $@
}

# Build and install a package from an equivs control file
#
# [internal]
#
# example: generate an empty control file with `equivs-control`, adjust its
#          content and use helper to build and install the package:
#              ynh_package_install_from_equivs /path/to/controlfile
#
# usage: ynh_package_install_from_equivs controlfile
# | arg: controlfile - path of the equivs control file
#
# Requires YunoHost version 2.2.4 or higher.
ynh_package_install_from_equivs () {
    local controlfile=$1

    # retrieve package information
    local pkgname=$(grep '^Package: ' $controlfile | cut -d' ' -f 2)	# Retrieve the name of the debian package
    local pkgversion=$(grep '^Version: ' $controlfile | cut -d' ' -f 2)	# And its version number
    [[ -z "$pkgname" || -z "$pkgversion" ]] \
        && ynh_die --message="Invalid control file"	# Check if this 2 variables aren't empty.

    # Update packages cache
    ynh_package_update

    # Build and install the package
    local TMPDIR=$(mktemp -d)

    # Force the compatibility level at 10, levels below are deprecated 
    echo 10 > /usr/share/equivs/template/debian/compat

    # Note that the cd executes into a sub shell
    # Create a fake deb package with equivs-build and the given control file
    # Install the fake package without its dependencies with dpkg
    # Install missing dependencies with ynh_package_install
    ynh_wait_dpkg_free
    cp "$controlfile" "${TMPDIR}/control"
    (cd "$TMPDIR"
    equivs-build ./control 1> /dev/null
    dpkg --force-depends -i "./${pkgname}_${pkgversion}_all.deb" 2>&1)
    ynh_package_install -f || ynh_die --message="Unable to install dependencies"
    [[ -n "$TMPDIR" ]] && rm -rf $TMPDIR	# Remove the temp dir.

    # check if the package is actually installed
    ynh_package_is_installed "$pkgname"
}

# Define and install dependencies with a equivs control file
#
# This helper can/should only be called once per app
#
# example : ynh_install_app_dependencies dep1 dep2 "dep3|dep4|dep5"
#
# usage: ynh_install_app_dependencies dep [dep [...]]
# | arg: dep - the package name to install in dependence. Writing "dep3|dep4|dep5" can be used to specify alternatives. For example : dep1 dep2 "dep3|dep4|dep5" will require to install dep1 and dep 2 and (dep3 or dep4 or dep5).
#
# Requires YunoHost version 2.6.4 or higher.
ynh_install_app_dependencies () {
    local dependencies=$@
    local dependencies=${dependencies// /, }
    local dependencies=${dependencies//|/ | }
    local manifest_path="../manifest.json"
    if [ ! -e "$manifest_path" ]; then
    	manifest_path="../settings/manifest.json"	# Into the restore script, the manifest is not at the same place
    fi

    local version=$(grep '\"version\": ' "$manifest_path" | cut -d '"' -f 4)	# Retrieve the version number in the manifest file.
    if [ ${#version} -eq 0 ]; then
        version="1.0"
    fi
    local dep_app=${app//_/-}	# Replace all '_' by '-'

    #
    # Epic ugly hack to fix the goddamn dependency nightmare of sury
    # Sponsored by the "Djeezusse Fokin Kraiste Why Do Adminsys Has To Be So Fucking Complicated I Should Go Grow Potatoes Instead Of This Shit" collective
    # https://github.com/YunoHost/issues/issues/1407
    # 
    # If we require to install php dependency
    if echo $dependencies | grep -q 'php';
    then
        # And we have packages from sury installed (7.0.33-10+weirdshiftafter instead of 7.0.33-0 on debian)
        if dpkg --list | grep "php7.0" | grep -q -v "7.0.33-0+deb9u5"
        then
            # And sury ain't already installed
            if ! grep -nrq "sury" /etc/apt/sources.list*
            then
                # Re-add sury
                echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/sury.list
                wget -O /etc/apt/trusted.gpg.d/sury.gpg https://packages.sury.org/php/apt.gpg    
            fi
        fi
    fi

    cat > /tmp/${dep_app}-ynh-deps.control << EOF	# Make a control file for equivs-build
Section: misc
Priority: optional
Package: ${dep_app}-ynh-deps
Version: ${version}
Depends: ${dependencies}
Architecture: all
Description: Fake package for ${app} (YunoHost app) dependencies
 This meta-package is only responsible of installing its dependencies.
EOF
    ynh_package_install_from_equivs /tmp/${dep_app}-ynh-deps.control \
        || ynh_die --message="Unable to install dependencies"	# Install the fake package and its dependencies
    rm /tmp/${dep_app}-ynh-deps.control
    ynh_app_setting_set --app=$app --key=apt_dependencies --value="$dependencies"
}

# Remove fake package and its dependencies
#
# Dependencies will removed only if no other package need them.
#
# usage: ynh_remove_app_dependencies
#
# Requires YunoHost version 2.6.4 or higher.
ynh_remove_app_dependencies () {
    local dep_app=${app//_/-}	# Replace all '_' by '-'
    ynh_package_autopurge ${dep_app}-ynh-deps	# Remove the fake package and its dependencies if they not still used.
}