# Copyright 1999-2011 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id: $

inherit config multilib output package-manager path-manipulation

DESCRIPTION="Manage active PostgreSQL client applications and libraries"
MAINTAINER="pgsql-bugs@gentoo.org"
SVN_DATE='$Date: $'
VERSION="1.0.4"

# Global Data
B_PATH="${EROOT%/}/usr"
E_PATH="${EROOT%/}/etc/eselect/postgresql"
ENV_FILE="${EROOT%/}/etc/env.d/50postgresql"
if [ -r ${E_PATH}/active -a -n ${E_PATH}/active ] ; then
	ACTIVE_SLOT=$(<${E_PATH}/active)
else
	ACTIVE_SLOT="(none)"
fi
LIB_LIST=$(list_libdirs)
if [[ ${LIB_LIST} =~ .*lib64.* && "$(ls -d ${B_PATH}/lib64/postgresql-*/lib)" != "" ]] ; then
	LIBDIR="lib64"
elif [[ ${LIB_LIST} =~ .*lib32.* && "$(ls -d ${B_PATH}/lib32/postgresql-*/lib)" != "" ]] ; then
	LIBDIR="lib32"
else
	LIBDIR="lib"
fi

### Linker Function ###
# Takes three arguments:
#   - Full source path (e.g. /usr/lib/postgresql-9.0/lib/lib*.{a,so})
#   - Full target directory path (e.g. /usr/bin)
#   - Suffix (Optional) (e.g 84 to make /usr/bin/psql84)
linker() {
	local source_dir=$1
	local target_dir=$2
	local suffix=$3
	local link_source

	for link_source in $(eval ls ${source_dir} 2> /dev/null) ; do
		local link_target="${target_dir%/}/$(basename ${link_source})${suffix}"

		# For good measure, remove target before creating the symlink
		[ -h ${link_target} ] && rm -f ${link_target}
		[ -e ${link_target} ] && die -q "The target '${link_target}' still exists and could not be removed!"

		ln -s ${link_source} ${link_target} || die -q "Unable to create link!"
		echo ${link_target} >> ${E_PATH}/active.links${suffix}
	done
}

