#!/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/>.
#

epm_install_files_alt_via_repo()
{
    local files="$*"
    [ -n "$files" ] || return

    local repodir="$epm_vardir/local-repo"

    # create repo and put packages to it
    [ -d "$repodir" ] || sudocmd epm repo create $repodir
    sudocmd epm repo pkgadd $repodir $files
    sudocmd epm repo index $repodir

    #docmd epm repo save

    docmd epm repo add $repodir
    docmd epm update

    # install with name=version
    # TODO: improve
    st=''
    for i in $files ; do
        st="$st $(epm print name for package $i)=$(epm print version for package $i)"
    done
    docmd epm install $st

    # the repo will used in epm image s supply
    #docmd epm repo restore
}

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

    # TODO: check read permissions
    # sudo test -r FILE
    # do not fallback to install_names if we have no permissions

    __epm_print_warning_for_nonalt_packages $files

    # do repack if needed
    if __epm_repack_if_needed $files ; then
        [ -n "$repacked_pkgs" ] || fatal 'Can'\''t convert $files'
        files="$repacked_pkgs"
    fi

    if [ -n "$save_only" ] ; then
        echo
        cp -v $files "$EPMCURDIR"
        return
    fi

    # install packages via apm on ALT Atomic
    #if [ "$PMTYPE" = "apm-rpm" ] ; then
    if [ "$FULLDISTRNAME" = "ALT Atomic" ] ; then
        [ -n "$nodeps" ] || fatal "Option --nodeps is not supported in apm"
        epm_install_files_alt_via_repo $files
        return
    fi

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

    __epm_check_if_src_rpm $files

    if [ -z "$repacked_pkgs" ] ; then
        __epm_check_vendor $files
        __epm_check_if_needed_repack $files
    fi

    # --replacepkgs: Install the Package Even If Already Installed
    local replacepkgs="$(__epm_get_replacepkgs $files)"
    sudocmd rpm -Uvh $replacepkgs $(subst_option dryrun --test) $force $noscripts $nodeps $files && save_installed_packages $files && return
    local RES=$?
    # TODO: check rpm result code and convert it to compatible format if possible
    __epm_check_if_rpm_already_installed $force $replacepkgs $noscripts $nodeps $files && return

    # if run with --nodeps, do not fallback on hi level
    [ -n "$nodeps" ] && return $RES

    # separate second output
    info

    # Workround for https://bugzilla.altlinux.org/56327
    # TODO: check apt version
    if is_file_greater_2gb $files ; then
        direct="--direct"
    fi

    # TODO: check rpm version
    if is_file_greater_4gb $files ; then
        fatal "Only RPM 6 can install packages larger than 4 GB"
    fi

    # try install via apt if we could't install package file via rpm (we guess we need install requirements firsly)

    if [ -z "$direct" ] && [ -z "$noscripts" ] ; then
        epm_install_names $files
        return
    fi

    # TODO: use it always (apt can install version from repo instead of a file package)
    info "Workaround for install packages via apt with --noscripts (see https://bugzilla.altlinux.org/44670)"
    info "Firstly install package requrements …"
    # names of packages to be installed
    local fl="$(epm print name for package $files)"
    local req="$(docmd epm req --short $files)" || return
    # exclude package names from requires (req - fl)
    req="$(estrlist exclude "$fl" "$req")"
    # TODO: can we install only requires via apt?
    docmd epm install --skip-installed $req || return

    # retry with rpm
    # --replacepkgs: Install the Package Even If Already Installed
    local replacepkgs="$(__epm_get_replacepkgs $files)"
    sudocmd rpm -Uvh $replacepkgs $(subst_option dryrun --test) $force $noscripts $nodeps $files && save_installed_packages $files
}

get_current_kernel_flavour()
{
    rrel=$(uname -r)
    rflv=${rrel#*-}
    rflv=${rflv%-*}
    echo "$rflv"
}

# std-def 1.2.3-alt1 -> 1.2.3-std-def-alt1
make_kernel_release()
{
    echo "$2" | sed -e "s|-|-$1-|"
}

# return latest installed kernel in a form like 5.15.109-un-def-alt1
get_latest_kernel_rel()
{
    local kernel_flavour="$1"
    # current
    rrel=$(uname -r)

    # latest
    # copied and modified from update-kernel
    # get the maximum available kernel package version
    kmaxver=
    while read version
    do
        comparever="$(a='' rpmevrcmp "$kmaxver" "$version")"
        [ "$comparever" -lt 0 ] && kmaxver="$version" ||:
    done <<<"$(epm print version-release for package kernel-image-$kernel_flavour)"
    [ -z "$kmaxver" ] && echo "$rrel" && return

    make_kernel_release "$kernel_flavour" "$kmaxver"
}

# install <module-name>-std-def
epm_install_alt_kernel_module()
{
    [ -n "$1" ] || return 0

    local kflist=''
    local kmplist=''
    local kmf module flavour tmp

    # fill kernel flavour list
    for kmf in "$@"; do
        case "$kmf" in
            # full package with explicit version: kernel-modules-<mod>-<ver>
            kernel-modules-*-*[0-9]*)
                tmp=${kmf#kernel-modules-}      # tmp="<mod>-<ver>"
                flavour=${tmp##*-}              # take version part
                ;;
            # full package without version: kernel-modules-<mod>
            kernel-modules-*)
                flavour=$(get_current_kernel_flavour)
                ;;
            # short name with version: <mod>-<ver>
            *-[0-9]*)
                flavour=${kmf##*-}
                ;;
            # everything else — module name only
            *)
                flavour=$(get_current_kernel_flavour)
                ;;
        esac
        kflist="$kflist $flavour"
    done

    # firstly, update all needed kernels (by flavour)
    for flavour in $(estrlist uniq $kflist); do
        info
        docmd epm update-kernel -t "$flavour" || exit
    done

    # skip install modules if there are no installed kernels (may be, a container)
    epm installed "kernel-image-$flavour" || return 0

    # make list for install kernel modules
    for kmf in "$@"; do
        case "$kmf" in
            kernel-modules-*-*[0-9]*)
                tmp=${kmf#kernel-modules-}
                module=${tmp%-*}
                flavour=${tmp##*-}
                ;;
            kernel-modules-*)
                module=${kmf#kernel-modules-}
                flavour=$(get_current_kernel_flavour)
                ;;
            *-[0-9]*)
                module=${kmf%-*}
                flavour=${kmf##*-}
                ;;
            *)
                module=$kmf
                flavour=$(get_current_kernel_flavour)
                ;;
        esac
        kvf=$(get_latest_kernel_rel "$flavour")
        kmplist="$kmplist kernel-modules-$module-$kvf"
    done

    # secondly, install module(s)
    epm_install_names $kmplist
}


