#!/bin/sh
set -e

. /usr/share/debconf/confmodule

db_capb backup

tpl_di_locale="debian-installer/locale"
tpl_di_fallbacklocale="debian-installer/fallbacklocale"
tpl_di_language="debian-installer/language"
tpl_di_country="debian-installer/country"
tpl_di_consoledisplay="debian-installer/consoledisplay"
tpl_languagelist="localechooser/languagelist"
tpl_shortlist="localechooser/shortlist"
tpl_continentlist="localechooser/continentlist"
tpl_countrylist="localechooser/countrylist"
tpl_supportedlocales="localechooser/supported-locales"

# This is the iso_3166.tab file location
ISO3166TAB=/usr/share/iso-codes/iso_3166.tab
SUPPORTEDLOCALES=/usr/share/localechooser/SUPPORTED
if [ ! -f "$SUPPORTEDLOCALES" ]; then
	SUPPORTEDLOCALES=/usr/share/i18n/SUPPORTED
fi
SHORTLISTS=/etc/shortlists
if [ ! -f "$SHORTLISTS" ]; then
	SHORTLISTS=/usr/share/localechooser/shortlists
fi
LANGUAGELISTFILE=/usr/share/localechooser/languagelist
LANGUAGELISTDATA=/usr/share/localechooser/languagelist.data.gz

NL="
"

error() {
	logger -t localechooser "error: $@"
	exit 1
}

log() {
	logger -t localechooser "info: $@"
}


locale2countrycode() {
	if [ -n "$1" ]; then
		if echo $1 | grep -q "_"; then
			echo $1 | cut -f2 -d_ | cut -f1 -d@ | cut -f1 -d\.
		else
			echo
		fi
	else
		error "Missing argument"
	fi
}

choices_add() {
	echo "${1:+$1, }$2"
}

# Determine the display level
language_display_level() {
	local level

	#log "Frontend in use: $DEBIAN_FRONTEND"
	case $DEBIAN_FRONTEND in
	    text)
		level=0 ;;
	    gtk)
		level=4 ;;
	    *)
		# Keep only Latin1 languages if we don't have a framebuffer
		if [ "$TERM_FRAMEBUFFER" ]; then
			level=3
		else
			level=1
		fi
		# ASCII only if we are on serial console or a dumb terminal
		# Both variables should already be set at init time
		if [ "$TERM_TYPE" = "serial" ] || [ "$TERM" = "dumb" ]; then
			level=0
		fi
		;;
	esac

	if [ "$OVERRIDE_SHOW_ALL_LANGUAGES" ]; then
		level=4
	fi
	#log "Language display level is $level"
	echo $level
}

# Build list of available languages for a display level
build_language_template() {
	local level=$1
	local oldlevel=$(cat /var/lib/localechooser/langlevel \
		2>/dev/null || true)
	if [ "$level" = "$oldlevel" ]; then
		return 0
	fi

	local OLDIFS IFS line name codes names_en names_both
	rm -f /var/lib/localechooser/langlevel

	OLDIFS="$IFS"
	IFS="$NL"
	for line in $(zcat $LANGUAGELISTDATA | grep "^[0-$level]:"); do
		name="$(echo "$line" | cut -d: -f3)"
		codes="$(choices_add "$codes" \
			"$(echo "$line" | cut -d: -f2)")"
		names_en="$(choices_add "$names_en" \
			"$name")"
		names_both="$(choices_add "$names_both" \
			"$name\${!TAB}-\${!TAB}$(echo "$line" | cut -d: -f4)")"
	done
	IFS="$OLDIFS"

	db_subst $tpl_languagelist CODES "$codes"
	db_subst $tpl_languagelist NAMES_EN "$names_en"
	db_subst $tpl_languagelist NAMES_BOTH "$names_both"

	echo $level >/var/lib/localechooser/langlevel
}


