#!/bin/sh
#
# Copyright (C) 2012-2020  Etersoft
# Copyright (C) 2012-2020  Vitaly Lipatov <lav@etersoft.ru>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#

load_helper epm-sh-altlinux
load_helper epm-sh-install
load_helper epm-query
load_helper epm-assure
load_helper epm-repack
load_helper epm-requires
load_helper epm-check_updated_repo
load_helper epm-sh-warmup
load_helper epm-installed


# for zypper before SUSE/11.0
__use_zypper_no_gpg_checks()
{
    a='' zypper install --help 2>&1 | grep -q -- "--no-gpg-checks" && echo "--no-gpg-checks"
}

# args: cmd_reinstall, cmd_install, packages
__separate_sudocmd_foreach()
{
    local cmd_re=$1
    local cmd_in=$2
    shift 2
    separate_installed $@
    if [ -n "$pkg_noninstalled" ] ; then
        sudocmd_foreach "$cmd_re" $pkg_noninstalled || return
    fi
    if [ -n "$pkg_installed" ] ; then
        sudocmd_foreach "$cmd_in" $pkg_installed || return
    fi
    return 0
}

# args: cmd_reinstall, cmd_install, packages
__separate_sudocmd()
{
    local cmd_re=$1
    local cmd_in=$2
    shift 2
    separate_installed $@
    if [ -n "$pkg_noninstalled" ] ; then
        sudocmd $cmd_re $pkg_noninstalled || return
    fi
    if [ -n "$pkg_installed" ] ; then
        sudocmd $cmd_in $pkg_installed || return
    fi
    return 0
}

# copied from etersoft-build-utils/share/eterbuild/functions/rpmpkg
epm_install_names()
{
    [ -z "$1" ] && return

    warmup_hibase

    if [ -n "$download_only" ] ; then
        epm download "$@"
        return
    fi

    if [ -n "$dryrun" ] ; then
        load_helper epm-simulate
        epm simulate "$@"
        return
    fi

    if [ -n "$non_interactive" ] ; then
        epm_ni_install_names "$@"
        return
    fi

    if [ -n "$force_overwrite" ] ; then
        APTOPTIONS="$APTOPTIONS -o Dpkg::Options::=--force-overwrite"
    fi

    case $PMTYPE in
        apt-rpm|apt-dpkg)
            APTOPTIONS="$APTOPTIONS -o APT::Sandbox::User=root $(subst_option verbose "-o Debug::pkgMarkInstall=1 -o Debug::pkgProblemResolver=1")"
            # https://bugzilla.altlinux.org/44670
            VIRTAPTOPTIONS="-o APT::Install::VirtualVersion=true -o APT::Install::Virtual=true"
            # not for kernel packages
            echo "$*" | grep -q "^kernel-"  && VIRTAPTOPTIONS=''
            sudocmd apt-get $VIRTAPTOPTIONS $APTOPTIONS $noremove install $@ && save_installed_packages $@
            return ;;
        aptitude-dpkg)
            sudocmd aptitude install $@
            return ;;
        deepsolver-rpm)
            sudocmd ds-install $@
            return ;;
        urpm-rpm)
            sudocmd urpmi $URPMOPTIONS $@
            return ;;
        packagekit)
            docmd pkcon install $@
            return ;;
        pkgsrc)
            sudocmd pkg_add -r $@
            return ;;
        pkgng)
            sudocmd pkg install $@
            return ;;
        redox-pkg)
            sudocmd pkg install $@
            return ;;
        emerge)
            sudocmd emerge -uD $@
            return ;;
        pacman)
            sudocmd pacman -S $nodeps $@
            return ;;
        aura)
            sudocmd aura -A $force $nodeps $@
            return ;;
        yum-rpm)
            sudocmd yum $YUMOPTIONS install $(echo "$*" | exp_with_arch_suffix)
            return ;;
        dnf-rpm|dnf5-rpm)
            sudocmd dnf install $(echo "$*" | exp_with_arch_suffix)
            return ;;
        snappy)
            sudocmd snappy install $@
            return ;;
        zypper-rpm)
            sudocmd zypper install $ZYPPEROPTIONS $@
            return ;;
        mpkg)
            sudocmd mpkg install $@
            return ;;
        eopkg)
            sudocmd eopkg $(subst_option nodeps --ignore-dependency) install $@
            return ;;
        conary)
            sudocmd conary update $@
            return ;;
        npackd)
            # FIXME: correct arg
            __separate_sudocmd_foreach "npackdcl add --package=" "npackdcl update --package=" $@
            return ;;
        slackpkg)
            __separate_sudocmd_foreach "/usr/sbin/slackpkg install" "/usr/sbin/slackpkg upgrade" $@
            return ;;
        homebrew)
            # FIXME: sudo and quote
            SUDO='' __separate_sudocmd "brew install" "brew upgrade" "$@"
            return ;;
        opkg)
            [ -n "$force" ] && force=-force-depends
            sudocmd opkg $force install $@
            return ;;
        nix)
            __separate_sudocmd "nix-env --install" "nix-env --upgrade" "$@"
            return ;;
        apk)
            sudocmd apk add $@
            return ;;
        tce)
            sudocmd tce-load -wi $@
            return ;;
        guix)
            __separate_sudocmd "guix package -i" "guix package -i" $@
            return ;;
        termux-pkg)
            sudocmd pkg install $@
            return ;;
        android)
            fatal "We still have no idea how to use package repository, ever if it is F-Droid."
            return ;;
        aptcyg)
            sudocmd apt-cyg install $@
            return ;;
        xbps)
            sudocmd xbps-install $@
            return ;;
        nix)
            info "When you ask Nix to install a package, it will first try to get it in pre-compiled form from a binary cache. By default, Nix will use the binary cache https://cache.nixos.org; it contains binaries for most packages in Nixpkgs. Only if no binary is available in the binary cache, Nix will build the package from source."
            sudocmd nix-env -iA $@
            return ;;
        appget|winget)
            sudocmd $PMTYPE install $@
            return ;;
        *)
            fatal 'Have no suitable install command for $PMTYPE'
            ;;
    esac
}

