#! /bin/bash
################################################################################
# Filename: ./src/base/include/qrm-opts
# 
# The contents of this file are subject to the Qlusters Public License
# Version 1.1 (the License); you may not use this file except in compliance
# with the License.
# You may obtain a copy of the License at
# http://www.openqrm.org/content/view/33/69/.
# 
# Software distributed under the License is distributed on an AS IS basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
# the specific language governing rights and limitations under the License.
# 
# The Original Code is openQRM.
# 
# The Initial Developer of the Original Code is Qlusters Corporation. Portions
# created by Qlusters Corporation are Copyright (C) 2003-2005. All Rights
# Reserved.
# 
# Contributor(s):
# 
# Description:
# 
# Comments:
################################################################################
QRM_OPTS_SEP="|"

OCCOUR_1='1'
OCCOUR_0_1='?'
OCCOUR_0_N='*'
OCCOUR_1_N='+'

function _qrm_opts_debug() {
	if [ -n "$QRM_OPTS_DEBUG" ]; then
		echo "$@"
	fi
}

function _qrm_opts_err() {
	echo "ERROR: $@" 1>&2
}

function qrm_opts_short_desc() {
	eval local "$@"
	local -a desc=(${short:+-$short} ${long:+--$long})
	IFS=','
	echo "${desc[*]}"
	unset IFS	
}

function _qrm_opts_opt_eval() {
	local init="exp= short= long= has_arg= occour=1 arg_desc=arg arg_valid=':' desc="
	echo "local $init $@"
}

function _qrm_opts_opt_string() {
	echo "exp=$exp short=$short long=$long has_arg=$has_arg occour=$occour arg_desc=$arg_desc arg_valid=$arg_valid desc=$desc"
}

# use:
# qrm_opts_parse opts_specs free_arg $@
# opts_specs:
#    a string of options specifications delimited with '|'
#    an option specification is a string containing variable declarations:
#	exp - an expression that will be evaluated when the option is 
#	      recognized. in the expression, $1 will be replaced by the 
#	      option's value, or 'true' if this represents a flag.
#	short - short name for the option
#       long - long name for the option
#       has_arg - whether this option has arguments
#       occour - alllowed number of occourances of this option. defaul 1.
#	arg_valid - expression that will be evaluated to validate a passed 
#		    argument value. $1 is replaced by the value. default is 
#		    ':' (which evaluates to true) 
#    the following variables are used for printing the option
#       arg_desc - description of the argument (if has_arg). default: arg) 
# 	desc - a description of the option

# free_arg: name of a variable to assign the index of the first free argument 
#    into. if empty, then free arguments are not allowed
function qrm_opts_parse() {
	# initialization of data structures
	local n_opts=$#
	local -a opts_arr;

	IFS="$QRM_OPTS_SEP"
	opts_arr=($1) && shift
	unset IFS
	local free_var=$1 && shift

	for (( i=0 ; i<${#opts_arr[*]} ; i++ )); do
		eval $(_qrm_opts_opt_eval  ${opts_arr[$i]})

		_qrm_opts_debug $(_qrm_opts_opt_string)

		if [ -n "$short" ]; then
			eval short_opts_arr_$short=$i
		fi
		if [ -n "$long" ]; then
			eval long_opts_arr_$long=$i
		fi
	
	done
	# end initialization

	# parsing start
	_qrm_opts_debug "Parsing: $@"
	local -a opt_cnt
	local opt_idx
	while [ $# -gt 0 ]; do
		case "$1" in
			--)
				_qrm_opts_debug "$1: end of opts"
				shift;
				break;
				;;
			--*)
				_qrm_opts_debug "$1: long"
				eval opt_idx=\$long_opts_arr_${1:2}
				;;	
			-*)
				_qrm_opts_debug "$1: short"
				eval opt_idx=\$short_opts_arr_${1:1}
				;;
			*)	
				
				_qrm_opts_debug "$1: end of opts"
				break;
				;;
		esac
                if [ -z "$opt_idx" ]; then
                	_qrm_opts_err "$1 is unknown"
			return 1
                fi
			
		eval $(_qrm_opts_opt_eval  ${opts_arr[$opt_idx]})	
	
		let "opt_cnt[$opt_idx]=${opt_cnt[$opt_idx]} + 1"
		 _qrm_opts_debug "$(_qrm_opts_opt_string) -> ${opt_cnt[$opt_idx]}"	
		shift
	
		if [ -z "$has_arg" ]; then
			eval ${exp//\$1/true}
			continue
		fi
		
		# value fetch & validation
		local short_desc=$(qrm_opts_short_desc ${opts_arr[$opt_idx]})
		
		local value=$1
		_qrm_opts_debug "value: $1"
		shift
		case "${value:0:2}" in
			-*)
				_qrm_opts_err "$value cannot be an argument to $short_desc" 1>&2
				return 1
				;;
			"\-")
				value=${value:1}
				;;
			'')
				_qrm_opts_err "no value for $short_desc" 1>&2
				return 1
				;;
		esac
		if [ -n "$arg_valid" ]; then
			if ! eval ${arg_valid//\$1/${value}}; then
				_qrm_opts_err "$value is not valid for $short_desc"
				return 1
			fi
		fi
		# end value validation
		
		# call expression to assign value	
		eval ${exp//\$1/${value}}

	done # end of parsing
	
	[[ -z "$free_var" && $# -gt 0 ]] && _qrm_opts_err "leftover args: $@" 1>&2 && return 1 
	[ -n "$free_var" ] && eval $free_var=$((n_opts - $#))

	# check occourances
	for (( i=0 ; i<${#opts_arr[*]} ; i++ )); do
		eval $(_qrm_opts_opt_eval ${opts_arr[$i]})
		cnt=${opt_cnt[$i]}
		local short_desc=$(qrm_opts_short_desc ${opts_arr[$i]})
		case $occour in
			"$OCCOUR_1")
				case $((cnt)) in
					0)
						_qrm_opts_err  "$short_desc is missing" 
						return 1
						;;
					1)
						break
						;;
					*)
						_qrm_opts_err "$short_desc occours $cnt times" 1>&2
						return 1
						;;
				esac
				;;
			"$OCCOUR_0_1")
				if [ $((cnt)) -gt 1 ]; then
                                        _qrm_opts_err "$short_desc occours $cnt times"
                                        return 1
                                fi
				;;
        		"$OCCOUR_1_N")
				if [ $((cnt)) == 0 ]; then	
					_qrm_opts_err "$short_desc is missing" 
					return 1
				fi
				;;
			"$OCCOUR_0_N")
				;;
			*)
				_qrm_opts_err "$short_desc: Invalid occourances specifier" 
				return 1
				;;
		esac
	done # end check occourances	
	
	
}