epm_install_alt_names()
{
    local kmlist=''
    local installnames=''

    while [ -n "$1" ] ; do
        local pkgname
        pkgname="$1"
        if echo "$pkgname" | grep -v "#" | grep -q "^kernel-modules*-" ; then
            # virtualbox[-std-def]
            local kmn="$(echo $pkgname | sed -e 's|kernel-modules*-||')"
            local kf1="$(echo "$kmn" | cut -d- -f2)"
            local kf2="$(echo "$kmn" | cut -d- -f4)"
            # pass install with full pkgnames
            if [ "$kf1" != "$kf2" ] && [ -n "$kf2" ] || echo "$kf1" | grep -q "^[0-9]" ; then
                installnames="$installnames $pkgname"
            else
                kmlist="$kmlist $kmn"
            fi
        else
            installnames="$installnames $pkgname"
        fi
        shift
    done

    epm_install_names $installnames || return
    epm_install_alt_kernel_module $kmlist || return
}

# Unused hack for apt-repo
# apt-repo with non_interactive support
apt_repo_prepare()
{
    assure_exists apt-repo
    [ -n "$non_interactive" ] || return

    set_sudo
    trap "$SUDO rm /etc/apt/apt.conf.d/eepm-apt-noninteractive.conf 2>/dev/null" EXIT
    echo 'APT::Get::Assume-Yes "true";' | $SUDO tee /etc/apt/apt.conf.d/eepm-apt-noninteractive.conf >/dev/null
}

apt_repo_after()
{
    [ -n "$non_interactive" ] || return

    $SUDO rm /etc/apt/apt.conf.d/eepm-apt-noninteractive.conf 2>/dev/null
}

# Process arguments in TASK or TASK:package format
prepare_task_packages()
{
    # Sets global vars:
    #   installlist  — final list of packages to install
    #   unique_tasks — unique task names from args
    #
    # Local vars:
    #   seen_tasks, task_packages, task, pkg

    installlist=""
    unique_tasks=""
    local seen_tasks=""
    local task_packages=""

    for arg in "$@"; do
        # Parse argument into task and package
        # Supported formats: 123456, 123456/pkg, task/123456, task/123456/pkg
        local task=""
        local pkg=""
        case "$arg" in
            task/*/*)
                # task/123456/pkg
                task=$(printf "%s" "$arg" | cut -d/ -f2)
                pkg=$(printf "%s" "$arg" | cut -d/ -f3)
                ;;
            task/*)
                # task/123456
                task=$(printf "%s" "$arg" | cut -d/ -f2)
                ;;
            */*)
                # 123456/pkg
                task=$(printf "%s" "$arg" | cut -d/ -f1)
                pkg=$(printf "%s" "$arg" | cut -d/ -f2)
                ;;
            *)
                # 123456
                task="$arg"
                ;;
        esac

        # Fetch task packages once per unique task
        if ! echo " $seen_tasks " | grep -q " $task "; then
            unique_tasks="$unique_tasks $task"
            task_packages=$(get_task_packages "$task")
            seen_tasks="$seen_tasks $task"
        fi

        # Add specific package or all task packages to install list
        if [ -n "$pkg" ]; then
            installlist="$installlist $pkg"
        else
            installlist="$installlist $task_packages"
        fi
    done
}

# copied from epm_upgrade_alt_tasks()
epm_install_alt_tasks()
{
    load_helper epm-addrepo
    load_helper epm-reposave
    load_helper epm-removerepo
    load_helper epm-Install

    prepare_task_packages "$@"

    [ -n "$verbose" ] && info "Packages from task(s): $installlist"

    if [ -n "$full" ] ; then
        installlist="$(estrlist reg_exclude ".*-checkinstall .*-debuginfo" "$installlist")"
    else
        # hack: drop -devel packages to avoid package provided by multiple packages
        installlist="$(estrlist reg_exclude ".*-devel .*-devel-static .*-checkinstall .*-debuginfo" "$installlist")"
    fi

    # TODO: need we this option?
    #if [ -z "$force" ] ; then
    #    # skip i586- on install
    installlist="$(estrlist reg_exclude "i586-.*" "$installlist")"
    #fi

    [ -n "$verbose" ] && info "Packages to install: $installlist"

    if [ -z "$installlist" ] ; then
        warning 'There is no installed packages for upgrade from task' "$*"
        return 22
    fi

    local res
    try_change_alt_repo
    epm_addrepo $unique_tasks
    __epm_update
    (pkg_names="$installlist" epm_install)
    res=$?
    epm_removerepo $unique_tasks
    end_change_alt_repo
    return $res
}
