#!/bin/bash

declare    PMODULES_MODULEFILES_DIR='modulefiles'
declare    PMODULES_CONFIG_DIR='config'
declare    PMODULES_VERSION='@PMODULES_VERSION@'
declare -A GroupDepths=()
declare -A Subcommands=()
declare -A Options=()
declare -A Help=()

declare -a Overlays=()
declare -A OverlayInfo
declare -a UsedOverlays
declare -A Dir2OverlayMap

declare -r ol_normal='n'
declare -r ol_hiding='h'
declare -r ol_replacing='r'


# initialize help text of 'module --version'
Help['version']="
Pmodules @PMODULES_VERSION@
using Tcl Environment Modules
VERSION = @MODULES_VERSION@
"

#
# display help text for command given in $1
#
print_help() {
        echo -e "${Help[$1]}" 1>&2
        std::die 1
}

#
# compute depth of modulefile directory.
#
# Args:
#       $1: absolute path of a modulefile directory
#
compute_group_depth () {
	local -n result="$1"
        local -r dir="$2"
	if [[ ! -d "${dir}" ]]; then
		${mkdir} -p "${dir}" || \
			std::die 1 "Cannot create directory -- ${dir}"
	fi
        local group=${dir%/*}
        local group=${group##*/}
	result=$(${find} "${dir}" -depth \( -type f -o -type l \) \
                          -printf "%d" -quit 2>/dev/null)
	(( result-=2 ))
	# if a group doesn't contain a modulefile, depth is negativ
	# :FIXME: better solution?
	(( result < 0 )) && (( result = 0 ))
}

#
# (Re-)Scan available groups in given overlays and compute group depth's
#
# Args:
#       $@: overlay names
#
scan_groups () {
        local    ol
	local depth
        for ol in "$@"; do
		local mod_root="${OverlayInfo[${ol}:mod_root]}"
	        local dir
	        for dir in ${mod_root}/*/${PMODULES_MODULEFILES_DIR}; do
			local group="${dir%/*}"
			group="${group##*/}"
			if [[ ! -v GroupDepths[${group}] ]]; then
				compute_group_depth depth "${dir}"
				GroupDepths[$group]=${depth}
			fi
			Dir2OverlayMap[${dir%/${PMODULES_MODULEFILES_DIR}*}]="${ol}"
	        done
        done
}

pm::read_config(){
	local -a config_files=()
	
	_get_config_files(){
		#
		# return array with overlay configuration files
		#
		# Args:
		#	$1	[upvar] result
		local -n fnames="$1"

		# user defined via environment variable
		if [[ -v PMODULES_OVERLAYS_DEF ]]; then
			test -r "${PMODULES_OVERLAYS_DEF}" || \
				std::die 3 \
					 "%s -- %s" \
					 "overlay definition file is not readable" \
					 "$_"
			fnames+=("${PMODULES_OVERLAYS_DEF}")
		fi

		# user defined
		if [[ -r "${HOME}/.Pmodules/Pmodules.yaml" ]]; then
			fnames+=("${HOME}/.Pmodules/Pmodules.yaml")
		fi

		# system config file
		test -r "${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml" || \
			std::die 3 \
				 "%s %s -- %s" \
				 "base overlay definition file" \
				 "does not exist or is not readable" \
				 "$_"
		
		fnames+=("${PMODULES_HOME%%/Tools*}/config/Pmodules.yaml")
	}

	_get_ol_names(){
		#
		# get the names of all overlays
		#
		local -n fnames="$1"
		${yq} -Ne eval-all '. as $item ireduce ({}; . *+ $item) |.Overlays|keys()' \
		      "${fnames[@]}" | awk '{print $2}'
	}

	_get_install_root(){
		local -n fnames="$1"
		local -- _ol="$2"
		local -- _tmp
		_tmp=$(${yq} -Ne eval-all ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${_ol}.install_root" \
			      "${fnames[@]}" 2>/dev/null) || return 1
		echo "${_tmp}" | envsubst
	}

	_get_modulefiles_root(){
		local -n fnames="$1"
		local -- _ol="$2"
		local -- _tmp
		_tmp=$(${yq} -Ne eval-all ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${_ol}.modulefiles_root" \
			      "${fnames[@]}" 2>/dev/null) || return 1
		echo "${_tmp}" | envsubst
	}

	_get_excludes(){
		local -n fnames="$1"
		local -- _ol="$2"
		${yq} -Ne eval-all ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${_ol}.excludes[]" \
		      "${fnames[@]}" 2>/dev/null
	}

	_get_type(){
		local -n fnames="$1"
		local -- _ol="$2"
		${yq} -Ne eval-all ". as \$item ireduce ({}; . *+ \$item) |.Overlays.${_ol}.type" \
		      "${fnames[@]}" 2>/dev/null
	}

	_join_array() {
		local IFS="$1"
		shift
		echo -n "$*"
	}

	_get_config_files config_files

	eval $(std::parse_yaml "${config_files[-1]}" 'cfg_')
	[[ -v cfg_DefaultGroups ]] && DefaultGroups="${cfg_DefaultGroups}"
	[[ -v cfg_DefaultReleaseStages ]] && DefaultReleaseStages="${cfg_DefaultReleaseStages}"
	[[ -v cfg_ReleaseStages ]] && ReleaseStages="${cfg_ReleaseStages}"
	[[ -v cfg_SysCollectionsDir ]] && SysCollectionsDir="${cfg_SysCollectionsDir}"
	unset ${!cfg_*}

	#
	# loop over all overlays and save config in OverlayInfo and Dir2OverlayMap
	#
	# - at least install_root must be specified
	# - modulefiles_root default so install_root
	# - the type defaults to ${ol_normal}
	#
	Overlays=( $(_get_ol_names config_files) )
	local ol=''
	local install_root=''
	local modulefiles_root=''
	local type=''
	local excludes=()
	for ol in "${Overlays[@]}"; do
		# install_root
		install_root=$(_get_install_root config_files "${ol}") || \
			std::die 3 \
				 "Install_root missing for overlay -- ${ol}"
		install_root=$(envsubst <<< "${install_root}")
		[[ -d ${install_root} ]] || \
			std::die 3 \
				 "Invalid installation root directory for overlay '${ol}' -- ${install_root}"
		OverlayInfo[${ol}:inst_root]="${install_root}"

		# modulefiles_root
		modulefiles_root=$(_get_modulefiles_root config_files "${ol}") || \
			modulefiles_root=${install_root}
		[[ -d ${modulefiles_root} ]] || \
			std::die 3 \
				 "Invalid modulefiles root directory for overlay '${ol}' -- ${modulefiles_root}"
		OverlayInfo[${ol}:mod_root]="${modulefiles_root}"
		Dir2OverlayMap[${modulefiles_root}]="${ol}"

		# type
		type=$(_get_type config_files "${ol}") || \
			type=${ol_normal}
		case ${type} in
			${ol_normal} | ${ol_replacing} | ${ol_hiding} )
				:
				;;
			* )
				std::die 3 "Invalid type for overlay '${ol}' -- ${type}"
				;;
		esac
		OverlayInfo[${ol}:type]="${type}"

		# excludes
		excludes=$(_get_excludes config_files "${ol}") || \
			excludes=()
		OverlayInfo[${ol}:excludes]=$(_join_array ':' "${excludes[@]}")

		# mark as unused
		OverlayInfo[${ol}:used]='no'
	done
}


# Local Variables:
# mode: sh
# sh-basic-offset: 8
# tab-width: 8
# End:
