#!/bin/bash

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # --- VARIABLES --- # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

### --- CONFIGURABLE VARIABLES --- ###

# Partition variables
DISK="/dev/sda !/dev/nvme0n1"	# If you don't want the script to automatically set this, then choose /dev/sda (SATA devices such as HDDs) or /dev/nvme0n1 (NVMe devices such as SSDs)!

# Name variables
LUKS_BOOT="cryptboot"			# name of encrypted boot partition
LUKS_LVM="cryptlvm"				# name of encrypted LVM container
HOSTNAME="archTest"				# hostname; can be set here
USERNAME="user"					# username; can be set here

# Password/-phrase variables
LUKS_PASSPHRASE="luks"			# LUKS encryption key; should be set here
ROOT_PASSWORD=""				# root user password; should be set here if wanted
USER_PASSWORD="user"			# super-user password; should be set here

# Config variables
# DEBUGGING
REFLECTOR=false					# whether reflector should update the mirror list
REFLECTOR_COUNTRIES="Germany"	# mirror country
TIMEZONE="/usr/share/zoneinfo/Europe/Berlin"
TIME="!utc localtime"
LOCALES=("de_DE.UTF-8 UTF-8" "en_US.UTF-8 UTF-8")
LOCALE_CONF="LANG=de_DE.UTF-8"
KEYMAP="de-latin1-nodeadkeys"

### --- MISCELLANEOUS GLOBAL VARIABLES (do not edit) --- ###

ENCRYPTED_BOOT_PT=false			# default=false; whether the boot partition should be encrypted; currently only works for BIOS/MBR systems
DEVICE_TYPE_IS_SATA=false		# default=false; is automatically set by script
DEVICE_TYPE_IS_NVME=false		# default=false; is automatically set by script
DUAL_BOOT_WITH_WIN=false		# default=false; is set to 'true' if 'dual boot' is selected in the select_install_setup function

# Messages
INVALID_1="Invalid reply..."
INVALID_2="Invalid input..."

### --- COLOR VARIABLES (do not edit) --- ###

# Color codes (ANSI escape sequences)
# These color escape sequences are composed of the escape character (<Esc>) followed by some other characters: <Esc> + [ + format code + m.
# In Bash the <Esc> character can be obtained with "\033" (octal syntax) or just "\e".
BRIGHT_RED="\e[91m"
BRIGHT_GREEN="\e[92m"
BRIGHT_YELLOW="\e[93m"
BRIGHT_BLUE="\e[94m"
BRIGHT_CYAN="\e[96m"
BRIGHT_WHITE="\e[97m"
CLEAR="\e[0m"

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # --- FUNCTIONS --- # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

## "Sanitize" variables, i.e. removing disabled values (marked by !) and unnecessary whitespace (spacebar, Tab, etc.)
# This was just straight up taken from picodotdev's Arch Linux Install Script (alis).
# Shame on me! It's such a handy feature though - I had to include it!

function sanitize_variables () {
	DISK=$(sanitize_variable "$DISK")
	REFLECTOR_COUNTRIES=$(sanitize_variable "$REFLECTOR_COUNTRIES")
	TIMEZONE=$(sanitize_variable "$TIMEZONE")
	TIME=$(sanitize_variable "$TIME")
	LOCALES=$(sanitize_variable "$LOCALES")
	LOCALE_CONF=$(sanitize_variable "$LOCALE_CONF")
	KEYMAP=$(sanitize_variable "$KEYMAP")
	# DEBUGGING
	#KEYBOARD_LAYOUT=$(sanitize_variable "$KEYBOARD_LAYOUT")
}

function sanitize_variable () {
	VARIABLE=$1
	# Removing disabled values (!); regex matches a ! followed by every character that is not blank space ([^ ]), this character occurs zero or more times (*)
	VARIABLE=$(echo "$VARIABLE" | sed "s/![^ ]*//g")
	# Substituting unnecessary whitespace (  ) with just one blank space ( ); regex matches blank space that occurs at least two times ({2,})
	VARIABLE=$(echo "$VARIABLE" | sed "s/ {2,}/ /g")
	# Trimming leading whitespace; regex matches any kind of whitespace ([[:space:]]) that occurs zero or more times (*) at the beginning of the string (^)
	VARIABLE=$(echo "$VARIABLE" | sed "s/^[[:space:]]*//")
	# Trimming trailing whitespace; regex matches any kind of whitespace ([[:space:]]) that occurs zero or more times (*) at the end of the string ($)
	VARIABLE=$(echo "$VARIABLE" | sed "s/[[:space:]]*$//")
	echo "$VARIABLE"
}

## Overwrite text