do_preseed() {
	LOCALE="$1"
	log "Locale has been preseeded to $LOCALE"

	# Only mark variables seen if this one was preseeded seen.
	db_fget $tpl_di_locale seen
	seenflag=$RET
	db_fset $tpl_di_locale seen "false" || true

	# Only populate debconf if this is a supported locale
	# and if the language is supported in D-I
	LANGUAGE=${LOCALE%%.*}
	if ! grep -q "^$LANGUAGE;" $LANGUAGELISTFILE; then
		LANGUAGE=${LANGUAGE%%_*}
		if ! grep -q "^$LANGUAGE;" $LANGUAGELISTFILE; then
			LANGUAGE="$(grep "^${LANGUAGE}_" $LANGUAGELISTFILE | \
				head -n1 | cut -d';' -f1)"
		fi
	fi
	if [ -n "$LANGUAGE" ]; then
		db_set $tpl_languagelist $LANGUAGE
		log "Set $tpl_languagelist = '$LANGUAGE'"
		db_fset $tpl_languagelist seen $seenflag || true
		COUNTRY=$(locale2countrycode "$LOCALE")
		if [ -n "$COUNTRY" ]; then
			db_set $tpl_di_country "$COUNTRY"
			log "Set $tpl_di_country = '$COUNTRY'"
			db_fset $tpl_di_country seen $seenflag || true
			db_fset $tpl_continentlist seen $seenflag || true
			country_preseeded=1

			if grep -q "^$LOCALE " $SUPPORTEDLOCALES ; then
				db_set $tpl_di_locale $LOCALE
				db_fset $tpl_di_locale seen $seenflag || true
				db_fset $tpl_supportedlocales seen $seenflag || true
				log "Set $tpl_di_locale = '$LOCALE'"
			else
				# The locale was invalid, empty it
				LOCALE=""
			fi
		else
			# The locale was invalid, empty it
			LOCALE=""
		fi
	fi
}

# Install specific packages depending on selected language
# Those we install here are those required immediately
# Otherwise we will install them in finish-install
install_lang_specific() {
	if [ "$LOCALE" != "C" ]; then
		case "$LANGUAGE" in
		    ar|el|fa|he|ja|ko|ku|tr|vi|wo|zh*)
			# We need a complete font for later steps
			anna-install bterm-unifont
			;;
		esac
	fi
}

# Change language and switch font for graphical installer
set_debconf_language() {
	db_set "debconf/language" "$1"

	if type gtk-set-font >/dev/null 2>&1; then
		gtk-set-font || true
	fi
}

# Determine which template to display to warn for incomplete translations
# and fill in the variable contents
warning_template() {
	local RET status template tbase twarn tabort
	status=$1
	tbase=localechooser/translation

	case $status in
	    0)	twarn=incomplete; tabort=abort ;;
	    1)	twarn=normal-ok; tabort=abort ;;
	    2)	twarn=partial; tabort=maybe-abort ;;
	    3)	twarn=mostly-ok ;;
	    4)	twarn=exceptions ;;
	esac
	if [ $status -le 2 ]; then
		template=$tbase/warn-severe
		db_metaget $tbase/text/$tabort description
		db_subst $template TXT-ABORT "$RET"
	else
		template=$tbase/warn-light
	fi

	# Languages that have fallbacks may have special templates
	if [ "$twarn" != exceptions ] && \
	   expr $LANGUAGELIST : ".*:" >/dev/null && \
	   db_metaget $tbase/text/warn_$twarn/$LANGUAGE description; then
		:
	else
		db_metaget $tbase/text/warn_$twarn description
	fi
	db_subst $template TXT-WARN "$RET"

	echo $template
}

# Returns 0 if the template has the country among its choices
has_country() {
	local RET template country
	template="$1"
	country="$2"

	[ "$country" ] || return 1

	db_metaget $template Choices-C
	echo "$RET" | grep -q "$country"
}

# Set defaults for continent based on country
set_default_continent() {
	local OLDIFS IFS RET country continents c continent
	country="$1"

	if [ -z "$country" ]; then
		db_reset $tpl_continentlist
		return
	fi

	# Use Choices-en.UTF-8 to avoid getting translated values
	db_metaget $tpl_continentlist Choices-en.UTF-8
	continents=$(echo $RET | sed "s/, /,/g")

	OLDIFS="$IFS"
	IFS=,
	for continent in $continents; do
		IFS="$OLDIFS"
		c=$(echo "$continent" | sed "s/ /_/g")
		if has_country $tpl_countrylist/$c "$country"; then
			db_set $tpl_continentlist "$continent"
			break
		fi
	done
}