# Non interactive install
epm_ni_install_names()
{
    [ -z "$1" ] && return

    case $PMTYPE in
        apt-rpm)
            sudocmd apt-get -y $noremove --force-yes -o APT::Install::VirtualVersion=true -o APT::Install::Virtual=true -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" $APTOPTIONS install $@
            return ;;
        apt-dpkg)
            sudocmd env ACCEPT_EULA=y DEBIAN_FRONTEND=noninteractive apt-get -y $noremove --force-yes -o APT::Install::VirtualVersion=true -o APT::Install::Virtual=true -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" $APTOPTIONS install $@
            return ;;
        aptitude-dpkg)
            sudocmd env ACCEPT_EULA=y DEBIAN_FRONTEND=noninteractive aptitude -y install $@
            return ;;
        yum-rpm)
            sudocmd yum -y $YUMOPTIONS install $(echo "$*" | exp_with_arch_suffix)
            return ;;
        dnf-rpm|dnf5-rpm)
            sudocmd dnf install -y --allowerasing $YUMOPTIONS $(echo "$*" | exp_with_arch_suffix)
            return ;;
        urpm-rpm)
            sudocmd urpmi --auto $URPMOPTIONS $@
            return ;;
        zypper-rpm)
            # FIXME: returns true ever no package found, need check for "no found", "Nothing to do."
            yes | sudocmd zypper --non-interactive $ZYPPEROPTIONS install $@
            return ;;
        packagekit)
            docmd pkcon install --noninteractive $@
            return ;;
        pkgsrc)
            sudocmd pkg_add -r $@
            return ;;
        pkgng)
            sudocmd pkg install -y $@
            return ;;
        emerge)
            sudocmd emerge -uD $@
            return ;;
        pacman)
            sudocmd pacman -S --noconfirm $nodeps $@
            return ;;
        aura)
            sudocmd aura -A $force $nodeps $@
            return ;;
        npackd)
            #  npackdcl update --package=<package> (remove old and install new)
            sudocmd npackdcl add --package="$*"
            return ;;
        choco)
            docmd choco install $@
            return ;;
        opkg)
            sudocmd opkg -force-defaults install $@
            return ;;
        eopkg)
            sudocmd eopkg --yes-all install $@
            return ;;
        nix)
            sudocmd nix-env --install $@
            return ;;
        apk)
            sudocmd apk add $@
            return ;;
        tce)
            sudocmd tce-load -wi $@
            return ;;
        xbps)
            sudocmd xbps-install -y $@
            return ;;
        appget|winget)
            sudocmd $PMTYPE -s install $@
            return ;;
        homebrew)
            # FIXME: sudo and quote
            SUDO='' __separate_sudocmd "brew install" "brew upgrade" $@
            return ;;
        termux-pkg)
            sudocmd pkg install $@
            return ;;
        slackpkg)
            # FIXME: broken status when use batch and default answer
            __separate_sudocmd_foreach "/usr/sbin/slackpkg -batch=on -default_answer=yes install" "/usr/sbin/slackpkg -batch=on -default_answer=yes upgrade" $@
            return ;;
        *)
            fatal 'Have no suitable appropriate install command for $PMTYPE'
            ;;
    esac
}