function overwrite () {
	local input=$1
	for i in {1..${#input}}; do
		echo -n -e ""
	done
}

## Animations for waiting till a process is finished

function wait_animation () {
	local animation=$2
	if [[ $animation = "spinner" ]]; then
		spinner=('|' '/' '-' '\')
		# Run while the given process is not yet finished and dump any errors into /dev/null
		while kill -0 $1 2> /dev/null; do
			for i in ${spinner[@]}; do
				# Move one space back (or simply backspace), then print one character of the spinner without printing a new line at the end (-n)
				echo -n -e "\b$i"
				sleep 0.1
			done
		done
		echo -n -e "\b\b...done\n"
	elif [[ $animation = "dot" ]]; then
		while kill -0 $1 2> /dev/null; do
			echo -n -e "."
			sleep 1
		done
	fi
}

## Initial warning

function warning () {
	# For stylized prompts
	local x=0
	local y_1=2
	local y_2=0
	# For output review prompt specifically
	local y_3=0
	local y_4=0
	# Messages
	local line_1="Welcome to the Arch Linux Laptop installation script!"
	local line_2="Warning!"
	local line_3="If you're opting for an Arch-only installation, this script will delete any partitions formerly set up."
	local line_4="All your data will be lost."
	local prompt="Do you want to continue? [y/N] "
	local placeholder="#####"
	clear
	echo -e "\e[93;104m\e[${y_1};$(( ($(tput cols) - ${#line_1}) / 2 ))f$line_1$CLEAR"
	((y_1+=2))
	echo -e "\n$BRIGHT_RED\e[${y_1};$(( ($(tput cols) - ${#line_2}) / 2 ))f$line_2$CLEAR"
	((y_1++))
	echo -e "$BRIGHT_RED\e[${y_1};$(( ($(tput cols) - ${#line_3}) / 2 ))f$line_3$CLEAR"
	((y_1++))
	echo -e "$BRIGHT_RED\e[${y_1};$(( ($(tput cols) - ${#line_4}) / 2 ))f$line_4$CLEAR\n"
	((y_1+=2))
	while true; do
		echo -e "\e[${y_1};$(( ($(tput cols) - ${#prompt}) / 2 ))f$BRIGHT_WHITE$prompt"
		((y_1++))
		((y_2=y_1))
		echo -n -e "\e[${y_1};$(( ($(tput cols) - ${#placeholder}) / 2 ))f${BRIGHT_WHITE}> "
		((y_1+=2))
		((y_3=y_1))
		read reply
		if [[ $reply =~ (^[yY][eE][sS]$)|^[yY]$ ]]; then
			echo -n -e "$CLEAR"
			break
		elif [[ $reply =~ ^[nN][oO]{0,1}$ ]]; then
			echo -n -e "$CLEAR"
			exit
			break
		else
			echo -n -e "\e[${y_2};$(( ($(tput cols) - ${#INVALID_1}) / 2 ))f$BRIGHT_RED$INVALID_1$CLEAR"
			read -n 1 -s
			echo -n -e "\e[${y_2};$(( ($(tput cols) - ${#INVALID_1}) / 2 ))f$BRIGHT_RED$INVALID_1$CLEAR\r\033[K$(overwrite "$INVALID_1")"
			((y_1-=3))
			((y_2--))
		fi
	done
	echo ""
}

## Check if mandatory variables like username, user password etc. have been set

function check_mandatory_variables () {
	while true; do
		if [[ -n $USERNAME && $HOSTNAME ]]; then
			break
		else
			echo -e "${BRIGHT_RED}Both username and hostname (name for the device) have to be assigned!$CLEAR\n"
			missing_names
			break
		fi
	done
	while true; do
		if [[ -n $LUKS_PASSPHRASE && $USER_PASSWORD ]]; then
			break
		else
			echo -e "${BRIGHT_RED}Both passwords for LUKS and the user have to be assigned!$CLEAR\n"
			missing_passwords
			break
		fi
	done
	while true; do
		if [[ -n $ROOT_PASSWORD ]]; then
			break
		else
			echo -n -e "${BRIGHT_YELLOW}Do you want to set a root password? [y/N] > $CLEAR"
			read reply
			if [[ $reply =~ (^[yY][eE][sS]$)|^[yY]$ ]]; then
				set_password "root"
				break
			elif [[ $reply =~ ^[nN][oO]{0,1}$ ]]; then
				echo ""
				break
			else
				echo -e "$BRIGHT_RED$INVALID_1$CLEAR\n"
			fi
		fi
	done
}

## Check for missing username and hostname variable values and prompt to assign them to their variables

function missing_names () {
	[[ -z $HOSTNAME ]] && set_name_variables "host"
	[[ -z $USERNAME ]] && set_name_variables "user"
}

function set_name_variables () {
	local invalid=false
	local name_type=$1

	function recursive_name_check () {
		if [[ $invalid = false ]]; then
			if [[ $name_type = "host" ]]; then
				echo -n -e "${BRIGHT_WHITE}Type in a hostname for the installation: $CLEAR"
				read supplied
				[[ -n $supplied ]] && { HOSTNAME=$supplied; echo ""; }
				[[ -z $supplied ]] && { invalid=true; echo -n -e "$BRIGHT_RED$INVALID_2$CLEAR\n"; recursive_name_check; }
			elif [[ $name_type = "user" ]]; then
				echo -n -e "${BRIGHT_WHITE}Type in a name for the required super-user with sudo priviliges: $CLEAR"
				read supplied
				[[ -n $supplied ]] && { USERNAME=$supplied; echo ""; }
				[[ -z $supplied ]] && { invalid=true; echo -n -e "$BRIGHT_RED$INVALID_2$CLEAR\n"; recursive_name_check; }
			fi
		elif [[ $invalid = true ]]; then
			echo -n -e "${BRIGHT_WHITE}Please re-enter: $CLEAR"
			read supplied
			[[ -n $supplied ]] && { invalid=false; HOSTNAME=$supplied; echo ""; }
			[[ -z $supplied ]] && { echo -n -e "$BRIGHT_RED$INVALID_2$CLEAR\n"; recursive_name_check; }
		fi
	}

	recursive_name_check
}

## Check for missing passwords and prompt to assign values to them

function missing_passwords () {
	[[ -z $LUKS_PASSPHRASE ]] && set_password "LUKS"
	[[ -z $USER_PASSWORD ]] && set_password "user"
}

function set_password () {
	local invalid=false
	local pw_denominator=$1
	local pw_type
	if [[ $pw_denominator = "LUKS" ]]; then
		pw_type="passphrase"
		local false_confirm_LUKS="Passphrase doesn't match..."
	else
		pw_type="password"
		local false_confirm="Password doesn't match..."
	fi

	function check_password () {
		while true; do
			echo -n -e "${BRIGHT_WHITE}Verify $pw_type: $CLEAR"
			read -s correct
			if [[ $correct = $supplied ]]; then
				[[ $pw_denominator = "LUKS" ]] && { LUKS_PASSPHRASE=$correct; echo -e "\n${BRIGHT_GREEN}Passphrase for $pw_denominator set!$CLEAR\n"; break; }
				[[ $pw_denominator = "root" ]] && { ROOT_PASSWORD=$correct; echo -e "\n${BRIGHT_GREEN}Password for $pw_denominator set!$CLEAR\n"; break; }
				[[ $pw_denominator = "user" ]] && { USER_PASSWORD=$correct; echo -e "\n${BRIGHT_GREEN}Password for $pw_denominator set!$CLEAR\n"; break; }
			else
				if [[ $pw_denominator = "LUKS" ]]; then
					echo -n -e "$BRIGHT_RED$false_confirm_LUKS$CLEAR"
					read -n 1 -s
					echo -n -e "$BRIGHT_RED$false_confirm_LUKS$CLEAR\r\033[K$(overwrite "$false_confirm_LUKS")"
				else
					echo -n -e "$BRIGHT_RED$false_confirm$CLEAR"
					read -n 1 -s
					echo -n -e "$BRIGHT_RED$false_confirm$CLEAR\r\033[K$(overwrite "$false_confirm")"
				fi
			fi
		done
	}

	function recursive_password_check () {
		if [[ $invalid = false ]]; then
			echo -n -e "${BRIGHT_WHITE}Enter $pw_type for $pw_denominator: $CLEAR"
			read -s supplied
			[[ -n $supplied ]] && { echo ""; check_password; }
			[[ -z $supplied ]] && { invalid=true; echo -n -e "$BRIGHT_RED$INVALID_2$CLEAR\n"; recursive_password_check; }
		elif [[ $invalid = true ]]; then
			echo -n -e "${BRIGHT_WHITE}Re-enter $pw_type for $pw_denominator: $CLEAR"
			read -s supplied
			[[ -n $supplied ]] && { invalid=false; echo ""; check_password; }
			[[ -z $supplied ]] && { echo -n -e "$BRIGHT_RED$INVALID_2$CLEAR\n"; recursive_password_check; }
		fi
	}

	recursive_password_check
}

## Encryption options

function encryption_options () {
	local options=("Full Disk Encryption" "Full Disk Encryption including boot partition")
	echo -e "\n${BRIGHT_YELLOW}Select how you want your system to be encrypted$CLEAR"
	select option in "${options[@]}"; do
		case $option in
			"Full Disk Encryption")
				ENCRYPTED_BOOT_PT=false
				break
				;;
			"Full Disk Encryption including boot partition")
				ENCRYPTED_BOOT_PT=true
				break
				;;
		esac
	done
}

## Partitioning

function partitioning_setup () {
	## Preparation

	# Check firmware type
	function firmware_type () {
		if [[ -d /sys/firmware/efi ]]; then
			echo -e "\n${BRIGHT_CYAN}Firmware type ${BRIGHT_GREEN}UEFI$BRIGHT_CYAN detected. The system will boot in ${BRIGHT_GREEN}UEFI$BRIGHT_CYAN mode.$CLEAR"
			FIRMWARE_TYPE="UEFI"
		else
			echo -e "\n${BRIGHT_CYAN}Firmware type ${BRIGHT_GREEN}BIOS$BRIGHT_CYAN detected. The system will boot in ${BRIGHT_GREEN}Legacy BIOS$BRIGHT_CYAN mode.$CLEAR"
			FIRMWARE_TYPE="BIOS"
		fi
	}

	# Check device type
	function device_type () {
		if [[ -n $(echo "$DISK" | grep "sd[a-z]") ]]; then
			DEVICE_TYPE_IS_SATA=true
		elif [[ -n $(echo "$DISK" | grep "nvme") ]]; then
			DEVICE_TYPE_IS_NVME=true
		fi
	}

	# Automatically set variables for partitioning
	function auto_partitioning_setup () {
		# Because partx prints out an error if there're no partitions to read, there needs to be a hack to mute the error message.
		# If an error occurs, this (usually) means there're no partitions to read which in turn means that the partition count is 0.
		if [[ $PARTX_EXIT -ne 0 ]]; then
			local partition_count=0
		else
			partition_count=$(partx --noheadings --show $DISK | wc --lines)
		fi
		# DEBUGGING: For Arch-only installs
		function with_both_ESP_and_boot_pt () {
			EFI_PT_PARTTOOL=$(( $partition_count + 1 ))
			BOOT_PT_PARTTOOL=$(( $partition_count + 2 ))
			ROOT_PT_PARTTOOL=$(( $partition_count + 3 ))
			if [[ $DEVICE_TYPE_IS_SATA = true ]]; then
				EFI_PT=$(( $partition_count + 1))
				BOOT_PT=$(( $partition_count + 2 ))
				ROOT_PT=$(( $partition_count + 3 ))
			elif [[ $DEVICE_TYPE_IS_NVME = true ]]; then
				EFI_PT="p$(( $partition_count + 1 ))"
				BOOT_PT="p$(( $partition_count + 2 ))"
				ROOT_PT="p$(( $partition_count + 3 ))"
			fi
		}
		# DEBUGGING: For dual boot with Windows installs
		function with_boot_pt () {
			BOOT_PT_PARTTOOL=$(( $partition_count + 1 ))
			ROOT_PT_PARTTOOL=$(( $partition_count + 2 ))
			if [[ $DEVICE_TYPE_IS_SATA = true ]]; then
				BOOT_PT=$(( $partition_count + 1 ))
				ROOT_PT=$(( $partition_count + 2 ))
			elif [[ $DEVICE_TYPE_IS_NVME = true ]]; then
				BOOT_PT="p$(( $partition_count + 1 ))"
				ROOT_PT="p$(( $partition_count + 2 ))"
			fi
		}
		if [[ $1 = "arch_only" ]]; then
			if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
				BOOT_PT_PARTTOOL="1"
				ROOT_PT_PARTTOOL="2"
			elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
				EFI_PT_PARTTOOL="1"
				BOOT_PT_PARTTOOL="2"
				ROOT_PT_PARTTOOL="3"
			fi
			if [[ $DEVICE_TYPE_IS_SATA = true ]]; then
				if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
					BOOT_PT="1"
					ROOT_PT="2"
				elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
					EFI_PT="1"
					BOOT_PT="2"
					ROOT_PT="3"
				fi
			elif [[ $DEVICE_TYPE_IS_NVME = true ]]; then
				if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
					BOOT_PT="p1"
					ROOT_PT="p2"
				elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
					EFI_PT="p1"
					BOOT_PT="p2"
					ROOT_PT="p3"
				fi
			fi
		else
			# DEBUGGING
			## For Arch-only installs
			#with_both_ESP_and_boot_pt
			## For dual boot with Windows installs
			with_boot_pt
		fi
	}

	# Display partition table changes, cryptsetup results or display attributes of physical volumes
	function check_results () {
		local prompt_type=$1
		local what_to_check=$2
		local prompt_for_individual_check="Would you like to check the results? [y/N] > "
		local prompt_for_menu_check="Would you like to check the results of the previous steps? [y/N] > "

		function press_enter () {
			local input=$1
			if [[ $input = "menu" ]]; then
				local prompt="Press [ENTER] to return to menu"
			else
				local prompt="Press [ENTER] to continue"
			fi
			echo -e "$BRIGHT_WHITE"
			read -p "$prompt"
			echo -n -e "$CLEAR"
		}
		function check_results_partitions () {
			partx --show $DISK
		}
		function check_results_parttool () {
			lsblk $DISK
			echo ""
			fdisk --list $DISK
		}
		function check_results_luks () {
			if [[ $ENCRYPTED_BOOT_PT = true ]]; then
				cryptsetup luksDump $DISK$BOOT_PT
				press_enter
			fi
			cryptsetup luksDump $DISK$ROOT_PT
		}
		function check_results_lvm () {
			pvdisplay --maps --verbose
		}
		function individual_check () {
			[[ $what_to_check = "partitions" ]] && check_results_partitions
			[[ $what_to_check = "parttool" ]] && check_results_parttool
			[[ $what_to_check = "cryptsetup" ]] && check_results_luks
			[[ $what_to_check = "lvm" ]] && check_results_lvm
			press_enter
		}
		function menu_check () {
			echo -n -e "
${BRIGHT_WHITE}Partitioning setup results menu$CLEAR"
			if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
				echo -n -e "
\n${BRIGHT_GREEN}1) ${BRIGHT_WHITE}Partitions set up with fdisk$CLEAR"
			elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
				echo -n -e "
\n${BRIGHT_GREEN}1) ${BRIGHT_WHITE}Partitions set up with gdisk$CLEAR"
			fi
			echo -n -e "
${BRIGHT_GREEN}2) ${BRIGHT_WHITE}Encryption results
${BRIGHT_GREEN}3) ${BRIGHT_WHITE}LVM results
${BRIGHT_RED}0) Exit menu\n
${BRIGHT_YELLOW}Choose an option: $CLEAR"
			read option
			case $option in
				1) check_results_parttool; press_enter "menu"; menu_check ;;
				2) check_results_luks; press_enter "menu"; menu_check ;;
				3) check_results_lvm; press_enter "menu"; menu_check ;;
				0) ;;
				*) echo -n -e "$BRIGHT_RED$INVALID_1$CLEAR"; read -n 1 -s; echo ""; menu_check ;;
			esac
		}
		while true; do
			if [[ $prompt_type = "individual" ]]; then
				echo -n -e "$BRIGHT_YELLOW$prompt_for_individual_check$CLEAR"
			elif [[ $prompt_type = "menu" ]]; then
				echo -n -e "\n$BRIGHT_YELLOW$prompt_for_menu_check$CLEAR"
			fi
			read -n 1 reply
			if [[ $reply =~ [yY] ]]; then
				if [[ $prompt_type = "individual" ]]; then
					echo -e "\n"
					individual_check
				elif [[ $prompt_type = "menu" ]]; then
					echo ""
					menu_check
				fi
				break
			elif [[ $reply =~ [nN] ]]; then
				echo ""
				break
			else
				echo -n -e "\b$BRIGHT_RED$INVALID_1$CLEAR"
				read -n 1 -s
				echo -n -e "$BRIGHT_RED$INVALID_1$CLEAR\r\033[K$(overwrite "$INVALID_1")"
			fi
		done
	}

	# Display all available disk devices
	function available_disk_devices () {
		local prompt="Do you want to see a list of all available disk devices? [y/N] > "
		echo ""
		while true; do
			echo -n -e "$BRIGHT_YELLOW$prompt$CLEAR"
			read -n 1 reply
			if [[ $reply =~ [yY] ]]; then
				echo ""
				lsblk --all
				break
			elif [[ $reply =~ [nN] ]]; then
				echo ""
				break
			else
				echo -n -e "\b$BRIGHT_RED$INVALID_1$CLEAR"
				read -n 1 -s
				echo -n -e "$BRIGHT_RED$INVALID_1$CLEAR\r\033[K$(overwrite "$INVALID_1")"
			fi
		done
		echo ""
	}

	# Prompt for manual input of disk device
	function manual_disk_input () {
		while true; do
			echo -n -e "${BRIGHT_WHITE}Type in which disk to install to (e.g. sda or nvme0n1): $CLEAR"
			read dest_disk
			if [[ -n $dest_disk ]]; then
				# Checking if disk exists
				if [[ -d $DIR/$dest_disk ]]; then
					# Checking criterias for disk
					if [[ $(cat $DIR/$dest_disk/removable) -eq 0 ]]; then
						# Converting Block size to GB
						if [[ $(( $(cat $DIR/$dest_disk/size) / 1953125 )) -ge $MINSIZE ]]; then
							check_for_partitions "manual" "$dest_disk"
							break
						else
							echo -e "\n${BRIGHT_RED}The disk should not have less than $MINSIZE GB of free space!$CLEAR\n"
							local prompt="Do you want to use this disk nonetheless? [y/N] > "
							while true; do
								echo -n -e "$BRIGHT_YELLOW$prompt$CLEAR"
								read reply
								if [[ $reply =~ (^[yY][eE][sS]$)|^[yY]$ ]]; then
									check_for_partitions "manual" "$dest_disk"
									break
								elif [[ $reply =~ ^[nN][oO]{0,1}$ ]]; then
									manual_disk_input
									break
								else
									echo -n -e "\b$BRIGHT_RED$INVALID_1$CLEAR"
									read -n 1 -s
									echo -n -e "$BRIGHT_RED$INVALID_1$CLEAR\r\033[K$(overwrite "$INVALID_1")"
								fi
							done
							break
						fi
					else
						echo -e "${BRIGHT_RED}The disk device must not be removable!$CLEAR\n"
					fi
				else
					echo -e "${BRIGHT_RED}Disk doesn't exist.$CLEAR\n"
				fi
			else
				echo -e "$BRIGHT_RED$INVALID_2$CLEAR\n"
			fi
		done
	}

	# Determine which disk to pick and assign it to the DISK variable
	function determine_disk_to_pick () {
		function free_disk_space_lsblk () {
			# Because partx prints out an error if there're no partitions to read, there needs to be a hack to mute the error message.
			# If an error occurs, this (usually) means there're no partitions to read which in turn means that the free disk space should be equal to the disks size.
			partx --show /dev/$disk &> /dev/null
			PARTX_EXIT=$?
			if [[ $PARTX_EXIT -ne 0 ]]; then
				FREE_DISK_SPACE=$(lsblk --bytes --noheadings --output SIZE /dev/$disk | head --lines=1)
				FREE_DISK_SPACE=$(( $FREE_DISK_SPACE / 2**30 ))
			else
				local partition_count=$(partx --noheadings --show /dev/$disk | wc --lines)
				local partition_size_sum=0
				for (( i=1; i<=$partition_count; i++ )); do
					partition_size_sum=$(( $partition_size_sum + $(lsblk --bytes --noheadings --output SIZE /dev/${disk}p$i) ))
				done
				FREE_DISK_SPACE=$(( $(lsblk --bytes --noheadings --output SIZE /dev/$disk | head --lines=1) - $partition_size_sum ))
				FREE_DISK_SPACE=$(( $FREE_DISK_SPACE / 2**30 ))
			fi
		}
		for disk in $DIR/sd?* $DIR/nvme?*; do
			disk=$(echo "$disk" | awk --field-separator '/' '{print $NF}')
			if [[ -d $DIR/$disk ]]; then
				local removable=$(cat $DIR/$disk/removable)
				if [[ $removable -eq 0 ]]; then
					local block_size=$(cat $DIR/$disk/size)
					# Convert Block size to gibibytes (1 GiB = 2**21 Block)
					## 1 Gibibyte (GiB) = 1024**3 Byte or 2**30 Byte
					## 1 Block = 512 Byte, 1/512 Block = 1 Byte, (2**30)/512 Block or 2**21 (2097152) Block = 2**30 Byte = 1 GiB
					## GiB=$(( $block_size / 2**21 ))
					# Convert Block size to gigabytes (1 GB = (10**9)/512 Block)
					## 1 Gigabyte (GB) = 1000**3 Byte or 10**9 Byte
					## 1 Block = 512 Byte, 1/512 Block = 1 Byte, (10**9)/512 (1953125) Block = 10**9 Byte = 1 GB
					local GB=$(( $block_size / 1953125 ))
					# DEBUGGING
					# Check free $disk space in Byte and convert to GB
					## Using df -> Does not detect my SSD (nvme0n1 device) for some reason...
					#local free_disk_space=$(( $(df --block-size=1 --output=avail /dev/$disk | tail --lines=1) / 10**9 ))
					## Using lsblk's size value
					free_disk_space_lsblk
					## Using parted: https://stackoverflow.com/questions/12313384/how-to-view-unallocated-free-space-on-a-hard-disk-through-terminal
					# Check if size of block device (/dev/$disk) is greater or equal to $MINSIZE
					if [[ $GB -ge $MINSIZE ]]; then
						echo -e "\n${BRIGHT_CYAN}Found $BRIGHT_GREEN/dev/$disk$BRIGHT_CYAN to be sufficient. It has about $BRIGHT_GREEN${FREE_DISK_SPACE}GB$BRIGHT_CYAN of free disk space available."
						# Ask whether to use the found disk
						while true; do
							echo -n -e "${BRIGHT_YELLOW}Do you want to use this disk? [y/N] > $CLEAR"
							read reply
							# 'yes' -> checks for partitions
							if [[ $reply =~ (^[yY][eE][sS]$)|^[yY]$ ]]; then
								DISK="/dev/$disk"
								check_for_partitions "automatic"
								break
							# 'no' -> manual setup
							elif [[ $reply =~ ^[nN][oO]{0,1}$ ]]; then
								available_disk_devices
								manual_disk_input
								break
							else
								echo -e "$BRIGHT_RED$INVALID_1$CLEAR\n"
							fi
						done
					else
						echo -e "\n${BRIGHT_RED}No disk with the storage capacity you specified was found. Please manually specify on which disk to install.$CLEAR"
						available_disk_devices
						manual_disk_input
					fi
				fi
			fi
		done
	}

	# Check for existing partitions
	function check_for_ESP () {
		fdisk --list $DISK | grep --ignore-case --quiet "EFI"
		local grep_exit=$?
		if [[ $grep_exit -eq 0 ]]; then
			echo -e "\n${BRIGHT_CYAN}Firmware type ${BRIGHT_GREEN}UEFI$BRIGHT_CYAN detected. The system will boot in ${BRIGHT_GREEN}UEFI$BRIGHT_CYAN mode.$CLEAR\n"
			FIRMWARE_TYPE="UEFI"
		else
			echo -e "\n${BRIGHT_CYAN}Firmware type ${BRIGHT_GREEN}BIOS$BRIGHT_CYAN detected. The system will boot in ${BRIGHT_GREEN}Legacy BIOS$BRIGHT_CYAN mode.$CLEAR\n"
			FIRMWARE_TYPE="BIOS"
		fi
		# Set Windows' ESP
		if [[ $(partx --noheadings --show $DISK | wc --lines) -gt 4 ]]; then
			echo -e "${BRIGHT_RED}The script couldn't figure out which partition is the Windows EFI system partition.$CLEAR"
			while true; do
				echo -n -e "\n${BRIGHT_WHITE}Please manually input the device path, i.e. /dev/...:$CLEAR "
				read input
				if [[ -n $input ]]; then
					# Check if partition exists
					fdisk --list | grep --quiet "$input"
					local grep_exit=$?
					if [[ $grep_exit -eq 0 ]]; then
						WIN_ESP_PATH=$input
						break
					else
						echo -e "${BRIGHT_RED}This partition doesn't exist.$CLEAR\n"
					fi
				else
					echo -e "$BRIGHT_RED$INVALID_2$CLEAR\n"
				fi
			done
		else
			WIN_ESP_PATH=$(fdisk --list | grep --ignore-case "EFI" | grep --extended-regexp --only-matching "/dev/(sd[a-z][1-9]*|nvme0n1p[1-9]*)")
		fi
	}
	function check_for_windows_partitions () {
		lsblk --list --noheadings --output NAME,SIZE,TYPE,FSTYPE,PTTYPE,LABEL $DISK | grep --ignore-case --quiet "ntfs"
		local grep_exit=$?
		if [[ $grep_exit -eq 0 ]]; then
			WIN_PARTITION_FOUND=true
			echo -e "${BRIGHT_CYAN}Existing Windows partitions have been found on $DISK.$CLEAR"
			# Check for EFI system partition (ESP)
			check_for_ESP
		fi
	}
	function check_for_partitions () {
		local select=$1
		local destination=$2
		WIN_PARTITION_FOUND=false
		if [[ $select = "automatic" ]]; then
			if [[ $PARTX_EXIT -eq 0 ]]; then
				echo -e "\n${BRIGHT_CYAN}Existing partitions have been found on $DISK.$CLEAR"
				check_for_windows_partitions
				check_results "individual" "partitions"
			else
				echo -e "\n${BRIGHT_CYAN}No partitions have been found on $DISK.$CLEAR"
			fi
		elif [[ $select = "manual" ]]; then
			partx --show /dev/$destination &> /dev/null
			PARTX_EXIT=$?
			if [[ $PARTX_EXIT -eq 0 ]]; then
				DISK="/dev/$destination"
				echo -e "\n${BRIGHT_CYAN}Existing partitions have been found on $DISK.$CLEAR"
				check_for_windows_partitions
				check_results "individual" "partitions"
			else
				DISK="/dev/$destination"
				echo -e "\n${BRIGHT_CYAN}No partitions have been found on $DISK.$CLEAR"
			fi
		fi
	}

	# Disk wipe function
	function wipe () {
		local prompt="Do you want to wipe the disk the zeroes? [y/N] > "
		echo ""
		while true; do
			echo -n -e "$BRIGHT_YELLOW$prompt$CLEAR"
			read reply
			if [[ $reply =~ (^[yY][eE][sS]$)|^[yY]$ ]]; then
				echo -e "\n${BRIGHT_CYAN}Wiping disk$CLEAR..."
				dd if=/dev/zero of=$DISK status=progress
				break
			elif [[ $reply =~ ^[nN][oO]{0,1}$ ]]; then
				break
			else
				echo -n -e "\b$BRIGHT_RED$INVALID_1$CLEAR"
				read -n 1 -s
				echo -n -e "$BRIGHT_RED$INVALID_1$CLEAR\r\033[K$(overwrite "$INVALID_1")"
			fi
		done
	}

	## Action

	# Actual partitioning with fdisk/gdisk
	function actual_partitioning_with_fdisk_and_gdisk () {
		function partitioning_for_BIOS () {
			cat << EOF | fdisk $DISK
$PT_TABLE_TYPE
n
p
$BOOT_PT_PARTTOOL

+512M
n
p
$ROOT_PT_PARTTOOL


t
$ROOT_PT_PARTTOOL
8e
a
$BOOT_PT_PARTTOOL
w
EOF
		}

		function partitioning_for_UEFI () {
			### v (just for Arch, Windows won't be detected by os-prober due to Arch's separate ESP)
			# DEBUGGING: For Arch-only installs
			# Source: https://www.youtube.com/watch?v=DPLnBPM4DhI
			function UEFI_gdisk_both_ESP_and_boot_pt () {
				cat << EOF | gdisk $DISK
$PT_TABLE_TYPE
Y
n
$EFI_PT_PARTTOOL

+512M
ef00
c
$EFI_PT_PARTTOOL
Linux EFI system partition
n
$BOOT_PT_PARTTOOL

+512M
8300
c
$BOOT_PT_PARTTOOL
Linux boot partition
n
$ROOT_PT_PARTTOOL


8e00
c
$ROOT_PT_PARTTOOL
Arch Linux root partition
w
Y
EOF
			}
			### v (/mnt/boot/efi & /mnt/efi)
			# DEBUGGING: For dual boot with Windows installs
			# Windows' ESP will be used as the system-wide ESP
			# Note: Mount Windows' ESP at /mnt/boot/efi or /mnt/efi
			# Source: https://www.youtube.com/watch?v=ybvwikNlx9I
			function UEFI_gdisk_only_boot_pt () {
				cat << EOF | gdisk $DISK
n
$BOOT_PT_PARTTOOL

+512M
8300
c
$BOOT_PT_PARTTOOL
Linux boot partition
n
$ROOT_PT_PARTTOOL


8e00
c
$ROOT_PT_PARTTOOL
Arch Linux root partition
w
Y
EOF
			}
			# DEBUGGING
			## For Arch-only installs
			#UEFI_gdisk_both_ESP_and_boot_pt
			## For dual boot installs
			UEFI_gdisk_only_boot_pt
		}
		if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
			if [[ $DUAL_BOOT_WITH_WIN = true ]]; then
				# Keep existing partition table
				PT_TABLE_TYPE=""
				echo -e "\n${BRIGHT_BLUE}Partitioning disk$CLEAR"
			else
				# Create a new empty MBR partition table using fdisk
				PT_TABLE_TYPE="o"
				echo -e "\n${BRIGHT_BLUE}Partitioning disk using the MBR partition table$CLEAR"
			fi
			partitioning_for_BIOS > /dev/null
		elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
			if [[ $DUAL_BOOT_WITH_WIN = true ]]; then
				# Keep existing partition table
				PT_TABLE_TYPE=""
				echo -e "\n${BRIGHT_BLUE}Partitioning disk$CLEAR"
			else
				# Create a new empty GUID Partition Table (GPT) using gdisk
				PT_TABLE_TYPE="o"
				echo -e "\n${BRIGHT_BLUE}Partitioning disk using the GUID Partition Table (GPT)$CLEAR"
			fi
			partitioning_for_UEFI > /dev/null
		fi
	}

	# Actual partitioning with parted
	function actual_partitioning_with_parted () {
		# Partition name variables
		local esp_name_for_parted="Linux_EFI_system_partition"
		local boot_pt_name_for_parted="Linux_boot_partition"
		local root_pt_name_for_parted="Arch_Linux_root_partition"
		# Sector variables
		## 1. Outputs information of $DISK - among others sector sizes
		## 2. Looks at last line with text in it (parteds print command prints an empty new line at the end)
		## 3. Greps only digits larger than two characters to only get the sector sizes
		## 4. Cuts out the last line and then cuts out the first line of the remaining two lines; the result is the end sector size of the last partition
		local end_sectors_of_last_partition=$(parted $DISK unit s print | tail --lines=2 | grep --extended-regexp --only-matching "[0-9]{2,}" | head --lines=2 | tail --lines=1)
		## Adds 512MiB (in sector size) to $end_sectors_of_last_partition, thus getting the end sector size for the boot partition which in turn is the start sector size for the root partition
		local end_and_start_sectors_of_boot_and_root_partitions=$(( $end_sectors_of_last_partition + 1048576 ))
		# parted input chain for dual boot installs
		local parted_BIOS_for_dual_boot="mkpart primary ext4 ${end_sectors_of_last_partition}MiB ${end_and_start_sectors_of_boot_and_root_partitions}MiB set $BOOT_PT_PARTTOOL boot on mkpart primary ext4 ${end_and_start_sectors_of_boot_and_root_partitions}MiB 100%"
		local parted_UEFI_for_dual_boot="mkpart $boot_pt_name_for_parted ext4 ${end_sectors_of_last_partition}MiB ${end_and_start_sectors_of_boot_and_root_partitions}MiB set $BOOT_PT_PARTTOOL boot on mkpart $root_pt_name_for_parted ext4 ${end_and_start_sectors_of_boot_and_root_partitions}MiB 100% set $ROOT_PT_PARTTOOL lvm on"
		# parted input chain for Arch-only installs
		local parted_BIOS="mklabel msdos mkpart primary ext4 1MiB 513MiB set $BOOT_PT_PARTTOOL boot on mkpart primary ext4 513MiB 100%"
		local parted_UEFI="mklabel gpt mkpart $esp_name_for_parted fat32 1MiB 513MiB set $EFI_PT_PARTTOOL esp on mkpart $boot_pt_name_for_parted ext4 513MiB 1025MiB mkpart $root_pt_name_for_parted ext4 1025MiB 100% set $ROOT_PT_PARTTOOL lvm on"

		if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
			if [[ $DUAL_BOOT_WITH_WIN = true ]]; then
				echo -e "\n${BRIGHT_BLUE}Partitioning disk$CLEAR"
				parted --script $DISK $parted_BIOS_for_dual_boot
			else
				echo -e "\n${BRIGHT_BLUE}Partitioning disk using the MBR partition table$CLEAR"
				parted --script $DISK $parted_BIOS
			fi
		elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
			if [[ $DUAL_BOOT_WITH_WIN = true ]]; then
				echo -e "\n${BRIGHT_BLUE}Partitioning disk$CLEAR"
				parted --script $DISK $parted_UEFI_for_dual_boot
			else
				echo -e "\n${BRIGHT_BLUE}Partitioning disk using the GUID Partition Table (GPT)$CLEAR"
				parted --script $DISK $parted_UEFI
			fi
		fi
	}

	# Encrypt partitions using LUKS and open them
	function LUKS_part () {
		if [[ $ENCRYPTED_BOOT_PT = true ]]; then
			local luks_version="luks1"
		else
			local luks_version="luks2"
		fi
		# Create LUKS partitions
		## This creates LUKS encrypted containers at the boot and root partition.
		echo -e "\n${BRIGHT_BLUE}Creating LUKS partitions$CLEAR"
		if [[ $ENCRYPTED_BOOT_PT = true ]]; then
			echo -n -e "Setting up boot partition as encrypted LUKS partition"
			echo -e "$LUKS_PASSPHRASE" | cryptsetup --cipher aes-xts-plain64 --key-size 512 --hash sha256 --iter-time 2000 --use-urandom --type $luks_version luksFormat $DISK$BOOT_PT > /dev/null &
			PID=$!; wait_animation "$PID" "dot"; echo ""
		fi
		echo -n -e "Setting up root partition as encrypted LUKS partition"
		echo -e "$LUKS_PASSPHRASE" | cryptsetup --cipher aes-xts-plain64 --key-size 512 --hash sha256 --iter-time 2000 --use-urandom --type $luks_version luksFormat $DISK$ROOT_PT > /dev/null &
		PID=$!; wait_animation "$PID" "dot"; echo ""

		# Unlock/Map LUKS partitions
		## This will map the partitions to a new device name using the device mapper.
		echo -e "\n${BRIGHT_BLUE}Unlocking LUKS partitions$CLEAR"
		if [[ $ENCRYPTED_BOOT_PT = true ]]; then
			echo -n -e "Opening LUKS boot container as $LUKS_BOOT"
			echo -e "$LUKS_PASSPHRASE" | cryptsetup luksOpen $DISK$BOOT_PT $LUKS_BOOT > /dev/null &
			PID=$!; wait_animation "$PID" "dot"; echo ""
		fi
		echo -n -e "Opening LUKS root container as $LUKS_LVM"
		echo -e "$LUKS_PASSPHRASE" | cryptsetup luksOpen $DISK$ROOT_PT $LUKS_LVM > /dev/null &
		PID=$!; wait_animation "$PID" "dot"; echo ""
	}

	# Configure LVM for partitions
	function LVM_manage () {
		function LVM_commands () {
			# Create a physical volume on top of the opened LUKS root container
			pvcreate /dev/mapper/$LUKS_LVM
			# Set volume group name variable
			VG="$HOSTNAME-vg"
			# Create a volume group, adding the previously created physical volume to it
			vgcreate --force $VG /dev/mapper/$LUKS_LVM
			# Define logical volumes in this volume group
			#lvcreate --size 2G $VG --name swap
			lvcreate --extents 25%VG $VG --name root
			lvcreate --extents 100%FREE $VG --name home
			# Load necessary kernel module, scan for volume groups and activate them
			modprobe dm_mod
			vgscan
			vgchange --activate y
		}
		# By calling the necessary LVM commands with a function all of their outputs can be muted by muting the function. Otherwise, each commands output would have to be muted individually.
		echo -e "\n${BRIGHT_BLUE}Logical Volume Management$CLEAR"
		LVM_commands > /dev/null
	}

	# Format filesystems
	function format_fs () {
		function format_fs_for_BIOS () {
			mkfs.ext4 -L ROOT /dev/$VG/root
			mkfs.ext4 -L HOME /dev/$VG/home
			#mkswap -L SWAP /dev/$VG/swap
			if [[ $ENCRYPTED_BOOT_PT = false ]]; then
				mkfs.ext4 -L BOOT $DISK$BOOT_PT
			else
				mkfs.ext4 -L BOOT /dev/mapper/$LUKS_BOOT
			fi
		}

		function format_fs_for_UEFI () {
			### v (but only just for Arch, Windows won't be detected by os-prober due to Arch's separate ESP)
			# DEBUGGING: For Arch-only installs
			# Source: https://www.youtube.com/watch?v=DPLnBPM4DhI
			function UEFI_fs_both_ESP_and_boot_pt () {
				mkfs.ext4 -L ROOT /dev/$VG/root
				mkfs.ext4 -L HOME /dev/$VG/home
				#mkswap -L SWAP /dev/$VG/swap
				mkfs.ext4 -L BOOT $DISK$BOOT_PT
				mkfs.fat -F32 -n ESP $DISK$EFI_PT
			}
			### v (/mnt/boot/efi & /mnt/efi)
			# DEBUGGING: For dual boot with Windows installs
			# Windows' ESP will be used as the system-wide ESP
			# Note: Mount Windows' ESP at /mnt/boot/efi or /mnt/efi
			# I don't really see the point for the boot partition...
			# Source: https://www.youtube.com/watch?v=ybvwikNlx9I
			function UEFI_fs_only_boot_pt () {
				mkfs.ext4 -L ROOT /dev/$VG/root
				mkfs.ext4 -L HOME /dev/$VG/home
				#mkswap -L SWAP /dev/$VG/swap
				mkfs.ext4 -L BOOT $DISK$BOOT_PT
			}
			# DEBUGGING
			## For Arch-only installs
			#UEFI_fs_both_ESP_and_boot_pt
			## For dual boot installs
			UEFI_fs_only_boot_pt
		}

		echo -e "\n${BRIGHT_BLUE}Formatting filesystems$CLEAR"
		if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
			format_fs_for_BIOS &> /dev/null
		elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
			format_fs_for_UEFI &> /dev/null
		fi
	}

	# Mount filesystems
	function mount_fs () {
		function mount_fs_for_BIOS () {
			mount /dev/$VG/root /mnt
			mkdir /mnt/home
			mount /dev/$VG/home /mnt/home
			#swapon /dev/$VG/swap
			mkdir /mnt/boot
			if [[ $ENCRYPTED_BOOT_PT = false ]]; then
				mount $DISK$BOOT_PT /mnt/boot
			else
				mount /dev/mapper/$LUKS_BOOT /mnt/boot
			fi
		}

		function mount_fs_for_UEFI () {
			### v (but only just for Arch, Windows won't be detected by os-prober due to Arch's separate ESP)
			# DEBUGGING: For Arch-only installs
			# Source: https://www.youtube.com/watch?v=DPLnBPM4DhI
			function UEFI_mount_both_ESP_and_boot_pt () {
				mount /dev/$VG/root /mnt
				mkdir /mnt/home
				mount /dev/$VG/home /mnt/home
				#swapon /dev/$VG/swap
				mkdir /mnt/boot
				mount $DISK$BOOT_PT /mnt/boot
				mkdir /mnt/boot/efi
				mount $DISK$EFI_PT /mnt/boot/efi
			}
			### v (/mnt/boot/efi & /mnt/efi)
			# DEBUGGING: For dual boot with Windows installs
			# Windows' ESP will be used as the system-wide ESP
			# Note: Mount Windows' ESP at /mnt/boot/efi or /mnt/efi
			# I don't really see the point for the boot partition...
			# Source: https://www.youtube.com/watch?v=ybvwikNlx9I
			function UEFI_mount_only_boot_pt () {
				mount /dev/$VG/root /mnt
				mkdir /mnt/home
				mount /dev/$VG/home /mnt/home
				#swapon /dev/$VG/swap
				mkdir /mnt/boot
				mount $DISK$BOOT_PT /mnt/boot
				if [[ $DUAL_BOOT_WITH_WIN = true ]]; then
					mkdir /mnt/boot/efi
					mount $WIN_ESP_PATH /mnt/boot/efi
				fi
			}
			# DEBUGGING
			## For Arch-only installs
			#UEFI_mount_both_ESP_and_boot_pt
			## For dual boot installs
			UEFI_mount_only_boot_pt
		}

		echo -e "\n${BRIGHT_BLUE}Mounting filesystems$CLEAR"
		if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
			mount_fs_for_BIOS > /dev/null
		elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
			mount_fs_for_UEFI > /dev/null
		fi
	}

	# Select installation setup and set partition variables accordingly
	function select_install_setup () {
		local options=("Arch Linux-only Setup:  Wipes everything and installs Arch Linux" "Dual boot Setup:  Installs Arch Linux alongside another OS, e.g. Windows")
		echo -e "\n${BRIGHT_YELLOW}Select how you want your system to be set up$CLEAR"
		select setup in "${options[@]}"; do
			case $setup in
				"${options[0]}")
					DUAL_BOOT_WITH_WIN=false
					[[ -z $FIRMWARE_TYPE ]] && firmware_type
					[[ $FIRMWARE_TYPE = "BIOS" ]] && encryption_options
					device_type
					auto_partitioning_setup "arch_only"
					wipe
					break
					;;
				"${options[1]}")
					DUAL_BOOT_WITH_WIN=true
					[[ -z $FIRMWARE_TYPE ]] && firmware_type
					[[ $FIRMWARE_TYPE = "BIOS" ]] && encryption_options
					device_type
					auto_partitioning_setup
					break
					;;
			esac
		done
	}

	# Pick the first disk that is not removable and is greater than or equal to MINSIZE
	DIR="/sys/block"
	# Minimum size of hard drive/solid state drive needed specified in GIBIBYTES; default: 120 GB
	MINSIZE="${1:-120}"
	local options=("Automatic Setup:  Picks the \"next best\" disk to install to (recommended for laptops)" "Manual Setup:  Type in which disk to install to")
	echo -e "\n${BRIGHT_YELLOW}Select a type of setup$CLEAR"
	select type in "${options[@]}"; do
		case $type in
			"${options[0]}")
				determine_disk_to_pick
				if [[ $WIN_PARTITION_FOUND = true ]]; then
					select_install_setup
				else
					[[ -z $FIRMWARE_TYPE ]] && firmware_type
					[[ $FIRMWARE_TYPE = "BIOS" ]] && encryption_options
					device_type
					auto_partitioning_setup
					wipe
				fi
				break
				;;
			"${options[1]}")
				available_disk_devices
				manual_disk_input
				if [[ $WIN_PARTITION_FOUND = true ]]; then
					select_install_setup
				else
					[[ -z $FIRMWARE_TYPE ]] && firmware_type
					[[ $FIRMWARE_TYPE = "BIOS" ]] && encryption_options
					device_type
					auto_partitioning_setup
					wipe
				fi
				break
				;;
		esac
	done
	# DEBUGGING
	actual_partitioning_with_fdisk_and_gdisk
	#actual_partitioning_with_parted
	partprobe $DISK
	LUKS_part
	LVM_manage
	format_fs
	mount_fs
	check_results "menu"
}