# use:
# eval set -- $(qrm_opts_std "$@")
function qrm_opts_std() {
	local -a args=()
	local i=0

	#separate parsing from handling. first normalize
	local arg=$1
	while [ $# -gt 0 ]; do
		case "${arg}" in
			--)
				break
				;;
			--*)
				args[$((i++))]=${arg%%=*}
				if [ -z "${arg##*=*}" ]; then		
					local value="${arg#*=}"
					args[$((i++))]="$value"
				fi
				shift
				arg=$1
				;;
			-*)
				if [ ${#arg} -eq 2 ]; then
					args[$((i++))]=$arg
					shift
					arg="$1"
				elif [ ${arg:2:1} == "=" ]; then
					args[$((i++))]=${arg%%=*}
					local value=${arg#*=}
					args[$((i++))]="$value"
					shift
					arg="$1"
				else
					args[$((i++))]=${arg:0:2}
					arg="-${arg:2}"
				fi
				;;
			*)
				args[$((i++))]="$arg"
				shift
				arg="$1"
				;;
		esac
	done
	for (( i=0 ; i<${#args[*]} ; i++ )); do
		echo -n "'${args[$i]}' "
	done
	while [ $# -gt 0 ]; do
		echo -n "'$1' "
		shift
	done	
	echo

}

# usage:
# qrm_opts_usage header opts_spec
# header is printed and then a formatting of opts_spec. see qrm_opts_parse
function qrm_opts_usage() {
	echo $1 && shift
	local -a opts_arr;

        IFS="$QRM_OPTS_SEP"
        opts_arr=($1) && shift
        unset IFS

	local -a usages
	local max_len=0
	for (( i=0 ; i<${#opts_arr[*]} ; i++ )); do
		eval $(_qrm_opts_opt_eval ${opts_arr[$i]})
		#_qrm_opts_opt_string
		local usage="$(qrm_opts_short_desc)${has_arg:+ <$arg_desc>}"
		
		case "$occour" in
			"$OCCOUR_1")
				;;
			"$OCCOUR_0_1")
				usage="[$usage]"
				;;
			"$OCCOUR_0_N")
				usage="[$usage] *"
				;;
			"$OCCOUR_1_N")
				usage="$usage +"
				;;
		esac	
		
		usages[${#usages[*]}]="$usage"

		local len=${#usage}
		max_len=$((max_len<len?len:max_len))
	done
	
	for (( i=0 ; i<${#opts_arr[*]} ; i++ )); do
                eval $(_qrm_opts_opt_eval ${opts_arr[$i]})
	
		printf "  %-*s %s %s\n" $max_len "${usages[$i]}" "${desc:+- $desc}"	
	done
}

#function example() {
#	boo_opt="exp='BOO=\$1' short=b long=boo occour='?' desc='give me some'"
#	foo_opt="exp='FOO=\$1' long=foo has_arg=1 arg_valid='expr match \$1 2 > /dev/null' occour='+' arg_desc='name' desc='gime me also'" 
#	goo_opt="exp='GOO[\${#GOO[*]}]=\$1' short=g has_arg=1 occour='+'"
#	help_opt="exp='usage ' short=h occour='?' desc='show help'"
#
#	opts="$boo_opt|$foo_opt|$goo_opt|$help_opt"
#	function usage() {
#		qrm_opts_usage "do it" "$opts"
#		exit 0
#	}
#	eval set -- $(qrm_opts_std "$@")
#	if ! qrm_opts_parse "$opts" '' "$@"; then
#		usage
#	fi
#	echo "FOO=$FOO"
#	echo "BOO=$BOO"
#	echo "GOO=(${GOO[@]})"
#}
#
#example "$@"