__epm_check_if_rpm_already_installed()
{
    # Not: we can make optimize if just check version?
    LC_ALL=C sudorun rpm -Uvh --test "$@" 2>&1 | grep -q "is already installed"
}

__handle_direct_install()
{
    case "$BASEDISTRNAME" in
        "alt")
            load_helper epm-download
            local pkg url
            for pkg in $pkg_names ; do
                url=$(__epm_get_altpkg_url $pkg)
                [ -n "$url" ] || continue
                # TODO: use estrlist
                pkg_urls="$pkg_urls $url"
            done
            # FIXME: need remove
            pkg_names=""
            ;;
    esac
}

# TODO: forbid src.rpm
__epm_check_if_src_rpm()
{
    local pkg
    for pkg in $@ ; do
        echo "$pkg" | grep -q "\.src\.rpm" && fatal "Installation of a source packages (like '$pkg') is not supported."
    done
}

__epm_if_command_path()
{
    is_dirpath "$1" && rhas "$1" "bin/" && ! rhas "$1" "/home"
}

__epm_get_replacepkgs()
{
    [ -n "$2" ] && echo '--replacepkgs' && return
    # don't use --replacepkgs when install only one file
}

epm_install_files()
{
    local files="$*"
    [ -z "$files" ] && return

    # TODO: enable support only for systems with support for it
    # on some systems install target can be a real path
    # use hi-level for install by command path (f.i. epm install /usr/bin/git)
    if __epm_if_command_path $files ; then
        epm_install_names $files
        return
    elif is_dirpath "$1" && [ ! -f "$1" ] ; then
        fatal "Can't install non-existent file '$1'"
    fi

    # TODO: check read permissions
    # sudo test -r FILE
    # do not fallback to install_names if we have no permissions
    case "$BASEDISTRNAME" in
        "alt")
            load_helper epm-install-alt
            epm_install_files_alt $files
            return
            ;;
    esac

    case $PMTYPE in
        apt-dpkg|aptitude-dpkg)
            load_helper epm-install-apt-dpkg
            epm_install_files_apt_dpkg $files
            return
            ;;

       *-rpm)
            load_helper epm-install-rpm
            epm_install_files_rpm $files
            return
            ;;
    esac


    # check save_only before commands without repack supporting
    if [ -n "$save_only" ] ; then
        echo
        cp -v $files "$EPMCURDIR"
        return
    fi

    if [ -n "$put_to_repo" ] ; then
        load_helper epm-repopkg
        epm_put_to_repo $files
        return
    fi


    case $PMTYPE in
        packagekit)
            docmd pkcon install-local $files
            return ;;
        pkgsrc)
            sudocmd pkg_add $files
            return ;;
        pkgng)
            local PKGTYPE="$(get_package_type $files)"
            case "$PKGTYPE" in
                tbz)
                    sudocmd pkg_add $files
                    ;;
                *)
                    sudocmd pkg add $files
                    ;;
            esac
            return ;;
        android)
            sudocmd pm install $files
            return ;;
        eopkg)
            sudocmd eopkg install $files
            return ;;
        emerge)
            load_helper epm-install-emerge
            sudocmd epm_install_emerge $files
            return ;;
        pacman)
            sudocmd pacman -U --noconfirm $nodeps $files && return
            local RES=$?

            [ -n "$nodeps" ] && return $RES
            sudocmd pacman -U $files
            return ;;
        slackpkg)
            # FIXME: check for full package name
            # FIXME: broken status when use batch and default answer
            __separate_sudocmd_foreach "/sbin/installpkg" "/sbin/upgradepkg" $files
            return ;;
    esac

    # other systems can install file package via ordinary command
    epm_install_names $files
}


