#!/bin/bash

. $LKP_SRC/lib/result.sh
. $LKP_SRC/lib/lkp_cmd.sh
. $LKP_SRC/lib/env.sh
. $LKP_SRC/lib/constant.sh

if ! [ $(id -u) = 0 ]; then
	echo "This script must be run as root" 1>&2
	exit 1
fi

script_name=$(basename $0)

[ -n "$HOSTNAME" ] || HOSTNAME=$(hostname)		#?chomp
[ -n "$LKP_SRC" ] || export LKP_SRC=$(dirname $(dirname $(readlink -e -v $0)))
TMP="$LKP_SRC/tmp"

usage() {
	echo "Usage: lkp $script_name [options] [<script>/<jobfile>]"
	echo "options: "
	echo "-f|--force: force to install testsuites"
	echo "--hdd partition: HDD partition for IO tests"
	echo "--ssd partition: SSD partition for IO tests"
	echo "--dry-run: preview changes will made testbox by install"
	echo "--china: install gem packages from the mirror in China"
	echo "--skip-base: skip to install base support"
	echo "--help: show this message"
}

hdd_index=1
ssd_index=1

while [ $# -gt 0 ]
do
	case "$1" in
		--help)
		usage
		exit
		;;
		--hdd)
		hdd_partitions[$hdd_index]=$2
		hdd_index=$((hdd_index+1))
		shift
		;;
		--ssd)
		ssd_partitions[$ssd_index]=$2
		ssd_index=$((ssd_index+1))
		shift
		;;
		--dry-run)
		DRY_RUN=0
		;;
		--china)
		CHINA=0
		;;
		--force|-f)
		FORCE_MODE=true
		;;
		--skip-base)
		SKIP_BASE=true
		;;
		*)
		break
		;;
	esac
	shift
done

validate_parameters()
{
    local ret=0
    for filename in "$@"
    do
        if [ -x "$filename" ]; then
            continue
        elif [ ${filename##*.} = "yaml" ]; then
            if [ -f $filename ]; then
                grep -q '^arch: ' $filename || {
                    echo "only atomic jobs can be installed" >&2
                    echo "Please run lkp split-job $filename first" >&2
                    ret=1
                }
            else
                echo "$0: cannot find file $filename" >&2
                ret=1
            fi
        else
            echo "$0: skip unknown parameter $filename" >&2
            ret=1
        fi
    done
    return $ret
}

make_wakeup() {
	echo "make -C $LKP_SRC/bin/event"
	[ -n "$DRY_RUN" ] && return

	[ -x "$LKP_SRC/bin/event/wakeup" ] || {
		make -C "$LKP_SRC/bin/event" wakeup
	}
}

# todo: make paths configurable and less intrusive to developer's system
# currently it's hard coded in a number of places, should be changed together
create_lkp_dirs() {
	[ -n "$DRY_RUN" ] && return

	mkdir -p $TMP
	mkdir -p $KTEST_PATHS_DIR
	mkdir -p '/lkp/benchmarks'
}

create_host_config() {
	[ -n "$DRY_RUN" ] && return

	local host_config="$LKP_SRC/hosts/${HOSTNAME}"
	[ -e $host_config ] || {
		echo "Creating testbox configuration file: $host_config."

		local mem_kb="$(grep MemTotal /proc/meminfo | awk '{print $2}')"
		local mem_gb="$(((mem_kb)/1024/1024))"
		local nr_cpu=$(nproc)

		cat <<EOT >> $host_config
nr_cpu: $nr_cpu
memory: ${mem_gb}G
hdd_partitions: ${hdd_partitions[*]}
ssd_partitions: ${ssd_partitions[*]}
local_run: 1
EOT
	}

	local tbox_group
	set_tbox_group $HOSTNAME

	local host_group_config="$LKP_SRC/hosts/${tbox_group}"
	[ -e $host_group_config ] || {
		echo "Creating testbox group configuration file: $host_group_config."
		cp $host_config $host_group_config
	}
}

bundle_install_china() {
	local tmpdir=$(mktemp -d /tmp/lkp-install-china-XXXXXX)
	trap "rm -rf $tmpdir" EXIT
	cp $LKP_SRC/Gemfile $tmpdir/Gemfile
	cp $LKP_SRC/Gemfile.lock $tmpdir/Gemfile.lock
	(cd $tmpdir; bundle config --local mirror.https://rubygems.org https://gems.ruby-china.com; bundle install)
}

makepkg_install() {
	local pkg=$1
	local force_opt=

	[ -n "$FORCE_MODE" ] && force_opt="-f"
	if [ -d "$LKP_SRC/pkg/$pkg" ]; then
		(
			cd "$LKP_SRC/pkg/$pkg" && \
			PACMAN="$LKP_SRC/sbin/pacman-LKP" CARCH=$arch BUILDDIR="$LKP_SRC/tmp-pkg" \
			"$LKP_SRC/sbin/makepkg" --config "$LKP_SRC/etc/makepkg.conf" -s -i --skippgpcheck "$force_opt"
		)
	else
		return 0
	fi
}

verify_install() {
	local pkg=$1
	case $DISTRO in
		debian|ubuntu)
			[[ ! $(dpkg -V "$pkg" 2>&1) ]] || [[ ! $(dpkg -V "$pkg-lkp" 2>&1) ]];;
		centos|fedora|openeuler)
			[[ ! $(rpm -V "$pkg" 2>&1) ]] || [[ ! $(rpm -V "$pkg-lkp" 2>&1) ]];;
		*)
			return 1;;
	esac
}

