#!/bin/bash

# Purpose {{{
# This script will download some diagnostic tools.
#   1. Memtest
#   2. Gparted
# and make a sample menu.cfg config file.
# }}}
# Vars {{{
readonly PROGNAME=$(basename "${0}")
readonly NBARGS="${#}"
[ -z "${DEBUG}" ] && DEBUG=1
## Export DEBUG for sub-script
export DEBUG

# Default value for TFTP's directory
if command -v in.tftpd > /dev/null; then
	source /etc/default/tftpd-hpa
else
	TFTP_DIRECTORY="/srv/tftp"
fi

# Tools versions
MEMTEST_VERSION_DEFAULT="$(curl --no-progress-meter http://memtest.org/ \
	| sed --silent 's;.*VERSION : \([[:alnum:]]*.[[:alnum:]]*\).*</font>.*;\1;p')"
GPARTED_VERSION_DEFAULT="$(curl --no-progress-meter https://gparted.org/ \
	| sed --silent 's;.*<a href.*sourceforge.net.*>Live \(.*\)</a>.*;\1;p')"

# TFTP tree
DIAG_INSTALLER_DIR="${TFTP_DIRECTORY}/installer"
CONFIG_PXE="${DIAG_INSTALLER_DIR}/menu.cfg.diag.example"
MEMTEST_INSTALLER_DIR="${DIAG_INSTALLER_DIR}/memtest"
GPARTED_INSTALLER_DIR="${DIAG_INSTALLER_DIR}/gparted"

## Colors
readonly PURPLE='\033[1;35m'
readonly RED='\033[0;31m'
readonly RESET='\033[0m'
readonly COLOR_DEBUG="${PURPLE}"
# }}}
usage() {                                                       # {{{

	cat <<- EOF
usage: $PROGNAME [-d|-h|-m]

This script will download some diagnostic tools (eg. Memtest, Gparted,…).

EXAMPLES :
    - Download default diagnostic tools.
        ${PROGNAME}

    - Download a specific version of Gparted (default : ${GPARTED_VERSION_DEFAULT}).
        ${PROGNAME} --gparted "1.0.0-5"

    - Download a specific version of Memtest (default : ${MEMTEST_VERSION_DEFAULT}).
        ${PROGNAME} --memtest "3.01"

OPTIONS :
    -g,--gparted
        Set the version of Gparted to download.

    -m,--memtest
        Set the version of Memtest to download.

    -d,--debug
        Enable debug messages.

    -h,--help
        Print this help message.

EOF

}
# }}}
debug_message() {                                               # {{{

	local_message="${1}"

	## Print message if DEBUG is enable (=0)
	[ "${DEBUG}" -eq "0" ] && printf '\e[1;35m%-6b\e[m\n' "DEBUG − ${PROGNAME} : ${local_message}"

	return 0
}
# }}}
error_message() {                                               # {{{

	local_error_message="${1}"
	local_error_code="${2}"

	## Print message
	printf '%b\n' "ERROR − ${PROGNAME} : ${RED}${local_error_message}${RESET}"

	exit "${local_error_code:=66}"
}
# }}}
define_vars() {                                                 # {{{

	# If memtest_version wasn't defined (argument) {{{
	if [ -z "${memtest_version}" ]; then
		## Use default value
		memtest_version="${MEMTEST_VERSION_DEFAULT}"
	fi
	# }}}
	# If gparted_version wasn't defined (argument) {{{
	if [ -z "${gparted_version}" ]; then
		## Use default value
		gparted_version="${GPARTED_VERSION_DEFAULT}"
	fi
	# }}}
}
# }}}

