# Auxiliary functions for menu system for custom build system
# Copyright (c) 2002 Serge van den Boom
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

MENU_ITEM_TYPES="MENU CHOICE INPUT CHECK"

MENU_ITEM_TYPE_MENU_INIT=menu_init_menu
MENU_ITEM_TYPE_MENU_PRINT_VALUE=menu_print_value_menu
MENU_ITEM_TYPE_MENU_HANDLER=menu_handle_menu
MENU_ITEM_TYPE_MENU_SAVE=menu_save_menu
MENU_ITEM_TYPE_MENU_PROCESS=menu_process_menu

MENU_ITEM_TYPE_CHOICE_INIT=menu_init_choice
MENU_ITEM_TYPE_CHOICE_PRINT_VALUE=menu_print_value_choice
MENU_ITEM_TYPE_CHOICE_HANDLER=menu_handle_choice
MENU_ITEM_TYPE_CHOICE_SAVE=menu_save_choice
MENU_ITEM_TYPE_CHOICE_PROCESS=menu_process_choice

MENU_ITEM_TYPE_INPUT_INIT=menu_init_input
MENU_ITEM_TYPE_INPUT_PRINT_VALUE=menu_print_value_input
MENU_ITEM_TYPE_INPUT_HANDLER=menu_handle_input
MENU_ITEM_TYPE_INPUT_SAVE=menu_save_input
MENU_ITEM_TYPE_INPUT_PROCESS=menu_process_input

MENU_ITEM_TYPE_CHECK_INIT=menu_init_check
MENU_ITEM_TYPE_CHECK_PRINT_VALUE=menu_print_value_check
MENU_ITEM_TYPE_CHECK_HANDLER=menu_handle_check
MENU_ITEM_TYPE_CHECK_SAVE=menu_save_check
MENU_ITEM_TYPE_CHECK_PROCESS=menu_process_check

# Description: Check if a string can be used as a path.
#              No checks are done if the path really exists.
# Arguments:   $1 - the string to check
# Returns:     0 if the string makes a valid path
#              1 if the string doesn't make a valid path
# Outputs:     the path, possibly modified
validate_path() {
	if [ "$1" = "/" ]; then
		echo "/"
		return
	fi
	cat << EOF
${1%/}
EOF
	return 0
}

# Description: Initialise a menu, setting unset values to default
# Arguments:   $1 - the type of menu item
#              $2 - the name of the menu item
menu_init() {
	local INIT_FUN

	eval INIT_FUN="\$MENU_ITEM_TYPE_$1_INIT"
	"$INIT_FUN" "$2"
}

# Description: Initialise this menu
# Arguments:   $1 - the name of the menu
menu_init_menu() {
	local MENU TEMP_ITEMS ITEM TEMP_TYPE

	MENU="$1"
	eval TEMP_ITEMS="\$MENU_${MENU}_ITEMS"
	for ITEM in $TEMP_ITEMS; do
		eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
		if [ -z "$TEMP_TYPE" ]; then
			echo "Fatal: \$MENU_${MENU}_ITEM_${ITEM}_TYPE is not defined!" >&2
			exit 1
		fi
		menu_init "$TEMP_TYPE" "$ITEM"
	done
}

# Description: Check if this choice is available
# Arguments:   $1 - the name of the choice menu
#              $2 - the name of the choice
# Returns:     0 - if the choice is available
#              1 - if the choice is not available
menu_have_choice() {
	local MENU CHOICE TEMP_VALID TEMP_PRECOND
	MENU="$1"
	CHOICE="$2"
	eval TEMP_VALID="\$CHOICE_${MENU}_OPTION_${CHOICE}_VALID"
	if [ -n "$TEMP_VALID" ]; then
		return "$TEMP_VALID"
	fi
	eval TEMP_PRECOND="\$CHOICE_${MENU}_OPTION_${CHOICE}_PRECOND"
	if [ -z "$TEMP_PRECOND" ] || $TEMP_PRECOND; then
		eval "CHOICE_${MENU}_OPTION_${CHOICE}_VALID"=0
		return 0
	fi
	eval "CHOICE_${MENU}_OPTION_${CHOICE}_VALID"=1
	return 1
}