## Pre-Installation

function prepare () {
	echo -e "\n${BRIGHT_BLUE}Updating system clock$CLEAR"
	timedatectl set-ntp true
	if [[ $REFLECTOR = true ]]; then
		echo -e "\n${BRIGHT_BLUE}Selecting mirrors$CLEAR"
		pacman --sync --refresh --noconfirm reflector
		reflector --country $REFLECTOR_COUNTRIES --latest 25 --protocol https --sort rate --save /etc/pacman.d/mirrorlist
	fi
	echo -e "\n${BRIGHT_BLUE}Syncing packages database$CLEAR"
	pacman --sync --refresh --noconfirm
}

## Installation

function base_install () {
	if [[ -n $(lscpu | grep GenuineIntel) ]]; then
		VENDOR_ID="intel"
	elif [[ -n $(lscpu | grep AuthenticAMD) ]]; then
		VENDOR_ID="amd"
	fi
	echo -n -e "\n${BRIGHT_BLUE}Installing essential packages$CLEAR...  "
	pacstrap /mnt base base-devel linux linux-lts linux-headers linux-lts-headers linux-firmware lvm2 grub dosfstools os-prober mtools $VENDOR_ID-ucode &> /dev/null &
	PID=$!; wait_animation "$PID" "spinner"
}