is_var_empty() {                                                # {{{

	## Return False by default
	return_var_empty="1"
	## Total number of variables to test
	local_total_var="${#}"

	loop_count_var_empty="0"

	## While it remains a variable to test
	while [ "${local_total_var}" -gt "${loop_count_var_empty}" ]; do
		debug_message "is_var_empty − \
Test var: ${1}."
		### Test if this is empty and set return value to True
		[ -z "${1}" ] && return_var_empty="0"

		### Increase the number of tested variables
		loop_count_var_empty=$((loop_count_var_empty+1))

		### Shift to the next variable
		shift
	done

	return "${return_var_empty}"

}
# }}}
is_file_absent() {                                              # {{{

	local_file_absent="${1}"

	## File exists by default
	return_is_file_absent="1"

	### Check if the file exists
	# shellcheck disable=SC2086
	if find ${local_file_absent} > /dev/null 2>&1; then
		return_is_file_absent="1"
		debug_message "is_file_absent − \
The file ${RED}${local_file_absent}${COLOR_DEBUG} exists."
	else
		return_is_file_absent="0"
		debug_message "is_file_absent − \
The file ${RED}${local_file_absent}${COLOR_DEBUG} doesn't exist."
	fi

	return "${return_is_file_absent}"

}
# }}}
download_file() {                                               # {{{

	local_url="${1}"
	local_dest_file="${2}"

	debug_message "download_file − \
Download ${local_url} to ${RED}${local_dest_file}${COLOR_DEBUG} ."

	wget --quiet "${local_url}" --output-document="${local_dest_file}"

	## Unset variables
	unset local_url
	unset local_dest_file
}
# }}}
is_file_empty() {                                               # {{{

	local_file="${1}"

	## File is empty by default
	return_is_file_empty="0"

	### Check if the file is empty
	if [ ! -s "${local_file}" ]; then
		return_is_file_empty="0"
		debug_message "is_file_empty − \
The file ${RED}${local_file}${COLOR_DEBUG} is empty or doesn't exists."
	else
		return_is_file_empty="1"
		debug_message "is_file_empty − \
The file ${RED}${local_file}${COLOR_DEBUG} exists and has a size greater than zero."
	fi

	## Unset variables
	unset local_file

	return "${return_is_file_empty}"
}
# }}}