# Description: Initialise this choice menu
# Arguments:   $1 - the name of the choice menu
menu_init_choice() {
	local MENU TEMP_VALUE TEMP_DEFAULT TEMP_OPTIONS OPTION TEMP_TITLE
	MENU="$1"
	eval TEMP_VALUE="\$CHOICE_${MENU}_VALUE"
	eval TEMP_DEFAULT="\$CHOICE_${MENU}_DEFAULT"
	eval TEMP_OPTIONS="\$CHOICE_${MENU}_OPTIONS"
	for OPTION in $TEMP_VALUE $TEMP_DEFAULT $TEMP_OPTIONS; do
		if menu_have_choice "$MENU" "$OPTION"; then
			eval CHOICE_${MENU}_VALUE="$OPTION"
			eval CHOICE_${MENU}_OLD_VALUE="$OPTION"
			return
		fi
	done
	eval TEMP_VALUE="\$CHOICE_${MENU}_TITLE"
	echo "Error: No option for '$TEMP_VALUE' is available for your system."
	exit 1
}

# Description: Initialise this input menu
# Arguments:   $1 - the name of the input menu
menu_init_input() {
	local MENU TEMP_VALUE
	MENU="$1"
	eval TEMP_VALUE="\$INPUT_${MENU}_VALUE"
	if [ -z "$TEMP_VALUE" ]; then
		eval TEMP_VALUE="\$INPUT_${MENU}_DEFAULT"
		eval INPUT_${MENU}_VALUE="\$TEMP_VALUE"
	fi
	eval INPUT_${MENU}_OLD_VALUE="\$TEMP_VALUE"
}

# Description: Initialise this checkbox
# Arguments:   $1 - the name of the checkbox
menu_init_check() {
	local CHECKBOX TEMP_VALUE TEMP_DEFAULT
	CHECKBOX="$1"

	eval TEMP_VALUE="\$CHECK_${CHECKBOX}_VALUE"
	eval TEMP_DEFAULT="\$CHECK_${CHECKBOX}_DEFAULT"

	# Make sure the default is either 'CHECKED' or 'UNCHECKED'
	case "$TEMP_DEFAULT" in
		yes|Yes|YES|checked|Checked|CHECKED|1|true|True|TRUE)
			TEMP_DEFAULT=CHECKED
			;;
		*)
			TEMP_DEFAULT=UNCHECKED
			;;
	esac
	eval CHECK_${CHECKBOX}_DEFAULT="\$TEMP_DEFAULT"

	if [ -z "$TEMP_VALUE" ]; then
		TEMP_VALUE="$TEMP_DEFAULT"
		eval CHECK_${CHECKBOX}_VALUE="\$TEMP_DEFAULT"
	fi
	eval CHECK_${CHECKBOX}_OLD_VALUE="\$TEMP_VALUE"
}

# Description: Print the string describing the value of a menu item.
# Arguments:   $1 - the type of menu item
#              $2 - the name of the menu item
# Outputs:     The string describing the value of the menu item
menu_print_value() {
	local PRINT_VALUE

	eval PRINT_VALUE="\$MENU_ITEM_TYPE_$1_PRINT_VALUE"
	"$PRINT_VALUE" "$2"
}

# Description: Print the string describing the value of this menu
# Arguments:   $1 - the name of the menu item
# Outputs:     The string describing the value of this menu
menu_print_value_menu() {
	echo "[...]"
}

# Description: Print the string describing the value this choice menu
# Arguments:   $1 - the name of the choice menu item
# Outputs:     The string describing the value of this choice menu
menu_print_value_choice() {
	local TEMP_VALUE TEMP_TITLE
	eval TEMP_VALUE="\$CHOICE_$1_VALUE"
	eval TEMP_TITLE=\"\$CHOICE_$1_OPTION_${TEMP_VALUE}_TITLE\"
	cat << EOF
$TEMP_TITLE
EOF
}