## Configure the system

function configuration () {
	echo -e "\n${BRIGHT_BLUE}Generating fstab$CLEAR"
	genfstab -U /mnt >> /mnt/etc/fstab

	# DEBUGGING
	#if [ "$DEVICE_TRIM" == "true" ]; then
    #    if [ "$FILE_SYSTEM_TYPE" == "f2fs" ]; then
    #        sed -i 's/relatime/noatime,nodiscard/' /mnt/etc/fstab
    #    else
    #        sed -i 's/relatime/noatime/' /mnt/etc/fstab
    #    fi
    #    arch-chroot /mnt systemctl enable fstrim.timer
    #fi

	echo -e "\n${BRIGHT_BLUE}Setting time zone$CLEAR"
	arch-chroot /mnt ln --force --symbolic $TIMEZONE /etc/localtime
	arch-chroot /mnt hwclock --systohc --$TIME

	echo -n -e "\n${BRIGHT_BLUE}Localization configuration$CLEAR... "
	for locale in "${LOCALES[@]}"; do
		sed --in-place "s/#$locale/$locale/" /mnt/etc/locale.gen
	done
	echo -e $LOCALE_CONF > /mnt/etc/locale.conf
	arch-chroot /mnt locale-gen &> /dev/null &
	PID=$!; wait_animation "$PID" "spinner"
	echo -e "KEYMAP=$KEYMAP" > /mnt/etc/vconsole.conf
	loadkeys $KEYMAP
	# DEBUGGING
    #arch-chroot /mnt localectl set-keymap --no-convert $KEYBOARD_LAYOUT

	echo -e "\n${BRIGHT_BLUE}Network configuration$CLEAR"
	echo $HOSTNAME > /mnt/etc/hostname
	cat << EOF > /mnt/etc/hosts
127.0.0.1		localhost
::1				localhost
127.0.1.1		${HOSTNAME}.localdomain		$HOSTNAME
EOF

	echo -e "\n${BRIGHT_BLUE}Initramfs setup$CLEAR"
	sed --in-place "s/HOOKS=([^)]*)/HOOKS=(base udev autodetect keyboard keymap modconf block encrypt lvm2 filesystems fsck)/" /mnt/etc/mkinitcpio.conf
	if [[ $ENCRYPTED_BOOT_PT = true ]]; then
		# Creating keyfiles...
		dd if=/dev/random of=/mnt/etc/$LUKS_BOOT.keyfile bs=1 count=256
		dd if=/dev/random of=/mnt/root/$LUKS_LVM.keyfile bs=1 count=256
		# ... and adding them as LUKS keys
		echo -n -e "Adding keyfile as LUKS key for boot partition"
		echo -e "$LUKS_PASSPHRASE" | cryptsetup --batch-mode luksAddKey $DISK$BOOT_PT /mnt/etc/cryptboot.keyfile > /dev/null &
		PID=$!; wait_animation "$PID" "dot"; echo ""
		echo -n -e "Adding keyfile as LUKS key for root partition"
		echo -e "$LUKS_PASSPHRASE" | cryptsetup --batch-mode luksAddKey $DISK$ROOT_PT /mnt/root/cryptroot.keyfile > /dev/null &
		PID=$!; wait_animation "$PID" "dot"; echo ""
		# Adding the keyfile to the initramfs image
		echo -e "$LUKS_BOOT $DISK$BOOT_PT /etc/$LUKS_BOOT.keyfile luks" > /mnt/etc/crypttab
		sed --in-place "s|FILES=()|FILES=(/root/$LUKS_LVM.keyfile)|" /mnt/etc/mkinitcpio.conf
	fi
	echo -n -e "${BRIGHT_CYAN}Creating initial ramdisk environment for linux$CLEAR...  "
	arch-chroot /mnt mkinitcpio --preset linux &> /dev/null &
	PID=$!; wait_animation "$PID" "spinner"
	echo -n -e "${BRIGHT_CYAN}Creating initial ramdisk environment for linux-lts$CLEAR...  "
	arch-chroot /mnt mkinitcpio --preset linux-lts &> /dev/null &
	PID=$!; wait_animation "$PID" "spinner"

	echo -e "\n${BRIGHT_BLUE}Creating user with sudo priviliges$CLEAR"
	NAME=$1
	PASSWORD=$2
	arch-chroot /mnt useradd --groups wheel --create-home $NAME &> /dev/null
	echo -e "$PASSWORD\n$PASSWORD" | arch-chroot /mnt passwd $NAME &> /dev/null
	arch-chroot /mnt sed --in-place "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/" /etc/sudoers

	if [[ -n $ROOT_PASSWORD ]]; then
		echo -e "\n${BRIGHT_BLUE}Setting root password$CLEAR"
		echo -e "$ROOT_PASSWORD\n$ROOT_PASSWORD" | arch-chroot /mnt passwd &> /dev/null
	fi

	echo -e "\n${BRIGHT_BLUE}Configuring bootloader (GRUB)$CLEAR"
	if [[ $FIRMWARE_TYPE = "BIOS" ]]; then
		# DEBUGGING
		arch-chroot /mnt grub-install --target=i386-pc --recheck $DISK #&> /dev/null
	elif [[ $FIRMWARE_TYPE = "UEFI" ]]; then
		# DEBUGGING
		arch-chroot /mnt pacman --sync --sysupgrade --refresh --noconfirm efibootmgr #&> /dev/null
		arch-chroot /mnt grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB --recheck #&> /dev/null
		#arch-chroot /mnt grub-install --target=x86_64-efi --efi-directory=/efi --bootloader-id=GRUB --recheck #&> /dev/null
	fi
	# Inform GRUB to trigger unlock of disk at boot
	sed --in-place "s/#GRUB_ENABLE_CRYPTODISK=y/GRUB_ENABLE_CRYPTODISK=y/" /mnt/etc/default/grub
	# Inform GRUB which partition is encrypted
	if [[ $ENCRYPTED_BOOT_PT = true ]]; then
		# Set the following kernel parameters to unlock the LUKS partition with the keyfile using the encrypt hook
		sed --in-place "s|GRUB_CMDLINE_LINUX=\"\"|GRUB_CMDLINE_LINUX=\"cryptdevice=$DISK$ROOT_PT:$LUKS_LVM cryptkey=rootfs:/root/$LUKS_LVM.keyfile\"|" /mnt/etc/default/grub
	else
		# DEBUGGING
		#UUID_BOOT=$(blkid -s UUID -o value $PARTITION_BOOT)
    	#UUID_ROOT=$(blkid -s UUID -o value $PARTITION_ROOT)
    	#PARTUUID_BOOT=$(blkid -s PARTUUID -o value $PARTITION_BOOT)
    	#PARTUUID_ROOT=$(blkid -s PARTUUID -o value $PARTITION_ROOT)
		#if [ "$DEVICE_TRIM" == "true" ]; then
        #    BOOTLOADER_ALLOW_DISCARDS=":allow-discards"
        #fi
        #CMDLINE_LINUX="cryptdevice=UUID=$UUID_ROOT:$LUKS_DEVICE_NAME$BOOTLOADER_ALLOW_DISCARDS"
		sed --in-place "s|GRUB_CMDLINE_LINUX=\"\"|GRUB_CMDLINE_LINUX=\"cryptdevice=$DISK$ROOT_PT:$LUKS_LVM root=/dev/$VG/root\"|" /mnt/etc/default/grub
	fi
	# Fix for dual boot: Enable os-prober to detect Windows Boot Manager
	cat << EOF >> /mnt/etc/default/grub

# Enable os-prober
GRUB_DISABLE_OS_PROBER=false
EOF
	arch-chroot /mnt grub-mkconfig --output=/boot/grub/grub.cfg
}