# Find the preferred locale given language, country, and encoding.
preferred_locale() {
	OLDIFS="$IFS"
	IFS="$NL"
	# Look for locales with the right encoding first.
	for trylocale in $(grep "^$1_$2" $SUPPORTEDLOCALES || true); do
		IFS="$OLDIFS"
		if [ -z "$3" ] || [ "${trylocale#* }" = "$3" ]; then
			echo "${trylocale% *}"
			return
		fi
		IFS="$NL"
	done
	# Pick the first of any other available locales.
	for trylocale in $(grep "^$1_$2" $SUPPORTEDLOCALES || true); do
		IFS="$OLDIFS"
		echo "${trylocale% *}"
		return
	done
	IFS="$OLDIFS"
}

refine_locale() {
	local old_locale
	old_locale=$1

	# Is the locale we inherited really a complete locale?
	LOCALE_LANGUAGECHOOSER_COMPLETE=$(echo $old_locale | grep "_" || true)
	PREFERRED_LOCALE="$(preferred_locale "$LANGUAGE" "$COUNTRYCODE" "$ENCODING")"
	if [ "$PREFERRED_LOCALE" ]; then
		# Special handling of cases where the locale
		# in languagechooser is NOT the combination of
		# language_COUNTRY. Used for Norwegian
		# Bokmal transition in order to keep no_NO as
		# locale. May be used in the future for other
		# special cases, so we'd better keep this.
		if [ "$LANGUAGE" = "$LANGUAGECODE_LANGUAGECHOOSER" ] && \
		   [ "$COUNTRYCODE" = "$DEFAULT_COUNTRY" ] && \
		   [ "${LANGUAGE}_${COUNTRYCODE}" != "$old_locale" ] && \
		   [ "$LOCALE_LANGUAGECHOOSER_COMPLETE" ]; then
			# Explanation: we revert back to the
			# locale inherited from the language
			# step if the country step did NOT
			# induce change in language and country
			# but the resulting locale is different
			# from the one we had in first step.
			echo "$old_locale"
		else
			echo "$PREFERRED_LOCALE"
		fi
	fi
}

# Extract a value from /etc/lsb-release
lsb_extract () {
	[ -f /etc/lsb-release ] || return 0
	grep "^$1=" /etc/lsb-release | \
		sed 's/^[^=]*=//; s/^"//; s/"$//' || true
}


# Reset all variables
LANGUAGE=""
COUNTRY=""
COUTRYNAME=""
LOCALE=""

# debconf/language is an alias for debian-installer/language
db_register "$tpl_di_language" "debconf/language"

# Only display the translated texts (ie the English "translation") when in
# UTF-8 mode. Note: seems the only case this triggers is serial console;
# probably not needed anymore: we already limit which languages we display.
if echo $LANG $LC_CTYPE | grep -q UTF-8; then
	INITIAL_LANGUAGE=en
else
	INITIAL_LANGUAGE=""
fi

db_fget $tpl_languagelist seen
if [ "$RET" = true ]; then
	db_get $tpl_languagelist
	PREVIOUS_LANGUAGE="$RET"
else
	PREVIOUS_LANGUAGE=""
fi

# Find the display level and set languages in the template
# Needs to be done before checking preseeding, so we can preseed the
# correct template.
build_language_template $(language_display_level)

# Support preseeding of the locale all in one variable for convenience.
# Only check for preseeding the first time localechooser is run.
country_preseeded=""
if [ ! -f /var/lib/localechooser/preseeded ]; then
	if db_get $tpl_di_locale && [ "$RET" ]; then
		do_preseed $RET
	elif db_get $tpl_di_language && [ "$RET" ]; then
		LANGUAGE="$RET"
		db_fget $tpl_di_language seen
		seenflag=$RET
		LANGUAGE="${LANGUAGE%%.*}"
		if ! grep -q "^$LANGUAGE;" $LANGUAGELISTFILE; then
			LANGUAGE="${LANGUAGE%%_*}"
			if ! grep -q "^$LANGUAGE;" $LANGUAGELISTFILE; then
				LANGUAGE="$(grep "^${LANGUAGE}_" $LANGUAGELISTFILE | \
					head -n1 | cut -d';' -f1)"
			fi
		fi
		if [ -n "$LANGUAGE" ]; then
			db_set $tpl_languagelist $LANGUAGE
			log "Set $tpl_languagelist = '$LANGUAGE'"
			db_fset $tpl_languagelist seen $seenflag || true
		fi
	fi

	>/var/lib/localechooser/preseeded