# Description: Print the value of this input menu
# Arguments:   $1 - the name of the input menu item
# Outputs:     The value of the given input menu
menu_print_value_input() {
	local TEMP_VALUE
	eval TEMP_VALUE="\$INPUT_$1_VALUE"
	cat << EOF
$TEMP_VALUE
EOF
}

# Description: Print the value of this checkbox
# Arguments:   $1 - the name of the checkbox item
# Outputs:     The value of the given checkbox
menu_print_value_check() {
	local TEMP_VALUE TEMP_DEFAULT TEMP_FIXED RESULT EXTRA
	eval TEMP_VALUE="\$CHECK_$1_VALUE"
	eval TEMP_DEFAULT="\$CHECK_$1_DEFAULT"
	eval TEMP_FIXED="\$CHECK_$1_FIXED"

	if [ "$TEMP_VALUE" = "CHECKED" ]; then
		RESULT="Yes"
	else
		RESULT="No"
	fi

	EXTRA=""
	if [ "$TEMP_VALUE" = "$TEMP_DEFAULT" ]; then
		EXTRA="default"
	fi

	if [ "$TEMP_FIXED" = "TRUE" ]; then
		EXTRA="${EXTRA:+$EXTRA, }unchangable"
	fi

	RESULT="$RESULT${EXTRA:+ ($EXTRA)}"
	echo "$RESULT"
}

# Description: Print the string describing the value of a menu item.
# Arguments:   $1 - the type of menu item
#              $2 - the name of the menu item
# Outputs:     The string describing the value of the menu item
menu_handle() {
	local HANDLER

	eval HANDLER="\$MENU_ITEM_TYPE_$1_HANDLER"
	"$HANDLER" "$2"
}

# Description: Process a menu-type menu item
# Arguments:   $1 - The name of the menu
menu_handle_menu() {
	local TEMP_ITEMS I CHOICE NUM_ITEMS TEMP_TYPE MENU ITEM TEMP_TITLE \
			TEMP_TEXT
	eval TEMP_ITEMS="\$MENU_$1_ITEMS"
	eval TEMP_TEXT="\$MENU_$1_TEXT"
	
	MENU="$1"
	while :; do
		echo
		eval TEMP_TITLE="\$MENU_${MENU}_TITLE"
		cat << EOF
  $ANSI_BOLD-= $TEMP_TITLE =-$ANSI_NORMAL
EOF

		if [ -n "$TEMP_TEXT" ]; then
			cat << EOF
$TEMP_TEXT
EOF
		fi
		
		# Count the number of options
		I=0
		for ITEM in $TEMP_ITEMS; do
			I=$(($I + 1))
		done
		NUM_ITEMS="$I"

		I=0
		for ITEM in $TEMP_ITEMS; do
			I=$(($I + 1))
			eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
			eval TEMP_TITLE="\$${TEMP_TYPE}_${ITEM}_TITLE"
			printf "  %${#NUM_ITEMS}i. %-36s %s\n" $I "$TEMP_TITLE" \
					"$(menu_print_value $TEMP_TYPE $ITEM)"
		done

		echo
		echo "Press a number plus <ENTER> if you want to change something, "
		$ECHON "or just <ENTER> if everything is ok: "
		read CHOICE

		# Check if the choice was empty
		if [ -z "$CHOICE" ]; then
			# We're done
			return
		fi

		# Check if what the user entered was a number
		egrep '^[0-9]+$' << EOF > /dev/null
$CHOICE
EOF
		if [ $? -ne 0 ]; then
			echo "Invalid choice."
			continue
		fi

		# Check if the number the user entered if valid
		if [ "$CHOICE" -lt 1 -o "$CHOICE" -gt "$NUM_ITEMS" ]; then
			echo "Invalid choice."
			continue
		fi

		# Now look up the choice
		I=0
		for ITEM in $TEMP_ITEMS; do
			I=$(($I + 1))
			if [ "$I" -eq "$CHOICE" ]; then
				eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
				menu_handle "$TEMP_TYPE" "$ITEM"
				break
			fi
		done
	done
}