epm_install()
{
    if [ "$BASEDISTRNAME" = "alt" ] ; then
        if tasknumber "$pkg_names" >/dev/null ; then
            load_helper epm-install-alt
            if [ -n "$interactive" ] ; then
                confirm_info "You are about to install $pkg_names task(s) from https://git.altlinux.org."
            fi
            epm_install_alt_tasks "$pkg_names"
            return
        fi
    fi

    if [ -n "$manual_requires" ] ; then
        local pkg_names="$pkg_names $(short=1 epm_requires $pkg_names)"
    fi

    if [ -n "$show_command_only" ] ; then
        # TODO: handle pkg_urls too
        load_helper epm-install-print-command
        epm_print_install_files_command $pkg_files
        epm_print_install_names_command $pkg_names
        return
    fi

    if [ -n "$interactive" ] && [ -n "$pkg_names$pkg_files$pkg_urls" ] ; then
        confirm_info "You are about to install $(echo $pkg_names $pkg_files $pkg_urls) package(s)."
        # TODO: for some packages with dependencies apt will ask later again
    fi

    if [ -n "$direct" ] && [ -z "$repack" ] ; then
        # it will put pkg_urls into pkg_files and reconstruct pkg_filenames
        __handle_direct_install
    fi

    if [ -n "$pkg_urls" ] ; then
        load_helper epm-download
        # it will put downloaded by pkg_urls packages to pkg_files and reconstruct pkg_filenames
        __handle_pkg_urls_to_install
    fi

    [ -z "$pkg_files$pkg_names" ] && info "Empty install list was skipped" && return 22

    # to be filter happy
    warmup_lowbase

    # Note: filter_out_installed_packages depends on skip_installed flag
    local names="$(echo $pkg_names | filter_out_installed_packages)"
    #local names="$(echo $pkg_names | exp_with_arch_suffix | filter_out_installed_packages)"
    local files="$(echo $pkg_files | filter_out_installed_packages)"

    # can be empty only after all installed packages were skipped
    if [ -z "$files$names" ] ; then
        # TODO: assert $skip_installed
        [ -n "$verbose" ] && info "Empty install list was skipped (filtered out, all requested packages is already installed)"
        # FIXME: see to_remove below
        return 0
    fi

    if [ -n "$names" ] && [ -z "$direct" ] ; then
        # it is useful for first time running
        update_repo_if_needed
    fi

    case "$BASEDISTRNAME" in
        "alt")
            load_helper epm-install-alt
            epm_install_alt_names $names || return
            ;;
        *)
            # FIXME: see to_remove below
            epm_install_names $names || return
            ;;
    esac

    [ -z "$files" ] && debug "Empty install files list was skipped" && return 0

    if [ -n "$download_only" ] ; then
        # save files to the current dir before install and repack
        echo
        cp -v $files "$EPMCURDIR"
        return
    fi

    if [ -n "$repack" ] ; then
        # repack binary files if asked
        __epm_repack $files || return
        files="$repacked_pkgs"
    fi

    epm_install_files $files
}