main() {                                                        # {{{

	# Define all vars
	define_vars

	# If memtest_version is empty {{{
	# Exit with error
	is_var_empty "${memtest_version}" \
		&& error_message "Memtest version variable is empty" 6
	# }}}
	# If gparted_version is empty {{{
	# Exit with error
	is_var_empty "${gparted_version}" \
		&& error_message "Gparted version variable is empty" 6
	# }}}

	# Create diagnostic tools directory and config file
	mkdir --parents -- "${DIAG_INSTALLER_DIR}" \
	|| error_message "Can't create ${DIAG_INSTALLER_DIR}/ directory." 1
	true > ${CONFIG_PXE}

	# Memtest {{{
	MEMTEST_URL="http://www.memtest.org/download/${memtest_version}/memtest86+-${memtest_version}.bin.gz"
	MEMTEST_BIN="memtest86+-${memtest_version}.bin"

	# If this version is not already present on the system
	if is_file_absent "${MEMTEST_INSTALLER_DIR}/${memtest_version}"; then

		## (re)Create the installer directory
		rm --recursive --force -- "${MEMTEST_INSTALLER_DIR}"
		mkdir --parents -- "${MEMTEST_INSTALLER_DIR}"
		pushd "${MEMTEST_INSTALLER_DIR}" > /dev/null \
			|| error_message "Can't move to ${MEMTEST_INSTALLER_DIR} directory." 2

		## Download and extract the last version
		wget --quiet "${MEMTEST_URL}" -O - | gzip --decompress > "${MEMTEST_BIN}"
		is_file_empty "${MEMTEST_BIN}"  \
			&& error_message "${MEMTEST_BIN} is empty." 3

		## Set simpler name (without the **.bin** extension !)
		ln --symbolic -- "${MEMTEST_BIN}" memtest86+

		### Add flag file to know that this version is available
		true > "${MEMTEST_INSTALLER_DIR}/${memtest_version}"

		popd > /dev/null \
			|| error_message "Can't move back from ${MEMTEST_INSTALLER_DIR}/ directory." 2
	fi

	# Config file
	/bin/cat >> "${CONFIG_PXE}" << EOF
label memtest
  menu label ^Memory diagnostic tool (Memtest)
  kernel installer/memtest/memtest86+
EOF

	# }}}

	# Gparted {{{

	# For available classic architecture
	for ARCH in amd64 i686; do
		GPARTED_URL="http://downloads.sourceforge.net/project/gparted/gparted-live-stable/${gparted_version}/gparted-live-${gparted_version}-${ARCH}.zip"
		GPARTED_TEMP_FILE="gparted-live-${gparted_version}-${ARCH}.zip"

		# If this version is not already present on the system
		if is_file_absent "${GPARTED_INSTALLER_DIR}/${ARCH}/${gparted_version}"; then

			## (re)Create the installer directory for this architecture
			rm --recursive --force -- "${GPARTED_INSTALLER_DIR}/${ARCH}"
			mkdir --parents -- "${GPARTED_INSTALLER_DIR}/${ARCH}"
			pushd "${GPARTED_INSTALLER_DIR}/${ARCH}" > /dev/null \
				|| error_message "Can't move to ${GPARTED_INSTALLER_DIR}/${ARCH} directory." 2

			## Download and extract the last version
			download_file "${GPARTED_URL}" "${GPARTED_TEMP_FILE}"
			is_file_empty "${GPARTED_TEMP_FILE}"  \
				&& error_message "${GPARTED_TEMP_FILE} is empty." 3
			unzip -qq -j "${GPARTED_TEMP_FILE}" live/filesystem.squashfs live/initrd.img live/vmlinuz -d . \
				|| error_message "Can't properly extract ${GPARTED_TEMP_FILE} archive."

			rm --force -- "${GPARTED_TEMP_FILE}"

			### Add flag file to know that this version is available
			true > "${GPARTED_INSTALLER_DIR}/${ARCH}/${gparted_version}"

			/bin/cat >> "${CONFIG_PXE}" << EOF
label gpartedlive${ARCH}
  menu label Partiton Manager ${ARCH} (^Gparted)
  kernel installer/gparted/${ARCH}/vmlinuz
  append initrd=installer/gparted/${ARCH}/initrd.img boot=live config components union=overlay username=user noswap noeject ip= vga=788 fetch=tftp://129.20.203.27/installer/gparted/${ARCH}/filesystem.squashfs
EOF

			popd > /dev/null \
				|| error_message "Can't move back from ${GPARTED_INSTALLER_DIR}/${ARCH}/ directory." 2
		fi

	done

	### }}}

	# Config file
	/bin/cat >> "${CONFIG_PXE}" << EOF
label separator
  menu label -----
label mainmenu
  menu label ^Back..
  menu exit
EOF
}
# }}}

# Manage arguments                                                # {{{
# This code can't be in a function due to argument management

if [ ! "${NBARGS}" -eq "0" ]; then

	manage_arg="0"

	## If the first argument is not an option
	if ! printf -- '%s' "${1}" | grep -q -E -- "^-+";
	then
		## Print help message and exit
		printf '%b\n' "${RED}Invalid option: ${1}${RESET}"
		printf '%b\n' "---"
		usage

		exit 1
	fi

	# Parse all options (start with a "-") one by one
	while printf -- '%s' "${1}" | grep -q -E -- "^-+"; do

	case "${1}" in
		-g|--gparted )                        ## Define gparted_version
			## Move to the next argument
			shift
			## Define var
			readonly gparted_version="${1}"
			;;
		-m|--memtest )                        ## Define memtest_version
			## Move to the next argument
			shift
			## Define var
			readonly memtest_version="${1}"
			;;
		-d|--debug )                          ## debug
			DEBUG=0
			;;
		-h|--help )                           ## help
			usage
			## Exit after help informations
			exit 0
			;;
		* )                                   ## unknow option
			printf '%b\n' "${RED}Invalid option: ${1}${RESET}"
			printf '%b\n' "---"
			usage
			exit 1
			;;
	esac

	debug_message "Arguments management − \
${RED}${1}${COLOR_DEBUG} option managed."

	## Move to the next argument
	shift
	manage_arg=$((manage_arg+1))

	done

	debug_message "Arguments management − \
${RED}${manage_arg}${COLOR_DEBUG} argument(s) successfully managed."
else
	debug_message "Arguments management − \
No arguments/options to manage."
fi

# }}}

main

exit 0