# Description: Process a choice-type menu item
# Arguments:   $1 - The name of the menu
menu_handle_choice() {
	local TEMP_OPTIONS I CHOICE NUM_OPTIONS TEMP_TYPE MENU OPTION \
			TEMP_VALUE TEMP_TITLE SELECTED TEMP_TEXT
	eval TEMP_OPTIONS="\$CHOICE_$1_OPTIONS"
	eval TEMP_TEXT="\$CHOICE_$1_TEXT"
	
	MENU="$1"
	while :; do
		echo
		eval TEMP_TITLE="\$CHOICE_${MENU}_TITLE"
		cat << EOF
  $ANSI_BOLD-= $TEMP_TITLE =-$ANSI_NORMAL
EOF
		if [ -n "$TEMP_TEXT" ]; then
			cat << EOF
$TEMP_TEXT
EOF
		fi

		eval TEMP_VALUE="\$CHOICE_${MENU}_VALUE"

		# Check in advance which options are present, so that that
		# is echoed before the menu is printed.
		# menu_have_choice caches results.
		# Also, count the number of options
		I=0
		for OPTION in $TEMP_OPTIONS; do
			I=$(($I + 1))
			menu_have_choice "$MENU" "$OPTION"
		done
		NUM_OPTIONS="$I"

		I=0
		for OPTION in $TEMP_OPTIONS; do
			I=$(($I + 1))
			eval TEMP_TITLE="\$CHOICE_${MENU}_OPTION_${OPTION}_TITLE"
			if [ "$TEMP_VALUE" = "$OPTION" ]; then
				SELECTED="-->"
			else
				SELECTED="   "
			fi
			if menu_have_choice "$MENU" "$OPTION"; then
				printf "  %${#NUM_OPTIONS}i. %s %s\n" "$I" "$SELECTED" \
						"$TEMP_TITLE"
			else
				printf "  %-${#NUM_OPTIONS}s  %s (N/A) %s\n" "-" \
						"$SELECTED" "$TEMP_TITLE"
			fi
		done

		echo
		echo "Select the option you want by typing a number plus <ENTER>"
		$ECHON "or just <ENTER> if everything is ok: "
		read CHOICE
		echo

		# Check if the choice was empty
		if [ -z "$CHOICE" ]; then
			# We're done
			return
		fi

		# Check if what the user entered was a number
		egrep '^[0-9]+$' << EOF > /dev/null
$CHOICE
EOF
		if [ $? -ne 0 ]; then
			echo "Invalid choice."
			continue
		fi

		# Check if the number the user entered if valid
		if [ "$CHOICE" -lt 1 -o "$CHOICE" -gt "$NUM_ITEMS" ]; then
			echo "Invalid choice."
			continue
		fi

		# Now look up the choice
		I=0
		for OPTION in $TEMP_OPTIONS; do
			I=$(($I + 1))
			if [ "$I" -eq "$CHOICE" ]; then
				if menu_have_choice "$MENU" "$OPTION"; then
					eval "CHOICE_${MENU}_VALUE"="$OPTION"
					return
				else
					echo "That option is unavailable on your system."
				fi
			fi
		done
	done
}

