#!/bin/sh
set -e

lower() {
	echo "$1" | tr '[:upper:]' '[:lower:]'
}

upper() {
	echo "$1" | tr '[:lower:]' '[:upper:]'
}

# global
MC_TYPE="$(lower "${MC_TYPE:-vanilla}")"
MC_VERSION="$(lower "${MC_VERSION:-latest}")"

# papermc
PAPERMC_BUILD="$(lower "${PAPERMC_BUILD:-latest}")"

# fabric
FABRIC_LOADER="$(lower "${FABRIC_LOADER:-latest}")"
FABRIC_INSTALLER="$(lower "${FABRIC_INSTALLER:-latest}")"

# forge
FORGE_VERSION="$(lower "${FORGE_VERSION:-recommended}")"

# other
QUIET=1

# all output
ALL=0

req() {
	res=$(curl --fail --silent "$1")
	code=$?

	if [ $code -ne 0 ] || [ "$res" = "" ]; then
		echo "null";
	else
		echo "$res";
	fi
}

error() {
	printf '\x1b[31m[ERROR]\t\x1b[0m%s\n' "$*" > /dev/stderr
	exit 1;
}

log() {
	if [ "$QUIET" -eq 0 ]; then
		printf '\x1b[0m[LOG]\t%s\n' "$*" > /dev/stderr
	fi
}

index_array() {
	array="$1"
	key="$2"
	type="$3"

	if [ "$key" = "latest" ]; then
		index=$(echo "$array" | jq ". | index( last )");
	else
		index=$(echo "$array" | jq ". | index( \"$key\" )");
	fi

	if [ "$index" = "null" ]; then
		error "Invalid $type: $key"
	else
		echo "$array" | jq -r ".[$index]";
	fi
}

#https://api.papermc.io/v2/projects/velocity/versions/3.3.0-SNAPSHOT/

papermc() {
	api="https://api.papermc.io/v2/projects"

	log "Using papermc api v2"

	get_version() {
		log "Getting VERSION for '$MC_TYPE'"
		versions=$(req "$api/$MC_TYPE" | jq -r '.versions')
		if [ "$versions" = "null" ]; then
			error "Invalid TYPE: $MC_TYPE (no versions)"
		fi
		MC_VERSION=$(index_array "$versions" "$MC_VERSION" "VERSION")
		log "VERSION: $MC_VERSION"
	}

	get_build() {
		log "Getting BUILD for '$MC_TYPE@$MC_VERSION'"
		builds=$(req "$api/$MC_TYPE/versions/$MC_VERSION" | jq -r ".builds")
		if [ "$builds" = "null" ]; then
			error "Invalid VERSION: $MC_TYPE@$MC_TYPE (no builds)"
		fi
		PAPERMC_BUILD=$(index_array "$builds" "$PAPERMC_BUILD" "BUILD")
		log "BUILD: $PAPERMC_BUILD"
	}

	get_version
	get_build

	name="$MC_TYPE-$MC_VERSION-$PAPERMC_BUILD.jar"
	url="$api/$MC_TYPE/versions/$MC_VERSION/builds/$PAPERMC_BUILD/downloads/$name"

	if [ $ALL -eq 1 ]; then
		echo "JAR_NAME=$name"
		echo "JAR_URL=$url"
		echo "MC_TYPE=$MC_TYPE"
		echo "MC_VERSION=$MC_VERSION"
		echo "PAPERMC_BUILD=$PAPERMC_BUILD"
	else
		echo "$url"
	fi
}

