#!/usr/bin/env bash

{
	SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

	USERNAME_FILE="$HOME/.oo_share_username"
	DONT_ASK_FILE="$HOME/.oo_share_dont_ask"

	source $SCRIPT_DIR/.colorfunctions.sh

	function displaytime {
		local T=$1
		local D=$((T/60/60/24))
		local H=$((T/60/60%24))
		local M=$((T/60%60))
		local S=$((T%60))
		(( $D > 0 )) && printf '%d days ' $D
		(( $H > 0 )) && printf '%d hours ' $H
		(( $M > 0 )) && printf '%d minutes ' $M
		(( $D > 0 || $H > 0 || $M > 0 )) && printf 'and '
		printf '%d seconds\n' $S
	}

	disable_calltracer=0
	function calltracer {
		if [[ $disable_calltracer -eq 0 ]]; then
			LINE_AND_FUNCTION="$(caller)"
			if [[ "$LINE_AND_FUNCTION" != *"./omniopt"* ]] && [[ "$LINE_AND_FUNCTION" != *"./.tests/main_tests"* ]]; then
				red_text "Error occurred in file/line: $LINE_AND_FUNCTION"
			fi

			echo ""
			caller
			echo "Runtime (calltracer): $(displaytime $SECONDS), PID: $$"
			_tput bel
		fi
	}

	trap 'calltracer' ERR
	trap 'calltracer' EXIT

	GREEN='\033[0;32m'
	YELLOW='\033[0;33m'
	BLUE='\033[0;34m'
	CYAN='\033[0;36m'
	MAGENTA='\033[0;35m'
	NC='\033[0m'

	function set_debug {
		trap 'echo -e "${CYAN}$(date +"%Y-%m-%d %H:%M:%S")${NC} ${MAGENTA}| Line: $LINENO ${NC}${YELLOW}-> ${NC}${BLUE}[DEBUG]${NC} ${GREEN}$BASH_COMMAND${NC}"' DEBUG
		true
	}

	function unset_debug {
		trap - DEBUG
	}

	no_color=0
	force=0
	DEBUG=0
	update=0
	folders_to_share=()
	username=$USER
	password=""
	outfile=
	send_single_runs=1

	function join_by {
		local d=${1-} f=${2-}
		if shift 2; then
			printf %s "$f" "${@/#/$d}"
		fi
	}

	RUN_DIR=$1

	function help {
		exit_code=$1

		echo "OmniOpt2 share - Share your hyperparameter optimization results with others. Options:"
		echo ""
		echo "Example:"
		echo "  bash omniopt_share runs/my_experiment/0"
		echo ""
		echo "  --help                                             This help"
		echo "  --update                                           Update a job that had this run-UUID previously"
		echo "  --debug                                            Enable debug options"
		echo "  --no_color                                         Disables color output"
		echo "  --force                                            Ignores cache"
		echo "  --username=s3811141                                Specify username (with or without = sign)"
		echo "  --password=abcdef123                               Specify a password for this run"
		echo "  --dont_send_singleruns                             Don't send single runs"
		echo "  --outfile=slurm-12345.out                          Path to the outfile"

		exit $exit_code
	}

	if [ $# -eq 0 ]; then
		yellow_text "Please run with a folder as argument"
		exit 1
	fi

	for arg in "$@"; do
		if [ "$arg" == "--debug" ]; then
			DEBUG=1
			set_debug
		elif [ "$arg" == "--help" ]; then
			help 0
		elif [ "$arg" == "--dont_send_singleruns" ]; then
			send_single_runs=0
		elif [ "$arg" == "--force" ]; then
			force=1
		elif [ "$arg" == "--update" ]; then
			update=1
		elif [ "$arg" == "--no_color" ]; then
			no_color=1
		elif [[ "$arg" == --password=* ]]; then
			password="${arg#--password=}"
		elif [[ "$arg" == --outfile=* ]]; then
			outfile="${arg#--outfile=}"
		elif [[ "$arg" == --username=* ]]; then
			username="${arg#--username=}"
			DONT_ASK_USERNAME=1
		elif [ -d "$arg" ]; then
			if [[ -d $arg ]]; then
				folders_to_share+=("$arg")
			else
				red_text "Cannot share $arg: folder not found"
			fi
		else
			red_text "Invalid option $arg, is this supposed to be your run folder? If so, it doesn't exist.\n"
			help 1
		fi
	done

	source "$SCRIPT_DIR/.shellscript_functions"

	k=0
	for RUN_DIR in "${folders_to_share[@]}"; do
		ok=1

		if [[ -z $RUN_DIR ]]; then
			red_text "Parameter for RUN_DIR is not set\n"
			ok=0
		fi

		if [[ ! -d "$RUN_DIR" ]]; then
			red_text "$RUN_DIR does not exist\n"
			ok=0
		fi

		if [[ "$ok" -eq "1" ]]; then
			experiment_name=$(echo "$RUN_DIR" | sed -e 's#/[0-9]*/*$##g' -e 's#.*/##')

			OO_tmp_dir=/tmp/oo_tmp
			everything_but_singleruns=$OO_tmp_dir/everything_but_singleruns.zip

			curl_options=()

			for available_file in $(ls "$RUN_DIR" 2>/dev/null); do
				available_file_param_name=$(echo "$available_file" | sed -e 's#\..*##')
				if echo "$available_file" | grep -Ei "\.(csv|txt|log|json)" 2>/dev/null > /dev/null; then
					curl_options+=("-F" "$available_file_param_name=@$RUN_DIR/$available_file")
				fi
			done

			if [[ -e "$RUN_DIR/git_version" ]]; then
				curl_options+=("-F" "git_version=@$RUN_DIR/git_version")
			fi

			if [[ -d "$RUN_DIR/state_files" ]]; then
				for available_file in $(ls "$RUN_DIR/state_files" 2>/dev/null); do
					available_file_param_name=$(echo "$available_file" | sed -e 's#\..*##')
					if echo "$available_file" | grep -Ei "\.(csv|txt\.json)" 2>/dev/null > /dev/null; then
						curl_options+=("-F" "$available_file_param_name=@$RUN_DIR/$available_file")
					fi
				done
			fi

			if [[ $send_single_runs -eq 1 ]]; then
				if [[ -d "$RUN_DIR/single_runs" ]]; then
					for available_run_folder in $(ls "$RUN_DIR/single_runs" | grep "^[0-9]*$" 2>/dev/null); do
						for available_out_and_err_files in $(ls "$RUN_DIR/single_runs/$available_run_folder"); do
							_file="$RUN_DIR/single_runs/$available_run_folder/$available_out_and_err_files"
							if [[ -e "$_file" ]] && [[ -s "$_file" ]]; then
								if echo "$_file" | grep -qE "\.(out|err)$"; then
									filename_on_server="single_run_file_${available_run_folder}_${available_out_and_err_files}"
									curl_options+=("-F" "$filename_on_server=@$_file")
								fi
							fi
						done
					done
				fi
			fi

			is_valid_username() {
				[[ -n "$1" && "$1" != *[[:space:]]* ]]
			}

			user_id=""

			if [[ "$k" -eq "0" ]]; then
				if [[ -z $DONT_ASK_USERNAME && ! -f "$DONT_ASK_FILE" ]]; then

				if [[ -f "$USERNAME_FILE" ]]; then
					stored_username=$(<"$USERNAME_FILE")
					if is_valid_username "$stored_username"; then
						user_id="$stored_username"
					fi
				fi

				if ! is_valid_username "$user_id"; then
					eval "$(resize)"
					user_id=$(whiptail --inputbox "By entering your name here you agree to make it public with this data. If you don't agree, cancel." 12 60 "$username" --title "What should be your user name?" 3>&1 1>&2 2>&3)

				    exitstatus=$?
				    if [[ $exitstatus -ne 0 || -z "$user_id" ]]; then
					    echo "Cancelled or empty input. Exiting."
					    exit 1
				    fi

				    echo "$user_id" > "$USERNAME_FILE"

				    if whiptail --yesno "Do you want to avoid this question in the future and simply use this username?" 10 60 --title "Remember this choice?"; then
					    touch "$DONT_ASK_FILE"
				    fi
				fi

			else
				user_id=$username
				fi
			fi

			exitstatus=$?
			if [ $exitstatus = 0 ]; then
				true
			else
				yellow_text "You cancelled sharing."
				exit 0
			fi

			BASEURL="https://imageseg.scads.de/omniax"

			if [[ -e "$HOME/.oo_base_url" ]]; then
				BASEURL=$(cat "$HOME/.oo_base_url")
				if [[ -z $no_color ]]; then
					yellow_text "$HOME/.oo_base_url exists. Using base-url $BASEURL"
				fi
			fi

			set -e

			url="$BASEURL/share_internal.php?user_id=$user_id&experiment_name=$experiment_name"

			run_uuid_path="$RUN_DIR/state_files/run_uuid"

			if [[ $update -eq 1 ]]; then
				url="$url&update=1";
			fi

			if [[ -e $run_uuid_path ]]; then
				run_uuid=$(cat "$run_uuid_path")
				url="$url&update_uuid=$run_uuid"

				curl_options+=("-F" "run_uuid=@$run_uuid_path")

				mkdir -p $OO_tmp_dir || {
					red_text "mkdir -p $OO_tmp_dir failed"
					exit 1
				}

				original_pwd="$(pwd)"

				cd $RUN_DIR


				if false; then
					if command -v zip 2>/dev/null >/dev/null; then
						zip -r $everything_but_singleruns . -x "single_runs/*" >/dev/null || {
							red_text "zip -r $everything_but_singleruns . -x \"single_runs/*\" failed"
							exit 1
						}
					else
						red_text "zip not found. You will not be able to continue this job from the URL."
					fi

					curl_options+=("-F" "everything_but_singleruns.zip=@$everything_but_singleruns")
				fi

				cd "$original_pwd"

				if [[ -n $outfile ]]; then
					if [[ -e $outfile ]]; then
						curl_options+=("-F" "outfile=@$outfile")
					fi
				fi


				log_dir="logs"
				current_dir="$(pwd)"

				if [[ ! -d $log_dir ]]; then
					alternative_log_dir="../../../logs"

					if [[ -d $alternative_log_dir ]]; then
						log_dir="$alternative_log_dir"
					fi
				fi

				while [[ ! -d "$current_dir/$log_dir" && "$current_dir" != "/" ]]; do
					current_dir=$(dirname "$current_dir")
					log_dir="$current_dir/$log_dir"
				done


				if [[ -z $outfile ]]; then
					if [[ -e "$log_dir/$run_uuid" ]]; then
						curl_options+=("-F" "outfile=@$log_dir/$run_uuid")
					fi
				fi

				if [[ -e "$log_dir/${run_uuid}_progressbar" ]]; then
					curl_options+=("-F" "progressbar=@$log_dir/${run_uuid}_progressbar")
				fi

				if [[ -e "$log_dir/${run_uuid}_install_errors" ]]; then
					curl_options+=("-F" "install_errors=@$log_dir/${run_uuid}_install_errors")
				fi

				if [[ -e "$log_dir/${run_uuid}_trial_index_to_param_logs" ]]; then
					curl_options+=("-F" "trial_index_to_params=@$log_dir/${run_uuid}_trial_index_to_param_logs")
				fi

				if [[ -e "${run_uuid}.svg" ]]; then
					curl_options+=("-F" "profile.svg=@$run_uuid.svg")
				fi

				if [[ -e "$log_dir/${run_uuid}_log" ]]; then
					curl_options+=("-F" "log=@$log_dir/${run_uuid}_log")
				elif [[ -n $bash_logname ]] && [[ -e $bash_logname ]]; then
					curl_options+=("-F" "log=@$bash_logname")
				fi
			fi

			share_upload_state_file="$RUN_DIR/state_files/last_share_md5"
			share_upload_state_file_new="$RUN_DIR/state_files/last_share_md5_tmp"

			if [[ $force -eq 0 ]]; then
				nr_curl_options_before="${#curl_options[@]}"

				new_curl_options=()

				OLDIFS=$IFS

				if [[ -e "$share_upload_state_file" ]]; then
					cp "$share_upload_state_file" "$share_upload_state_file_new"
				fi

				IFS=$'\n'

				for curl_option in "${curl_options[@]}"; do
					if echo "$curl_option" | grep -q "@"; then
						curl_path=$(echo "$curl_option" | sed -e "s/.*\@//")

						curl_path_md5=$(echo "$curl_path" | md5sum | sed -e "s#\s.*##")
						curl_path_content_md5=$(md5sum "$curl_path" | sed -e "s#\s.*##")

						this_file_cache_line="$curl_path_md5,$curl_path_content_md5"

						if [[ ! -e "$share_upload_state_file_new" ]]; then
							echo "$this_file_cache_line" >> $share_upload_state_file_new
							new_curl_options+=("-F $curl_option")
						elif [[ -e "$share_upload_state_file_new" ]]; then
							if ! grep -q "$curl_path_md5" "$share_upload_state_file_new"; then
								echo "$this_file_cache_line" >> "$share_upload_state_file_new"
								new_curl_options+=("-F $curl_option")
							elif ! grep -q "$this_file_cache_line" "$share_upload_state_file_new"; then
								#yellow_text "$share_upload_state_file_new exists, but $this_file_cache_line is not in it"
								sed -i "s#^$curl_path_md5.*#$this_file_cache_line#" "$share_upload_state_file_new"
								new_curl_options+=("-F $curl_option")
							fi
						fi
					fi
				done

				curl_options=("${new_curl_options[@]}")

				IFS=$OLDIFS
			fi

			if [[ ${#curl_options[@]} -eq 0 ]]; then
				if [[ $nr_curl_options_before -eq 0 ]]; then
					yellow_text "Could not find any files in $RUN_DIR\n"

					matching_dirs=$(find "$RUN_DIR" -mindepth 2 -maxdepth 2 -type d | grep -E "^$RUN_DIR/[a-zA-Z0-9_]+/[0-9]+$")

					if [ -n "$matching_dirs" ]; then
						IFS=' ' read -r term_height term_width < <(stty size)

						dialog_width=$((term_width - 4))
						dialog_height=$((term_height - 4))

						max_list_height=$((dialog_height - 10))
						list_height=$(( $(echo "$matching_dirs" | wc -l) ))
						[ $list_height -gt $max_list_height ] && list_height=$max_list_height

						checklist_items=()
						while IFS= read -r line; do
							shortname=$(basename "$(dirname "$line")")/$(basename "$line")
							checklist_items+=("$line" "$shortname" "ON")
						done <<< "$matching_dirs"

						selected_dirs=$(whiptail --title "Select folders to share" \
							--checklist "Could not find any files in:\n\n  $RUN_DIR\n\nHowever, the following subfolders were found:\n\n(Use SPACE to select/deselect folders)\n" \
							"$dialog_height" "$dialog_width" "$list_height" \
							"${checklist_items[@]}" \
							3>&1 1>&2 2>&3)

						exitstatus=$?
						if [ $exitstatus -eq 0 ]; then
							selected_dirs_array=()

							eval "selected_dirs_array=($selected_dirs)"

							filtered_args=()
							for arg in "$@"; do
								if [[ "$arg" == --username=* ]] || [[ "$arg" == "$RUN_DIR" ]]; then
									continue
								fi
								filtered_args+=("$arg")
							done

							for folder in "${selected_dirs_array[@]}"; do
								bash "$SCRIPT_DIR/omniopt_share" "$folder" --username="$username" "${filtered_args[@]}"
							done
						else
							yellow_text "Aborted sharing."
						fi
					fi
				else
					green_text "No update needed"
				fi
			else
				if [[ $password != "" ]]; then
					url="$url&password=$password"
				fi

				if [[ $DEBUG -eq 1 ]]; then
					echo "curl -s ${curl_options[@]} $url"
				fi

				set +e

				CURL_OUTPUT=$(curl -s "${curl_options[@]}" "$url")
				exit_code=$?

				set -e

				if [[ $exit_code -ne 0 ]] || echo "$CURL_OUTPUT" | grep "Error sharing the job." >/dev/null 2>&1 || echo "$CURL_OUTPUT" | grep -q "Error:"; then
					red_text "$CURL_OUTPUT\n"
					if [[ $exit_code -ne 0 ]]; then
						echo "Curling $url failed. Curl exited with $exit_code instead of 0"
						if [[ $exit_code == 7 ]]; then
							echo "Exit code 7 means that the server was not reachable. Are you online? Is the server properly started?"
						fi
						exit $exit_code
					fi
				else
					green_text "$CURL_OUTPUT"
				fi

				if [[ $force -eq 0 ]]; then
					if echo "$CURL_OUTPUT" | grep -qE 'https?://'; then
						cp "$share_upload_state_file_new" "$share_upload_state_file"
					fi
				fi
			fi
		fi
		k=$(($k+1))
	done

	disable_calltracer=1
	exit 0
}