## Additional packages

function additional_packages () {
	# Additional packages installed with pacman
	local packages
	## Internet
	# Network Connection
	packages+="networkmanager !network-manager-applet !wpa_supplicant !wireless_tools !netctl !dhcpcd !dnscrypt-proxy !openresolv "
	# Audio and Video
	packages+="alsa-utils !audacity !ffmpeg pulseaudio !vlc "
	## Utilities
	# Development
	packages+="!code !git !jdk-openjdk !jre-openjdk !python zsh "
	# Laptop - Thinkpad X1 Yoga 4th gen
	packages+="dmidecode fwupd sof-firmware xf86-input-wacom iio-sensor-proxy "
	# System
	packages+="bluez bluez-utils !htop !neofetch ntfs-3g !screenfetch "
	## Documents and texts
	# Text editors
	packages+="nano !vim "

	# Install the packages
	echo -n -e "\n${BRIGHT_BLUE}Installing additional packages$CLEAR...  "
	packages=$(sanitize_variable "$packages")
	arch-chroot /mnt pacman --sync --sysupgrade --refresh --noconfirm --needed $packages &> /dev/null &
	PID=$!; wait_animation "$PID" "spinner"
}

## Further network configuration

function network_config () {
	echo -e "\n${BRIGHT_BLUE}Further network configuration$CLEAR"
	arch-chroot /mnt systemctl enable NetworkManager &> /dev/null
	arch-chroot /mnt systemctl enable bluetooth &> /dev/null
}