fabric() {
	api="https://meta.fabricmc.net/v2/versions"

	log "Using fabric api v2"

	get_fabric_impl() {
		type="$1"
		value="$2"
		res=""
		if [ "$value" = "latest" ]; then
			res=$(req "$api/$type" | jq -r 'map(select(.stable == true) | .version) | reverse')
		else
			res=$(req "$api/$type" | jq -r 'map(.version) | reverse')
		fi;
		echo $res;
	}

	get_game() {
		log "Getting VERSION for '$MC_TYPE'"
		games=$(get_fabric_impl "game" "$MC_VERSION")
		MC_VERSION=$(index_array "$games" "$MC_VERSION" "VERSION")
		log "VERSION: $MC_VERSION"
	}

	get_loader() {
		log "Getting LOADER for '$MC_TYPE'"
		loaders=$(get_fabric_impl "loader" "$FABRIC_LOADER")
		FABRIC_LOADER=$(index_array "$loaders" "$FABRIC_LOADER" "LOADER")
		log "LOADER: $FABRIC_LOADER"
	}

	get_installer() {
		log "Getting INSTALLER for '$MC_TYPE'"
		installers=$(get_fabric_impl "installer" "$FABRIC_INSTALLER")
		FABRIC_INSTALLER=$(index_array "$installers" "$FABRIC_INSTALLER" "INSTALLER")
		log "INSTALLER: $FABRIC_INSTALLER"
	}

	get_game
	get_loader
	get_installer

	url="$api/loader/$MC_VERSION/$FABRIC_LOADER/$FABRIC_INSTALLER/server/jar"

	if [ $ALL -eq 1 ]; then
		echo "JAR_NAME=fabric-$MC_VERSION-$FABRIC_LOADER-$FABRIC_INSTALLER.jar"
		echo "JAR_URL=$url"
		echo "MC_TYPE=$MC_TYPE"
		echo "MC_VERSION=$MC_VERSION"
		echo "FABRIC_LOADER=$FABRIC_LOADER"
		echo "FABRIC_INSTALLER=$FABRIC_INSTALLER"
	else
		echo "$url"
	fi
}

vanilla() {
	versions=$(req 'https://launchermeta.mojang.com/mc/game/version_manifest.json' | jq '.versions | map({ "id": .id, "type": .type, "url": .url }) | reverse');

	map_type_impl() {
		type="$1"
		echo "$versions" | jq -r "map(select(.type == \"$type\"))"
	}

	url=""

	get_version() {
		log "Getting VERSION for '$MC_TYPE'"
		version=""
		case "$MC_VERSION" in
			"latest")
				version=$(map_type_impl "release" | jq -r 'last')
				;;
			"snapshot")
				version=$(map_type_impl "snapshot" | jq -r 'last')
				;;
			*)
				version=$(echo "$versions" | jq ".[] | select(.id == \"$MC_VERSION\")")
				;;
		esac

		if [ "$version" = "null" ] || [ "$version" = "" ]; then
			error "Invalid VERSION: $MC_VERSION"
		fi

		MC_VERSION=$(echo "$version" | jq -r '.id')
		log "VERSION: $MC_VERSION"

		json=$(echo "$version" | jq -r '.url')
		url=$(req "$json" | jq -r '.downloads.server.url')

		if [ "$url" = "null" ]; then
			error "Cannot get server jar for VERSION: $MC_VERSION"
		fi
	}

	get_version

	if [ $ALL -eq 1 ]; then
		echo "JAR_NAME=minecraft-$MC_VERSION.jar"
		echo "JAR_URL=$url"
		echo "MC_TYPE=$MC_TYPE"
		echo "MC_VERSION=$MC_VERSION"
	else
		echo "$url"
	fi

}

