#!/bin/bash

# Settings

declare \
	JAR_PATTERN="*upgrade-tool-*.jar" \
	JAVA_BIN="java" \
	REQUIRED_BASH="3.2.57" \
	REQUIRED_JAVA="1.8.0"

# Global resources

declare \
	C_GREEN="\033[1;32m" \
	C_GRAY="\e[38;5;240m" \
	C_RESET="\e[39m"

# Global variables

declare \
	JAVA_PATH \
	ACTION \
	MULE_HOME_OUTDATED \
	MULE_HOME_UPDATED \
	OUTDATED_JAR \
	UPDATED_JAR \
	OLD_AGENT_VERSION \
	NEW_AGENT_VERSION

# Strings

declare -a MSG
MSG[0]="ERROR: %s version is %s, minimum required is %s, exiting\n"
MSG[1]="ERROR: Java binary not found neither in JAVA nor in JAVA_HOME, exiting\n"
MSG[2]="ERROR: Please use absolute paths for MULE_HOMEs, exiting\n" # <--- Not used in Windows
MSG[3]="INFO: Mule Agent is outdated, running upgrade\n"
MSG[4]="INFO: Upgrading Mule Agent from %s to %s\n"
MSG[5]="INFO: Upgrade terminated with status %d\n"
MSG[6]="INFO: Skipping update, already at latest version\n"
MSG[7]="ERROR: MULE_HOME seems invalid, exiting\n"
MSG[8]="Verifying Mule Agent"
MSG[9]="Mule Runtime Upgrade Tool"
MSG[10]="INFO: Mule Agent is not present in the current setup, but it seems it was installed before, copying it to %s\n"
MSG[11]="INFO: Mule Agent not installed, skipping setup\n"
MSG[12]="INFO: No update available, skipping update\n"

function new_separator () {

	declare \
		TITLE \
		LINES \
		UNDERLINE

	# Prepare
	TITLE="${1}"
	LINES=$( printf "%*s" ${#TITLE} "" )
	UNDERLINE="${LINES// /─}"

	# Print
	printf "${C_GREEN}\n%s\n%s${C_RESET}\n\n" ${TITLE} ${UNDERLINE}

	return 0
}

function compare_versions () {

	declare -i ERR=0
	declare NAME

	NAME="${1}"                       # Set ${NAME}
	IFS="." read -a CUR <<< "${2}"    # Set ${CUR}
	IFS="." read -a REQ <<< "${3}"    # Set ${REQ}

	# Compare versions

	if [ ${CUR[0]} -lt ${REQ[0]} ]; then
		ERR=1
	elif [ ${CUR[0]} -eq ${REQ[0]} ]; then
		if [ ${CUR[1]} -lt ${REQ[1]} ]; then
			ERR=1
		elif [ ${CUR[1]} -eq ${REQ[1]} ]; then
			if [ ${CUR[2]} -lt ${REQ[2]} ]; then
				ERR=1
			fi
		fi
	fi

	# Report error

	if [ ${ERR} -eq 0 ]; then
		return 0
	else
		return 1
	fi
}

function check_bash_version () {

	declare -a \
		VERSION \
		ARGS

	declare \
		NAME \
		CUR \
		REQ

	VERSION[0]="${BASH_VERSINFO[@]:0:3}"
	VERSION[1]="${VERSION[0]// /.}"

	NAME="Bash"
	CUR="${VERSION[1]}"
	REQ="${REQUIRED_BASH}"
	ARGS=( ${NAME} ${CUR} ${REQ} )

	compare_versions ${ARGS[@]}
	if [ ${?} -ne 0 ]; then
		printf "${MSG[0]}" "${NAME}" "${CUR}" "${REQ}" 1>&2
		exit 1
	else
		return 0
	fi
}

function search_in_path () {

	type -p ${JAVA_BIN} &>/dev/null

	if [ ${?} -eq 0 ]; then
		JAVA_PATH=$(type -p ${JAVA_BIN})
		return 0
	else
		return 1
	fi
}

function search_in_env () {

	if [ ! -z "${JAVA_HOME}" ]; then
		JAVA_PATH="${JAVA_HOME}/bin/${JAVA_BIN}"
		return 0
	else
		return 1
	fi
}

function search_java_bin () {

	if search_in_path; then
		return 0
	elif search_in_env; then
		return 0
	else
		printf "${MSG[1]}"
		exit 1
	fi
}

function check_java_version () {

	declare -a \
		JAVA_INFO \
		VERSION \
		ARGS

	declare \
		NAME \
		CUR \
		REQ

	IFS=$'\n'
	JAVA_INFO=( $( ${JAVA_PATH} -version 2>&1 ) )
	VERSION[0]="${JAVA_INFO[0]#*\"}"
	VERSION[1]="${VERSION[0]%\"*}"
	VERSION[2]="${VERSION[1]%_*}"

	NAME="Java"
	CUR="${VERSION[2]}"
	REQ="${REQUIRED_JAVA}"
	ARGS=( ${NAME} ${CUR} ${REQ} )

	compare_versions ${ARGS[@]}
	if [ ${?} -ne 0 ]; then
		printf "${MSG[0]}" "${NAME}" "${CUR}" "${REQ}" 1>&2
		exit 1
	else
		return 0
	fi
}

function run_validations() {

	check_bash_version
	search_java_bin
	check_java_version

	return 0
}

function parse_arguments () {

	while [ -n "${1}" ]; do
		case "${1}" in

			"upgrade" | "rollback")
				ACTION="${1}"
				;;

			-o | -o=* | --old-mule-distro | --old-mule-distro=* )
				if [[ "${1}" == *=* ]]; then
					MULE_HOME_OUTDATED="${1#*=}"
				else
					shift 1
					test ${1} && MULE_HOME_OUTDATED="${1}"
				fi
				;;

			-n | -n=* | --new-mule-distro | --new-mule-distro=* )
				if [[ "${1}" == *=* ]]; then
					MULE_HOME_UPDATED="${1#*=}"
				else
					shift 1
					test ${1} && MULE_HOME_UPDATED="${1}"
				fi
				;;
		esac
		shift 1;
	done

	return 0
}