## Unmount and end script

function unmount () {
	# Unmount every partition
	umount --recursive /mnt
	# Disable all swap devices
	swapoff --all
	# Deactivate volume group
	vgchange --activate n
	cryptsetup luksClose $LUKS_LVM
	[[ $ENCRYPTED_BOOT_PT = true ]]; cryptsetup luksClose $LUKS_BOOT
}

function end () {
	echo -e "\n\n${BRIGHT_GREEN}Installation of Arch Linux finished!${CLEAR}\n"
	echo -e "${BRIGHT_WHITE}You may reboot now."
	read -s -p "Press [ENTER] to unmount and exit..."
	echo -n -e "\n${BRIGHT_CYAN}Unmounting files systems and exiting cryptsetup..."
	unmount &> /dev/null &
	PID=$!; wait_animation "$PID" "spinner"
	echo -e "\n${BRIGHT_YELLOW}Exiting Arch Linux Auto Install Script. Goodbye!$CLEAR"
}

## Call major functions with needed arguments for creating the user

function call () {
	warning
	sanitize_variables
	check_mandatory_variables
	while true; do
		echo -n -e "\n${BRIGHT_YELLOW}Set minimal size of free disk space in GB (default: 120 GB): $CLEAR"
		read value
		if [[ -n $value && $value -gt 0 ]] &> /dev/null; then
			break
		elif [[ -z $value ]]; then
			break
		elif [[ $value -lt 0 ]]; then
			echo -e "${BRIGHT_RED}Value can't be negative!$CLEAR"
		else
			echo -e "$BRIGHT_RED$INVALID_2$CLEAR"
		fi
	done
	partitioning_setup "$value"; sleep 1
	prepare; sleep 1
	base_install; sleep 1
	configuration "$USERNAME" "$USER_PASSWORD"; sleep 1
	additional_packages; sleep 1
	network_config; sleep 1
	end; sleep 1
}

# DEBUGGING
function initial_cleanup () {
	local device="/dev/nvme0n1"
	#local device="/dev/sda"
	local how_many_partitions_should_there_be=4
	umount --recursive /mnt
	swapoff --all
	vgchange --activate n
	cryptsetup luksClose $LUKS_LVM
	[[ $ENCRYPTED_BOOT_PT = true ]] && cryptsetup luksClose $LUKS_BOOT
	while [[ $(partx --noheadings --show $device | wc --lines) -gt $how_many_partitions_should_there_be ]]; do
		local part_number=$(echo "$(lsblk --list --noheadings --output NAME $device | tail --lines=1)" | grep --only-matching "[^nvme0n1p]")
		cat << EOF | gdisk $device > /dev/null
d
$part_number
w
Y
EOF
	done
	partprobe $device
}

## Oh Yeah, It's All Coming Together

function main () {
	# DEBUGGING
	initial_cleanup
	call
}

main