forge() {
	versions=$(req "https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json" | jq -r '.promos');

	log "Using forge promotions slim json"

	name=""
	url=""

	get_forge_url() {
		local url
		url="https://maven.minecraftforge.net/net/minecraftforge/forge/$1/forge-$1-installer.jar"
		code=0
		wget -q --spider "$url" || code=$?
		if [ $code -eq 0 ]; then
			echo "$url";
		fi
	}

	get_version() {
		log "Getting VERSION for '$MC_TYPE'"
		if [ "$MC_VERSION" = "latest" ]; then
			MC_VERSION=$(echo "$versions" | jq -r '. | keys | .[]' | awk -F '-' '{print $1}' | sort -t "." -n -k1,1 -k2,2 -k3,3 | tail -n1)
		fi
		log "VERSION: $MC_VERSION"
	}

	try_forge_version() {
		ver=$(echo "$versions" | jq -r ".\"$MC_VERSION-$FORGE_VERSION\"")
		if [ "$ver" = "null" ] && [ "$FORGE_VERSION" != "latest" ]; then
			FORGE_VERSION=latest
			try_forge_version
		else
			echo "$ver"
		fi
	}

	get_forge_version() {
		log "Getting FORGE VERSION for '$MC_TYPE'"
		if [ "$FORGE_VERSION" != "latest" ] && [ "$FORGE_VERSION" != "recommended" ]; then
			return;
		fi
		ver=$(try_forge_version)
		if [ "$ver" = "null" ]; then
			error "Invalid VERSION: $MC_VERSION-$FORGE_VERSION"
		fi
		FORGE_VERSION="$ver"
		log "FORGE VERSION: $FORGE_VERSION"
	}

	search_for_url() {
		log "Searching for forge URL"
		log "Trying MC_VERSION-FORGE_VERSION"
		name="$MC_VERSION-$FORGE_VERSION"
		ver=$(get_forge_url "$name")
		if [ "$ver" != "" ]; then url="$ver"; return 0; fi
		log "Trying MC_VERSION-FORGE_VERSION-MC_VERSION"
		name="$MC_VERSION-$FORGE_VERSION-$MC_VERSION"
		ver=$(get_forge_url "$name")
		if [ "$ver" != "" ]; then url="$ver"; return 0; fi
		log "Trying MC_VERSION-FORGE_VERSION-mc172"
		name="$MC_VERSION-$FORGE_VERSION-mc172"
		ver=$(get_forge_url "$name")
		if [ "$ver" != "" ]; then url="$ver"; return 0; fi
		error "Invalid VERSION: $MC_VERSION-$FORGE_VERSION"
	}

	get_version
	get_forge_version
	search_for_url

	if [ $ALL -eq 1 ]; then
		echo "JAR_NAME=forge-$name-installer.jar"
		echo "JAR_URL=$url"
		echo "MC_TYPE=$MC_TYPE"
		echo "MC_VERSION=$MC_VERSION"
		echo "FORGE_VERSION=$FORGE_VERSION"
	else
		echo "$url"
	fi
}

get_url() {
	log "TYPE: $MC_TYPE"

	case "$MC_TYPE" in
		"vanilla")
			vanilla
			;;
		"paper" | "waterfall" | "velocity" | "folia")
			papermc
			;;
		"fabric")
			fabric
			;;
		"forge")
			forge
			;;
		*)
			error "Invalid TYPE: $MC_TYPE"
			;;
	esac
}

usage() {
	printf "usage: mcjar [options]\n"
}

help() {
	printf "usage: mcjar [options]\n\n"
	printf "options:\n"
	printf "\t-h\tshow this message\n"
	printf "\t-l\tshow logging (verbose)\n\n"
	printf "\t-a\toutput all variables\n\n"

	printf "\t-t TYPE\t\tset the mc server type\n"
	printf "\t  [vanilla,fabric,forge,paper,folia,velocity,waterfall]\n\n"
	printf "\t-v VERSION\tset the mc server version\n"
	printf "\t  [latest,snapshot,<version>]\n\n"

	printf "environment:\n"
	printf "\tMC_TYPE\t\tsets the mc server type (same as -t TYPE)\n"
	printf "\tMC_VERSION\tsets the mc server version (same as -v VERSION)\n\n"

	printf "\tPAPERMC_BUILD\tsets the papermc build\n\n"

	printf "\tFABRIC_LOADER\t\tsets the fabric loder version\n"
	printf "\tFABRIC_INSTALLER\tsets the fabric installer version\n\n"

	printf "\tFORGE_VERSION\tsets the forge loader version\n\n"
}

chk_command() {
	if ! command -v "$1" > /dev/null; then
		>&2 echo "error: command '$1' could not be found"
		exit 1
	fi
}

chk_command jq
chk_command curl
chk_command getopts

while getopts "hlat:v:" arg 2> /dev/null; do
	case $arg in
		h)
			help
			exit 0
			;;
		l)
			QUIET=0
			;;
		a)
			ALL=1
			;;
		t)
			MC_TYPE=${OPTARG}
			;;
		v)
			MC_VERSION=${OPTARG}
			;;
		?)
			error "unknown option"
			;;
	esac;
done;

get_url