# -*-eselect-*-  vim: ft=eselect
# Copyright 1999-2013 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2 or later
# $Id: $

DESCRIPTION="Switch between settingsd implementations"
MAINTAINER="lxnay@gentoo.org"
VERSION="0.1"

# Directory where eselect settingsd must install root directory trees
# whose files will be symlinked onto the real system.
SETTINGSD_DIR="${EROOT}/%SETTINGSD_DIR%"

# File containing the current settingsd implementation.
CURRENT_SETTINGSD_FILE="${EROOT}/etc/env.d/eselect-settingsd/current"

# Convert source ${1} to be relative to destination, parent directory
# of ${2}. This is slightly different between relative_name, because
# source and destination may not exist.
_relative_name() {
	local s="${1}"
	local d="${2}"
	local parents=
	local d_dir=$(dirname "${d}")

	while [[ "${d_dir}" != "/" ]]; do
		parents+="../"
		d_dir="$(dirname "${d_dir}")"
	done
	parents="${parents:-.}"
	parents="${parents%/}"
	echo "${parents}${s}"
}

find_targets() {
	local d=
	local dname=
	local legit=
	for d in "${SETTINGSD_DIR}/"*; do
		[[ ! -d "${d}" ]] && continue
		dname=$(basename "${d}")
		echo "${dname}"
	done
}

_get_target_dir() {
	local target="${1}"

	if is_number "${target}"; then
		local targets=( $(find_targets) )
		target=${targets[target-1]}
	fi

	local target_dir="${SETTINGSD_DIR}/${target}"
	[[ -z "${target}" || ! -d "${target_dir}" ]] \
		&& die -q "Target \"${target}\" doesn't appear to be valid!"
	echo ${target_dir}
}

_get_settingsd_files() {
	local target="${1}"
	local target_dir=$(_get_target_dir "${target}")

	files="$(find "${target_dir}" -type f -print 2>/dev/null)"
	files+=" $(find "${target_dir}" -type l -print 2>/dev/null)"
	echo ${files}
}

_clean_old_target() {
	local target="${1}"
	local target_dir=$(_get_target_dir "${target}")

	local f=
	local files=( $(_get_settingsd_files "${target}") )
	for f in "${files[@]}"; do
		f="${f#${target_dir}}"
		[[ -L "${f}" ]] && rm -f "${f}"
	done
}

set_settingsd() {
	local target="${1}"
	local target_dir=$(_get_target_dir "${target}")
	echo "Setting the settingsd implementation to ${target}"

	# TODO: differential removal?
	local old_target=$(get_settingsd)
	[[ -n "${old_target}" ]] && _clean_old_target "${old_target}"

	echo "${target}" > "${CURRENT_SETTINGSD_FILE}.tmp" || {
		rm -f "${CURRENT_SETTINGSD_FILE}.tmp";
		die "Cannot save the current temp. settingsd state"
	}

	local s= d=
	local files=( $(_get_settingsd_files "${target}") )
	for s in "${files[@]}"; do
		d="${s#${target_dir}}"
		s=$(_relative_name "${s}" "${d}")
		ln -sf "${s}" "${d}.tmp" || {
			rm -f "${d}.tmp";
			die "Cannot symlink ${s} to ${d}.tmp";
		}
		mv "${d}.tmp" "${d}" || die "Cannot mv ${d}.tmp"
	done

	mv "${CURRENT_SETTINGSD_FILE}.tmp" "${CURRENT_SETTINGSD_FILE}" || \
		die "Cannot save the current settingsd state"

	# silently reload dbus
	/usr/bin/dbus-send --print-reply --system --type=method_call \
		--dest=org.freedesktop.DBus \
		/ org.freedesktop.DBus.ReloadConfig > /dev/null
}

get_settingsd() {
	cat "${CURRENT_SETTINGSD_FILE}" 2> /dev/null
}

describe_show() {
	echo "Show the current init implementation"
}

describe_show_options() {
	echo "--quiet  : only print the actual settingsd implementation"
	echo "--silent : same as --quiet"
}

do_show() {
	local quiet=
	local settingsd=

	while [[ $# -gt 0 ]]; do
		case ${1##--} in
			quiet|silent)
				quiet="1"
				;;
		esac
		shift
	done

	settingsd=$(get_settingsd)
	[[ -z "${settingsd}" ]] && write_list_start "Current :"
	if [[ -n "${settingsd}" ]]; then
		if [[ -n "${settingsd}" ]]; then
			echo "${settingsd}"
		else
			write_kv_list_entry "${settingsd}" ""
		fi
	else
		if [[ -z "${quiet}" ]]; then
			write_kv_list_entry "(unset)" ""
		fi
	fi
}

describe_list() {
	echo "List available settingsd implementations"
}

describe_list_options() {
	echo "--quiet  : only print the actual list of implementations"
	echo "--silent : same as --quiet"
}

do_list() {
	local quiet=
	local i=
	local settingsd=$(get_settingsd)
	local targets=( $(find_targets) )

	while [[ $# -gt 0 ]]; do
		case ${1##--} in
			quiet|silent)
				quiet="1"
				;;
		esac
		shift
	done

	[[ -z "${quiet}" ]] && \
		write_list_start "Available settingsd implementations:"
	for (( i = 0; i < ${#targets[@]}; i++ )); do
		if [[ -z "${quiet}" ]]; then
			[[ "${targets[i]}" == "${settingsd}" ]] && \
				targets[i]=$(highlight_marker "${targets[i]}")
		else
			echo "${targets[i]}"
		fi
	done
	[[ -z "${quiet}" ]] && \
		write_numbered_list -m "(none found)" "${targets[@]}"
}

describe_set() {
	echo "Set a new settingsd implementation"
}

describe_set_parameters() {
	echo "<target>"
}

describe_set_options() {
	echo "target : Target name or number (from 'list' action)"
	echo "--use-old : use the old value if target is already set"
}

do_set() {
	local use_old=
	local target="${1}"

	while [[ $# -gt 0 ]]; do
		case ${1##--} in
			use-old)
				use_old="1"
				;;
		esac
		shift
	done

	[[ -z "${target}" ]] && \
	   die -q "You didn't tell me what to set the symlink to"
	[[ ${#} -gt 1 ]] && die -q "Too many parameters"

	if [[ "${use_old}" = "1" ]]; then
	   old_target=$(get_settingsd)
	   target="${old_target:-${target}}"
	fi

	set_settingsd "${target}" || die -q "Couldn't set a new settingsd"
}