fi


# Main loop starts here
# Use a state machine to allow jumping back to previous questions.
# Main states are multiples of 10 to allow "preparation" states to be
# skipped when backing up.
STATE=10
while :; do
	case "$STATE" in
	   10)	# Display language list
		sel_language=1
		# Disabled because of #470258: template is set to true too early
		if false && \
		   db_get debconf/translations-dropped && [ "$RET" = true ]; then
			if db_fget $langname_template seen && [ "$RET" != true ]; then
				sel_language=""
				db_input high localechooser/translation/no-select || true
			fi
		else
			# Set initial language for correct display of list
			set_debconf_language $INITIAL_LANGUAGE

			db_capb backup align
			db_input critical $tpl_languagelist || [ $? -eq 30 ]
		fi
		;;

	   11)	# We have a language
		db_get $tpl_languagelist
		LANGUAGE="$RET"

		if [ "$LANGUAGE" = "$PREVIOUS_LANGUAGE" ]; then
			# The user picked the same language as before. We
			# don't need to reset the default country and
			# locale, and doing so may be confusing.
			STATE=12
			continue
		fi

		# Determine defaults based on languagelist
		. languagemap
		db_set "$tpl_di_language" "$LANGUAGELIST"
		log "Set $tpl_di_language = '$LANGUAGELIST'"
		db_set "$tpl_di_locale"   "$LOCALE"
		log "Set $tpl_di_locale = '$LOCALE'"
		db_set "$tpl_di_fallbacklocale"   "$FALLBACKLOCALE"
		log "Set $tpl_di_fallbacklocale = '$FALLBACKLOCALE'"

		if [ -n "$DEFAULT_COUNTRY" ]; then
			log "Default country = '$DEFAULT_COUNTRY'"
		fi

		db_set "$tpl_di_consoledisplay"  "$CONSOLE"
		log "Set $tpl_di_consoledisplay = '$CONSOLE'"

		db_set "debconf/language" "$LANGUAGELIST"
		log "Set debconf/language = '$LANGUAGELIST'"

		X_INSTALLATION_MEDIUM="$(lsb_extract X_INSTALLATION_MEDIUM)"
		if [ "$sel_language" ] && [ $LANGUAGE != en ] && \
		   [ "$X_INSTALLATION_MEDIUM" = "floppy" ]; then
			db_input high localechooser/translation/none-yet || true
		fi
		;;

	   12)	# Warn if translation is incomplete
		set_debconf_language "$LANGUAGELIST"

		# Display warning for incomplete translations; skip it for
		# automated installs to prevent a loop if the default is false
		twarning=""
		if [ "$sel_language" ] && \
		   db_get debconf/priority && [ "$RET" != critical ] && \
		   tstatus=$(translation-check "$LANGUAGE"); then
			twarning=$(warning_template $tstatus)
			db_input high $twarning || [ $? -eq 30 ]
		fi
		;;

	   13)	# Continue or choose alternative language
		if [ "$twarning" ]; then
			if db_get $twarning && [ "$RET" = false ]; then
				db_reset $twarning
				STATE=10
				continue
			fi
		fi

		install_lang_specific

		STATE=19
		continue
		;;

	   19)	# Prepare for country selection
		# Keep track of values we have after language selection step
		LOCALE_LANGUAGECHOOSER=$LOCALE
		LANGUAGECODE_LANGUAGECHOOSER=$LANGUAGE

		FIRST_LANG="${LANGUAGELIST%%:*}"

		# We use /etc/shortlists to check if we should present a shortlist
		# As we may unregister the question for shortlists, the value for the
		# shortlist template is also saved with the language specific question
		use_lang=""
		if [ "$LOCALE" != "C" ]; then
			if grep -q "^$FIRST_LANG" $SHORTLISTS; then
				use_lang=$FIRST_LANG
			elif grep -q "^$LANGUAGE" $SHORTLISTS; then
				use_lang=$LANGUAGE
			fi
		fi

		# At this point we should have either xx, or xx_YY in LOCALE
		allprio=critical
		shortprio=critical
		;;

	   20)	# Display a country shortlist if there is one
		askedshort=
		db_get $tpl_di_country
		current_country="$RET"

		# If the fallback locale does not include a country code
		# (and thus no underscore character), we must prompt for a
		# country and thus display all countries. Example: Esperanto.
		# The next "if" test will be false and "askedshort" remains
		# unset, so no short country list question and continent
		# dialogs are shown at critical priority as we need a country.
		if echo $FALLBACKLOCALE | grep "_" >/dev/null 2>&1; then
			# Otherwise, prompt with the short list for languages
			# that are listed in /etc/shortlists; for others prompt
			# with all continents/countries (at medium priority)
			if [ "$use_lang" ]; then
				shortlist_template="$tpl_shortlist/$use_lang"
				db_unregister $tpl_shortlist || true
				db_register $shortlist_template $tpl_shortlist
				if db_fget $tpl_di_country seen; then
					db_fset $tpl_shortlist seen $RET || true
				fi

				# Set default value
				if [ $LASTSTATE -ne 21 ]; then
					current_short=""
					if [ "$current_country" ]; then
						if has_country $tpl_shortlist "$current_country"; then
							current_short="$current_country"
						else
							current_short=other
						fi
					elif has_country $tpl_shortlist "$DEFAULT_COUNTRY"; then
						current_short="$DEFAULT_COUNTRY"
					fi
					db_set $tpl_shortlist "$current_short"
				fi

				# If the current (preseeded) value is not in the
				# shortlist, ask for continent/country instead
				if [ -z "$country_preseeded" ] || \
				   [ "$current_short" != other ]; then
					db_input $shortprio $tpl_shortlist || [ $? -eq 30 ]
					askedshort=1
				fi
				country_preseeded=""
			else
				allprio=medium
				# Display continents after backing up from locale
				# selection for countries without shortlist
				if [ $LASTSTATE -gt 21 ]; then
					STATE=21
					continue
				fi
			fi
		fi
		;;

	   21)	# Check if a country was selected from the short list
		# and if not, allow to select a continent
		if [ "$askedshort" ]; then
			db_get $tpl_shortlist
			COUNTRYCODE="$RET"
			if [ "$COUNTRYCODE" != "other" ]; then
				STATE=24
				continue
			fi
		fi

		# Set default value
		if [ $LASTSTATE -ne 22 ]; then
			if [ "$current_country" ]; then
				set_default_continent "$current_country"
			else
				set_default_continent "$DEFAULT_COUNTRY"
			fi
		else
			# Backed up; reset continent template if no country
			# was actually selected so it can get a new default
			if [ -z "$current_country" ]; then
				db_reset $country_template
			fi
		fi
		db_input $allprio $tpl_continentlist || [ $? -eq 30 ]
		;;

	   22)	# Select a country on the continent
		db_get $tpl_continentlist
		country_template="$tpl_countrylist/$(echo $RET | sed "s/ /_/g")"
		if db_fget $tpl_di_country seen; then
			db_fset $country_template seen $RET || true
		fi

		if [ "$current_country" ] && \
		   has_country $country_template "$current_country"; then
			db_set $country_template "$current_country"
		elif db_get $country_template && [ -z "$RET" ] && \
		   has_country $country_template "$DEFAULT_COUNTRY"; then
			db_set $country_template "$DEFAULT_COUNTRY"
		fi

		db_input $allprio $country_template || [ $? -eq 30 ]
		;;

	   23)	# Get the selected country
		db_get $country_template
		COUNTRYCODE="$RET"
		;;

	   24)	# We have a country
		db_set "$tpl_di_country"  "$COUNTRYCODE"
		log "Set $tpl_di_country = '$COUNTRYCODE'"

		STATE=29
		continue
		;;

	   29)	# Prepare for locale selection; determine default locale
		# Find a supported locale which best fits the selected language
		# and country.
		# Refinement: we try to use the modifier inherited from language
		# selection. However, we do this only if the locale is valid.

		# Maybe no refinement should be done if locale is preseeded?
		if [ "$LOCALE" != "C" ]; then
			OLDLOCALE=$LOCALE
			LOCALE=""

			LOCALE=$(refine_locale "$OLDLOCALE")

			# Fall back to a supported locale
			if [ -z "$LOCALE" ]; then
				if grep -q "^$FALLBACKLOCALE " $SUPPORTEDLOCALES; then
					LOCALE="$FALLBACKLOCALE"
				else
					LOCALE=$(echo $FALLBACKLOCALE | sed -e 's/[.@].*$//')
				fi
				log "Falling back to locale '$LOCALE'"
			fi
		fi

		# Set the locale
		db_set "$tpl_di_locale" "$LOCALE"
		log "Set $tpl_di_locale = '$LOCALE'"

		# The code below adds lang_COUNTRY at the beginning of the
		# language list we got from languagechooser.
		# We shouldn't just add this before the former list in case
		# the country is changed several times.
		if [ "$COUNTRYCODE" != "$DEFAULT_COUNTRY" ] && \
		   [ "$COUNTRYCODE" ] && [ "$LANGUAGE" ] && \
		   [ "$LOCALE" != "C" ] && [ "$LANGUAGELIST" != "$LANGUAGE" ]; then
			LANGUAGELIST=${LANGUAGE}_${COUNTRYCODE}:$LANGUAGELIST
			db_set "$tpl_di_language" "$LANGUAGELIST"
			log "Set $tpl_di_language = '$LANGUAGELIST'"
		fi
		;;

	   30)	# Select system locale
		# We will select from supported locales for LANGUAGE_COUNTRY
		if [ "$LOCALE" != "C" ]; then
			POSSIBLELOCALES=$(grep -e "^${LANGUAGE}_${COUNTRYCODE}" $SUPPORTEDLOCALES | cut -d' ' -f1 || true)
			if [ -z "$POSSIBLELOCALES" ]; then
				POSSIBLELOCALES=$(grep -e "^${LANGUAGE}_${COUNTRYCODE_LANGUAGECHOOSER}" $SUPPORTEDLOCALES | cut -d' ' -f1 || true)
			fi
			if [ $(echo $POSSIBLELOCALES | wc -w) -gt 1 ]; then
				CHOICES=""
				for i in $POSSIBLELOCALES ; do
					CHOICES="${CHOICES:+$CHOICES, }$i"
				done
				db_subst $tpl_di_locale LOCALELIST "$CHOICES"
				db_input medium $tpl_di_locale || [ $? -eq 30 ]
			fi
		fi
		;;

	   31)	# Select additional locales
		db_get $tpl_di_locale
		LOCALE="$RET"

		CHOICES=
		# *.UTF-8@euro locales are deprecated; don't use them
		for i in $(grep -v '\.UTF-8@euro$' $SUPPORTEDLOCALES | cut -d' ' -f1 | grep -v "^$LOCALE$"); do
			CHOICES="${CHOICES:+$CHOICES, }$i"
		done
		db_subst $tpl_supportedlocales LOCALELIST "$CHOICES"
		db_fget $tpl_supportedlocales seen
		if [ "$RET" = false ]; then
			# Always support English (unless preseeded
			# otherwise), so that we get English language packs
			# etc.
			if [ "$LOCALE" = en_US.UTF-8 ]; then
				db_set $tpl_supportedlocales "$LOCALE"
			else
				db_set $tpl_supportedlocales "$LOCALE, en_US.UTF-8"
			fi
		fi
		db_input medium $tpl_supportedlocales || [ $? -eq 30 ]
		;;

	    *)
		break
		;;
	esac

	LASTSTATE=$STATE
	if db_go; then
		STATE=$(($STATE + 1))
	else
		STATELEVEL=$(($STATE / 10 * 10)) # round down to multiple of 10
		if [ $STATE -eq $STATELEVEL ]; then
			STATE=$(($STATE - 10))
		else
			STATE=$(($STATE - 1))
		fi
	fi
	db_capb backup
done

if [ $STATE -eq 0 ]; then
	exit 10 # back out to main menu
fi

# All locales being UTF-8, unconditionnally set this, for
# console-setup purposes
db_set debian-installer/charmap UTF-8

exit 0