makepkg_install_packages() {
	local script=$1
	local distro=$2

	local packages dev_packages pkg
	packages="$(get_dependency_packages ${distro} ${script} pkg)"
	dev_packages="$(get_dependency_packages ${distro} ${script}-dev pkg)"
	packages="$(echo $packages $dev_packages | tr '\n' ' ')"
	[ -n "$packages" ] && [ "$packages" != " " ] || return

	for pkg in $packages; do
		[ "$pkg" = "$script" ] && continue
		if ! verify_install $pkg; then
			install_packages "$pkg" "$distro"
			makepkg_install_packages "$pkg" "$distro"
			makepkg_install "$pkg" || echo "Install $pkg failed"
		fi
	done
}

makepkg_install_benchmark() {
	local pkg=$1
	if grep -w "^$pkg:" "$LKP_SRC/distro/adaptation-pkg/$distro"; then
		makepkg_install "$pkg" || echo "Install $pkg failed"
	fi
}

install_packages() {
	local script=$1
	local distro=$2

	local packages="$(get_dependency_packages ${distro} ${script})"
	local dev_packages="$(get_dependency_packages ${distro} ${script}-dev)"
	local generic_packages="$(echo $packages $dev_packages | tr '\n' ' ')"
	[ -n "$generic_packages" -a "$generic_packages" != " " ] || return

	[ "$distro" = "debian" ] && remove_packages_repository
	echo "Use: $LKP_SRC/distro/installer/$distro install $generic_packages"
	[ -n "$DRY_RUN" ] && return

	if [ "$distro" = "centos" -o "$distro" = "aliyun" ]; then
		local ocfs2_tools_name=$(echo "$generic_packages" | grep 'ocfs2-tools')
		if [ -n "$ocfs2_tools_name" ]; then
			. $LKP_SRC/distro/$distro
			install_ocfs2_tools "$ocfs2_tools_name"
		fi
	fi

	$LKP_SRC/distro/installer/$distro $generic_packages || {
		echo "Cannot install some packages in $LKP_SRC/distro/depends/${script}"
		exit 1
	}
}

build_install_benchmarks() {
	local script=$1
	local distro=$2
	local force_install=$3

	[ -x "$LKP_SRC/pack/$script" ] || return 0

	echo "Making $script benchmark for $distro"
	[ -n "$DRY_RUN" ] && return

	[ "$script" = 'perf' ] && perf -v 2>/dev/null && {
		echo "perf tool exists, skip building"
		return 0
	}

	local pkg_name
	pkg_name=$(printf '%s-LKP\n' "$script" | sed 's/_/-/g')

	$LKP_SRC/sbin/pack -d $distro -c $force_install $script
	if [ $? -eq 0 ]; then
		echo "Installing $pkg_name"

		case $distro in
		debian|ubuntu)
			local deb_pkg=/tmp/$pkg_name.deb
			[ -f $deb_pkg ] || return 0
			dpkg -i $deb_pkg 2>/tmp/dpkg_error || {
				grep -v "dpkg: warning: files list file for package '.*' missing;" /tmp/dpkg_error
				return 1
			}
			;;
		fedora)
			BUILD_DIR=$(get_build_dir $script)
			local rpm_pkg=$BUILD_DIR/$pkg_name/RPMS/$pkg_name.$arch.rpm
			[ -f $rpm_pkg ] || return
			rpm -ivh --replacepkgs $rpm_pkg ;;
		*)
			echo "Just make /lkp/benchmarks/$script-$arch.cgz" ;;
		esac
	else
		echo "Making $pkg_name failed"
		return 1
	fi
}

install_base_support()
{
	install_packages "lkp" "$distro"

	if [ -n "$CHINA" ]; then
		gem install bundler --clear-sources -s https://gems.ruby-china.com/
		bundle_install_china
	else
		(
			cd $LKP_SRC
			gem install bundler
			bundle install
		)
	fi

	make_wakeup
	create_lkp_user
	create_lkp_dirs
	create_host_config
}

. $LKP_SRC/lib/install.sh

detect_system
distro=$_system_name_lowercase

DISTRO=$distro
export DISTRO

[ -x $LKP_SRC/distro/installer/$distro ] || {
	echo "Not a supported system, cannot install packages."
	exit 1
}

export LKP_LOCAL_RUN=1

validate_parameters "$@" || exit 1

[ -n "$SKIP_BASE" ] || install_base_support

for filename in "$@"
do
	scripts=
	if [ -x "$filename" ]; then
		scripts=$(basename $filename)
	elif [ ${filename##*.} = "yaml" ]; then
		parse_yaml $filename
	fi

	for script in $scripts
	do
		[ -L "$LKP_SRC/pack/$script" ] && {
			real_script=$(readlink "$LKP_SRC/pack/$script")
			script=$(basename "$real_script")
		}

		[ -z "$makepkg_once" ] && [ -d "$LKP_SRC/pkg/$script" ] && ! [ -x "$LKP_SRC/pack/$script" ] && {
			install_packages "makepkg" $distro
			makepkg_once=1
		}
		install_packages "$script" $distro
		makepkg_install_packages "$script" $distro
		if [ -n "$FORCE_MODE" ] || ! verify_install "$script"; then
			makepkg_install_benchmark "$script"
		fi
		[ -n "$FORCE_MODE" ] && install_opt="-f"
		build_install_benchmarks "$script" $distro $install_opt || exit 1
	done
done