# Description: Process an input-type menu item
# Arguments:   $1 - The name of the menu
menu_handle_input() {
	local ITEM TEMP_TITLE TEMP_VALUE TEMP_DEFAULT NEW_VALUE \
			TEMP_VALIDATOR TEMP_TEXT

	ITEM="$1"
	eval TEMP_TITLE="\$INPUT_${ITEM}_TITLE"
	eval TEMP_TEXT="\$INPUT_${ITEM}_TEXT"

	while :; do
		echo
		cat << EOF
  $ANSI_BOLD-= $TEMP_TITLE =-$ANSI_NORMAL
EOF

		if [ -n "$TEMP_TEXT" ]; then
			cat << EOF
$TEMP_TEXT
EOF
		fi

		eval TEMP_VALUE="\$INPUT_${ITEM}_VALUE"
		eval TEMP_DEFAULT="\$INPUT_${ITEM}_DEFAULT"
		echo "  Default value: $TEMP_DEFAULT"
		echo "  Current value: $TEMP_VALUE"
		$ECHON "  New value:     "
		read NEW_VALUE
		
		# If no new value is entered, keep the old one.
		if [ -z "$NEW_VALUE" ]; then
			return
		fi

		# If a validator function is present, validate the new value
		eval TEMP_VALIDATOR="\$INPUT_${ITEM}_VALIDATOR"
		if [ -n "$TEMP_VALIDATOR" ]; then
			NEW_VALUE=`$TEMP_VALIDATOR "$NEW_VALUE"`
			if [ $? -ne 0 ]; then
				echo "Invalid value"
				continue
			fi
		fi
		break
	done
	eval "INPUT_${ITEM}_VALUE"=\"\$NEW_VALUE\"
}

# Description: Process an checkbox-type menu item
# Arguments:   $1 - The name of the checkbox
menu_handle_check() {
	local CHECKBOX OLD_VALUE NEW_VALUE TEMP_FIXED

	CHECKBOX="$1"
	eval TEMP_FIXED="\$CHECK_${CHECKBOX}_FIXED"

	if [ "$TEMP_FIXED" = "TRUE" ]; then
		# Unchangable
		return;
	fi

	eval OLD_VALUE="\$CHECK_${CHECKBOX}_VALUE"
	if [ "$OLD_VALUE" = "CHECKED" ]; then
		NEW_VALUE="UNCHECKED"
	else
		NEW_VALUE="CHECKED"
	fi
	eval "CHECK_${CHECKBOX}_VALUE"=\"\$NEW_VALUE\"
}

# Description: echo the current state of a menu item in a form that can be
#              executed to restore the state.
# Arguments:   $1 - the type of menu item
#              $2 - the name of the menu item
# Outputs:     The string describing the value of the menu item
menu_save() {
	local SAVE_FUN
	eval SAVE_FUN="\$MENU_ITEM_TYPE_$1_SAVE"
	"$SAVE_FUN" "$2"
}

# Description: echo the current state of a menu in a form that can be
#              executed to restore the state.
# Arguments:   $1 - the name of the menu
# Outputs:     The string describing the value of the menu
menu_save_menu() {
	local MENU TEMP_ITEMS ITEM TEMP_TYPE

	MENU="$1"
	eval TEMP_ITEMS="\$MENU_${MENU}_ITEMS"
	for ITEM in $TEMP_ITEMS; do
		eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
		menu_save "$TEMP_TYPE" "$ITEM"
	done	
}

# Description: echo the current state of a choice menu in a form that can be
#              executed to restore the state.
# Arguments:   $1 - the name of the choice menu
# Outputs:     The string describing the value of the choice menu
menu_save_choice() {
	local MENU TEMP_VALUE
	MENU="$1"
	eval TEMP_VALUE="\$CHOICE_${MENU}_VALUE"
	cat << EOF
CHOICE_${MENU}_VALUE='$TEMP_VALUE'
EOF
}

# Description: echo the current state of an input menu in a form that can be
#              executed to restore the state.
# Arguments:   $1 - the name of the input menu
# Outputs:     The string describing the value of the input menu
menu_save_input() {
	local MENU TEMP_VALUE
	MENU="$1"
	eval TEMP_VALUE="\$INPUT_${MENU}_VALUE"
	cat << EOF
INPUT_${MENU}_VALUE='$TEMP_VALUE'
EOF
}