function test_homes () {

	# Check that MULE_HOMEs are readable and avoid relative paths

	for i in "${MULE_HOME_OUTDATED}" "${MULE_HOME_UPDATED}"; do
		# If it's not empty
		if [ ! -z "${i}" ]; then

			# If it's a directory
			if [ -d "${i}" ]; then

				# If starts with a slash (absolute path)
				if [[ ! ${i} =~ ^/ ]]; then
					printf "${MSG[2]}" 1>&2
					exit 1
				fi
			else
				printf "${MSG[7]}" 1>&2
				exit 1
			fi
		fi
	done

	return 0
}

function restore_agent () {

	declare \
		SRC \
		DST

	declare -i \
		VALID_SRC \
		VALID_DST

	# After upgrading, mule-agent-plugin doesn't exist, so If that's empty, grab a copy from the backup

	SRC=$( echo -n ${MULE_HOME_OUTDATED}/.backup/*plugins/mule-agent-plugin )
	DST=$( echo -n ${MULE_HOME_OUTDATED}/*plugins )

	VALID_SRC=0
	VALID_DST=0

	if [ -d ${SRC} ]; then
		if [[ ${SRC} =~ mule-agent-plugin$ ]]; then
			VALID_SRC=1
		fi
	fi

	if [ -d ${DST} ]; then
		if [ -d ${DST}/mule-agent-plugin ]; then
			VALID_DST=1
		fi
	fi

	if [ ${VALID_SRC} -eq 1 ] && [ ${VALID_DST} -eq 0 ]; then
		printf "${MSG[10]}" ${DST}
		command \cp -af ${SRC} ${DST}
		return 0

	else
		return 1
	fi
}

function agent_already_exists () {

	declare -i \
		UPGRADE

	declare \
		OUTDATED_PATH \
		UPDATED_PATH

	UPGRADE=0

	# Old version

	OUTDATED_PATH="${MULE_HOME_OUTDATED}/*plugins/mule-agent-plugin"
	OUTDATED_JAR=$( echo -n ${OUTDATED_PATH}/lib/mule-agent-api-*.*.*.jar )

	if [ ! -f ${OUTDATED_JAR} ]; then
		UPGRADE=1
		printf "${MSG[11]}"
	fi

	# New version

	UPDATED_PATH="${MULE_HOME_UPDATED}/tools"
	UPDATED_JAR=$( echo -n ${UPDATED_PATH}/agent-setup-*.*.*-amc-final.jar )

	if [ ! -f ${UPDATED_JAR} ]; then
		UPGRADE=1
		printf "${MSG[12]}"
	fi

	# Return

	return $UPGRADE
}

function new_agent_version_available () {

	declare -a \
		OLD_VER \
		NEW_VER \
		ARGS

	declare \
		NAME \
		CUR \
		REQ

	OLD_VER[0]=${OUTDATED_JAR}
	OLD_VER[1]=${OLD_VER[0]##*/}
	OLD_VER[2]=${OLD_VER[1]%*.jar}
	OLD_VER[3]=${OLD_VER[2]##*-}

	NEW_VER[0]=${UPDATED_JAR}
	NEW_VER[1]=${NEW_VER[0]##*/}
	NEW_VER[2]=${NEW_VER[1]%-amc-final.jar}
	NEW_VER[3]=${NEW_VER[2]#agent-setup-}

	NAME="Mule Agent"
	CUR="${OLD_VER[3]}"
	REQ="${NEW_VER[3]}"
	ARGS=( ${NAME} ${CUR} ${REQ} )

	# Export info to the global variables:

	OLD_AGENT_VERSION="${OLD_VER[3]}"
	NEW_AGENT_VERSION="${NEW_VER[3]}"

	if compare_versions ${ARGS[@]}; then
		return 1

	else
		return 0
	fi
}

function upgrade_agent () {

	# These are the files we need to migrate manually
	FILES=(
		"bin/amc_setup*"
		"tools/agent-setup*"
	)

	printf "${MSG[3]}"
	printf "${MSG[4]}" "${OLD_AGENT_VERSION}" "${NEW_AGENT_VERSION}"

	# Copy individual files manually
	command \cp -f ${MULE_HOME_UPDATED}/${FILES[0]} ${MULE_HOME_OUTDATED}/bin/
	command \cp -f ${MULE_HOME_UPDATED}/${FILES[1]} ${MULE_HOME_OUTDATED}/tools/

	# Run the upgrade, output is grayed out
	command ${JAVA_PATH} ${JAVA_OPTS} -jar "${UPDATED_JAR}" --update --mule-home "${MULE_HOME_OUTDATED}" > /dev/null

	# Upgrade completed
	printf "${MSG[5]}" ${?}

	return 0
}

function setup_mule_agent () {

	# If the Mule Agent is installed in the current Mule distro, check
	# if the new distro ships a newer version of the Mule Agent
	# If it does, update current Mule Agent by taking it from the newer
	# distro

	if [ "${ACTION}" = "upgrade" ]; then

		# When upgrading, we need both -o and -n
		if [ -z "${MULE_HOME_OUTDATED}" -o -z "${MULE_HOME_UPDATED}" ]; then
			# No action taken
			return 1
		fi

		new_separator "${MSG[8]}"

		restore_agent

		if agent_already_exists; then

			if new_agent_version_available; then
				upgrade_agent

			else
				printf "${MSG[6]}" ${NEW_AGENT_VERSION}
			fi

		else
			return 0
		fi
	fi

	return 0
}

function run_program () {
	declare -a JAVA_ARGS

	new_separator "${MSG[9]}"

	JAVA_ARGS=( "${JAVA_OPTS}" "-jar" "${0%\/*}/$( echo -e ${JAR_PATTERN})" "${@}" )

	command ${JAVA_PATH} ${JAVA_ARGS[@]}

	return 0
}

function main () {

	# Do some checks
	run_validations

	# Parse arguments to get MULE_HOMEs
	parse_arguments "${@}"

	# Test MULE_HOMEs
	test_homes

	# Run the JAR
	run_program "${@}"

	# If a MULE_HOME is found, check Agent in there
	setup_mule_agent
}

main "${@}" && exit 0 || exit 1