### Unlinker Function ###
# Takes one argument:
#   - Full path to active links file (e.g. /etc/eselect/postgresql/active.links)
unlinker() {
	local active_link_file=$1
	if [ -r ${active_link_file} ] ; then
		local active_links=($(<${active_link_file}))
		for (( i=0; $i < ${#active_links[@]}; i++ )) ; do
			[ -h ${active_links[$i]} ] && rm -f ${active_links[$i]}
			[ -e ${active_links[$i]} ] && die -q "The target '${active_links[$i]}' still exists and could not be removed!"
		done

		rm -f ${active_link_file}
	fi
}

### Get Slots Function ###
# Find all available slots in the preferred LIBDIR and return them.
get_slots() {
	echo $(ls -dv ${B_PATH}/${LIBDIR}/postgresql-* 2> /dev/null | sed -re 's#^.+-##')
}

### List Action ###
describe_list() {
	echo "List available PostgreSQL slots."
}

do_list() {
	write_list_start "Available PostgreSQL Slots"

	if $(is_output_mode brief) ; then
		echo $(get_slots)
	else
		local slot
		for slot in $(get_slots) ; do
			local postgres_ebuilds=""
			local src
			for src in ${E_PATH}/slots/${slot}/{server,service,base,docs} ; do
				[ -r ${src} ] && source ${src}
			done

			case "${slot}" in
				"${ACTIVE_SLOT}" ) write_kv_list_entry "$(highlight_marker ${slot})" "${postgres_ebuilds//postgresql-/}";;
				*                ) write_kv_list_entry "${slot}" "${postgres_ebuilds//postgresql-/}";;
			esac
		done

		[ -z "${postgres_ebuilds}" ] && write_warning_msg "No slots available."
	fi
}

### Show Action ###
describe_show() {
	echo "Show which slot is currently active."
}

do_show() {
	echo ${ACTIVE_SLOT}
}

### Show Service Action ###
# Here for backwards compatibility with ebuilds
describe_show-service()  {
	echo "Deprecated. For ebuild use; returns no useful information."
}

do_show-service() {
	echo 1
}

### Set Action ###
describe_set() {
	echo "Create symbolic links for PostgreSQL libraries and applications."
}

do_set() {
	local SLOT=$1
	if [ ! -d ${B_PATH}/${LIBDIR}/postgresql-${SLOT} ] ; then
		die -q "Not a valid slot."
	fi

	echo "Setting ${SLOT} as the default installation..."

	# Remove the active links to start a fresh list
	echo -ne "\tRemoving old links..."
	unlinker ${E_PATH}/active.links
	echo "done."

	echo -ne "\tGenerating new links..."
	# Sources and targets for header files
	local sources=(
		${B_PATH}/include/postgresql-${SLOT}
		${B_PATH}/include/postgresql-${SLOT}/libpq-fe.h
		${B_PATH}/include/postgresql-${SLOT}/pg_config_manual.h
		${B_PATH}/include/postgresql-${SLOT}/libpq
		${B_PATH}/include/postgresql-${SLOT}/postgres_ext.h
	)
	local targets=(
		${B_PATH}/include/postgresql
		${B_PATH}/include/libpq-fe.h
		${B_PATH}/include/pg_config_manual.h
		${B_PATH}/include/libpq
		${B_PATH}/include/postgres_ext.h
	)
	# The linker function cannot accomadate this special purpose.
	local i
	for (( i=0; $i < ${#sources[@]}; i++ )) ; do
		# Remove target before creating the symlink
		rm -f ${targets[$i]}

		# Check if link_target still exists
		[ -e ${targets[$i]} ] && die -q "The target '${targets[$i]}' exists and could not be removed!"

		ln -s ${sources[$i]} ${targets[$i]} || die -q "Unable to create link!"
		echo ${targets[$i]} >> ${E_PATH}/active.links
	done

	# Link modules to /usr/lib{,lib32,lib64}/
	local x
	for x in ${LIB_LIST} ; do
		if [ -d ${B_PATH}/${x}/postgresql-${SLOT}/${x} ] ; then
			# 'linker' function doesn't work for linking directories.
			# Default lib path
			ln -s ${B_PATH}/${x}/postgresql-${SLOT}/${x} ${B_PATH}/${x}/postgresql
			echo ${B_PATH}/${x}/postgresql >> ${E_PATH}/active.links
			# Linker works for files
			linker "${B_PATH}/${x}/postgresql-${SLOT}/${x}/lib*.{a,dylib,so}" "${B_PATH}/${x}"
		fi
	done

	# Link binaries to /usr/bin/
	linker "${B_PATH}/${LIBDIR}/postgresql-${SLOT}/bin/*" "${B_PATH}/bin"

	# Default share path
	ln -s ${B_PATH}/share/postgresql-${SLOT} ${B_PATH}/share/postgresql
	echo ${B_PATH}/share/postgresql >> ${E_PATH}/active.links

	echo ${SLOT} > ${E_PATH}/active
	echo "done."
	echo -e "\033[1mSetting ${SLOT} as default was successful!\033[0m"
}

### Unset Action ###
describe_unset() {
	echo "Remove symbolic links."
}

do_unset() {
	local SLOT=$1
	if [ "${SLOT}" = "${ACTIVE_SLOT}" ] ; then
		echo -n "Unsetting ${SLOT} as the default installation..."
		unlinker ${E_PATH}/active.links
		rm -f ${E_PATH}/active
		echo "done."
	else
		echo "Inactive slot selected. No work to do."
	fi
}

### Reset Action ###
describe_reset() {
	echo "Recreate symbolic links for currently active slot."
}

do_reset() {
	[ "${ACTIVE_SLOT}" = "(none)" ] && die -q "No active slot to reset."
	do_unset ${ACTIVE_SLOT}
	do_set ${ACTIVE_SLOT}
}

### Update Action ###
describe_update() {
	echo "Refreshes all symbolic links managed by this module"
}

do_update() {
	# Check for files managed by postgresql.eselect before 1.0
	[ -h /etc/eselect/postgresql/active ] && ACTIVE_SLOT="$(basename $(canonicalise /etc/eselect/postgesql/active))"
	# Remove service file outright.
	[ -h /etc/eselect/postgresql/service ] && rm -f /etc/eselect/postgresql/service

	local slots=($(get_slots))
	local index=${#slots[@]}

	# In case all slots have been unmerged
	if [ ${index} -eq 0 ] ; then
		write_warning_msg "No slots found!"
		write_warning_msg "Removing files (Control-C to abort) in..."
		local i=6
		while [ $[i--] -gt 0 ] ; do
			echo -n " $i"
			sleep 1
		done
		for sym_links in ${E_PATH}/active.links* ; do
			unlinker ${sym_links}
		done
		rm -f ${E_PATH}/active
		rm -f ${ENV_FILE}
		do_action env update &> /dev/null
		echo "Done!"
		return 0
	fi

	# Reset, otherwise set the highest slot available.
	if [[ ${slots[@]} =~ ${ACTIVE_SLOT} ]] ; then
		do_reset
	else
		# best_version doesn't work here as pkg_postrm runs before the world
		# file is updated, thereby returning a false positive.
		do_set ${slots[$index-1]}
	fi

	echo -en "\nCleaning out old links before refreshing..."
	local sym_links
	for sym_links in ${E_PATH}/active.links?* ; do
		unlinker ${sym_links}
	done
	echo "done."

	# Update paths to libs and docs
	local ldpath
	local x
	for x in ${LIB_LIST} ; do
		[ -h ${B_PATH}/${x}/postgresql ] && ldpath+="${B_PATH}/${x}/postgresql:"
	done
	ldpath="${ldpath%:}"
	local manpath="${B_PATH}/share/postgresql/"
	while [ $[--index] -gt -1 ] ; do
		local curslot="${slots[$index]}"
		echo -n "Refreshing symbolic links for ${curslot} applications (like /usr/bin/psql${curslot//.})..."
		for x in ${LIB_LIST} ; do
			local lib_path="${B_PATH}/${x}/postgresql-${curslot}/${x}/"
			[ -d ${lib_path} ] && ldpath+=":${lib_path}"
		done
		local share_path="${B_PATH}/share/postgresql-${curslot}/"
		[ -d ${share_path} ] && manpath+=":${share_path}"
		linker "${B_PATH}/${LIBDIR}/postgresql-${curslot}/bin/*" "${B_PATH}/bin" "${curslot//.}"
		echo "done."
	done

	# Remove environment files that have been generated by the ebuilds
	rm -f ${ENV_FILE}-*

	store_config ${ENV_FILE} LDPATH "${ldpath}"
	store_config ${ENV_FILE} MANPATH "${manpath}"
	do_action env update &> /dev/null
}