# Description: echo the current state of an check box in a form that can be
#              executed to restore the state.
# Arguments:   $1 - the name of the checkbox
# Outputs:     The string describing the value of the checkbox
menu_save_check() {
	local CHECKBOX TEMP_VALUE
	CHECKBOX="$1"
	eval TEMP_VALUE="\$CHECK_${CHECKBOX}_VALUE"
	cat << EOF
CHECK_${CHECKBOX}_VALUE='$TEMP_VALUE'
EOF
}

# Description: Perform the actions associated with the choice made for a
#              menu items.
# Arguments:   $1 - the type of menu item
#              $2 - the name of the menu item
menu_process() {
	local PROCESS_FUN
	eval PROCESS_FUN="\$MENU_ITEM_TYPE_$1_PROCESS"
	"$PROCESS_FUN" "$2"
}

# Description: Perform the actions associated with the chosen menu items
#              for a menu.
# Arguments:   $1 - the name of the menu
menu_process_menu() {
	local MENU TEMP_ITEMS ITEM TEMP_TYPE

	MENU="$1"
	eval TEMP_ITEMS="\$MENU_${MENU}_ITEMS"
	for ITEM in $TEMP_ITEMS; do
		eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
		menu_process "$TEMP_TYPE" "$ITEM"
	done	
}

# Description: Perform the actions associated with the choice made for
#              a choice menu.
# Arguments:   $1 - the name of the choice menu
menu_process_choice() {
	local MENU TEMP_VALUE TEMP_ACTION
	MENU="$1"
	eval TEMP_VALUE="\$CHOICE_${MENU}_VALUE"
	eval TEMP_ACTION="\$CHOICE_${MENU}_OPTION_${TEMP_VALUE}_ACTION"
	if [ -n "$TEMP_ACTION" ]; then
		$TEMP_ACTION
	fi
}

# Description: Perform the actions associated with the input menu.
# Arguments:   $1 - the name of the input menu
menu_process_input() {
	# Nothing to do
	:
}

# Description: Perform the actions associated with the status of a
#              check box.
# Arguments:   $1 - the name of the check box
menu_process_check() {
	local CHECKBOX TEMP_VALUE TEMP_ACTION
	CHECKBOX="$1"
	eval TEMP_VALUE="\$CHECK_${CHECKBOX}_VALUE"
	eval TEMP_ACTION="\$CHECK_${CHECKBOX}_OPTION_${TEMP_VALUE}_ACTION"
	if [ -n "$TEMP_ACTION" ]; then
		$TEMP_ACTION
	fi
}

# Description: Start processing a menu
# Arguments:   $1 - the type of the main menu
#              $2 - the name of the main menu
do_menu() {
	local MENU_TYPE START_MENU SAVE_FILE
	
	MENU_TYPE=$1
	START_MENU=$2

	menu_init "$MENU_TYPE" "$START_MENU"
	menu_handle "$MENU_TYPE" "$START_MENU"
	echo
}

# Description: Load the menu settings from file
# Arguments:   $1 - the type of the menu (currently ignored)
#              $2 - the name of the menu (currently ignored)
#              $3 - the name of the file to load from
# Returns:     0 - if the file was loaded successfully
#              1 - if the file did not exist
do_menu_load() {
	SAVE_FILE=$3

	if [ ! -e "$SAVE_FILE" ]; then
		return 1
	fi

	. "$SAVE_FILE"
	return 0
}

# Description: Save the menu settings to file
# Arguments:   $1 - the type of the menu
#              $2 - the name of the menu
#              $3 - the name of the file to save to
# Returns:     0 - if the file was saved successfully
do_menu_save() {
	MENU_TYPE=$1
	START_MENU=$2
	SAVE_FILE=$3

	echo "Saving choices..."
	menu_save "$MENU_TYPE" "$START_MENU" > "$SAVE_FILE"
	return 0
}




