#!/bin/bash
#================================================================
# HEADER
#================================================================
#% SYNOPSIS
#+    ${SCRIPT_NAME} [--dry] [--force] [--debug] [--autorestart]
#%
#% DESCRIPTION
#%    This script will perform AtomMiner software installation
#%    and update.
#%
#% OPTIONS
#%    -r, --dry               Perform dry run with not changes made
#%    -f, --force             Force install the most recent version
#%    -d, --debug             Install debug software version. Enables force install
#%    -v, --version           Print script information
#%    -h, --help              Print this help
#%
#% EXAMPLES
#%    ${SCRIPT_NAME} --dry --force
#%
#================================================================
#- IMPLEMENTATION
#-    version         ${SCRIPT_NAME} (www.atomminer.com) 0.0.3
#-    author          AtomMiner LLC
#-    copyright       Copyright (c) 2022 https://atomminer.com
#-    license         GNU General Public License
#-    script_id       20220112002
#-
#================================================================
#  HISTORY
#     2022/01/12 : Script creation
#     2015/04/14 : Relaxed script structure
# 
#================================================================
#  DEBUG OPTION
#    set -n  # Uncomment to check your syntax, without execution.
#    set -x  # Uncomment to debug this shell script
#
#================================================================
# END_OF_HEADER
#================================================================

fecho() {
	_Type=${1} ; shift ;
	[[ ${SCRIPT_TIMELOG_FLAG:-0} -ne 0 ]] && printf "$( date ${SCRIPT_TIMELOG_FORMAT} ) "
	printf "[${_Type%[A-Z][A-Z]}] ${*}\n"
	if [[ "${_Type}" = CAT ]]; then
		_Tag="[O]"
		[[ "$1" == \[*\] ]] && _Tag="${_Tag} ${1}"
		if [[ ${SCRIPT_TIMELOG_FLAG:-0} -eq 0 ]]; then
			cat -un - | awk '$0="'"${_Tag}"' "$0; fflush();' ;
		elif [[ "${GNU_AWK_FLAG}" ]]; then # fast - compatible linux
			cat -un - | awk -v tformat="${SCRIPT_TIMELOG_FORMAT#+} " '$0=strftime(tformat)"'"${_Tag}"' "$0; fflush();' ;
		elif [[ "${PERL_FLAG}" ]]; then # fast - if perl installed
			cat -un - | perl -pne 'use POSIX qw(strftime); print strftime "'${SCRIPT_TIMELOG_FORMAT_PERL}' ' "${_Tag}"' ", gmtime();'
		else # average speed but resource intensive- compatible unix/linux
			cat -un - | while read LINE; do \
				[[ ${OLDSECONDS:=$(( ${SECONDS}-1 ))} -lt ${SECONDS} ]] && OLDSECONDS=$(( ${SECONDS}+1 )) \
				&& TSTAMP="$( date ${SCRIPT_TIMELOG_FORMAT} ) "; printf "${TSTAMP}${_Tag} ${LINE}\n"; \
			done 
		fi
	fi
}

usage() { printf "Usage: "; scriptinfo usg ; }
usagefull() { scriptinfo ful ; }
scriptinfo() { headFilter="^#-"
	[[ "$1" = "usg" ]] && headFilter="^#+"
	[[ "$1" = "ful" ]] && headFilter="^#[%+]"
	[[ "$1" = "ver" ]] && headFilter="^#-"
	head -${SCRIPT_HEADSIZE:-99} ${0} | grep -e "${headFilter}" | sed -e "s/${headFilter}//g" -e "s/\${SCRIPT_NAME}/${SCRIPT_NAME}/g"; 
}
SCRIPT_NAME="$(basename ${0})" # scriptname without path
SCRIPT_ID="$(scriptinfo | grep script_id | tr -s ' ' | cut -d' ' -f3)"
SCRIPT_HEADSIZE=$(grep -sn "^# END_OF_HEADER" ${0} | head -1 | cut -f1 -d:)
SCRIPT_UNIQ="${SCRIPT_NAME%.*}.${SCRIPT_ID}.${HOSTNAME%%.*}"
SCRIPT_UNIQ_DATED="${SCRIPT_UNIQ}.$(date "+%y%m%d%H%M%S").${$}"
SCRIPT_DIR_TEMP="/tmp"
SCRIPT_TIMELOG_FLAG=0
SCRIPT_TIMELOG_FORMAT="+%y/%m/%d@%H:%M:%S"
SCRIPT_TIMELOG_FORMAT_PERL="$(echo ${SCRIPT_TIMELOG_FORMAT#+} | sed 's/%y/%Y/g')"
GNU_AWK_FLAG="$(awk --version 2>/dev/null | head -1 | grep GNU)"
PERL_FLAG="$(perl -v 1>/dev/null 2>&1 && echo 1)"
SED=sed
SHASUM=
FULL_COMMAND="${0} $*"
EXEC_DATE=$(date "+%y%m%d%H%M%S")
flagOptErr=0

archprefix=
dryrun=
force=0
autorestart=0
dl=

echo ""
SCRIPT_OPTS=':rdfahv-:'
while getopts ${SCRIPT_OPTS} OPTION ; do
	if [[ "x$OPTION" == "x-" ]]; then
		OPTION="${OPTARG%%=*}"
		OPTARG="${OPTARG#$OPT}"
		OPTARG="${OPTARG#=}" 
	fi
	if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then 
		OPTARG="$OPTION" OPTION=":"
	fi
	case "$OPTION" in
		r | dry) echo "Warning! This is a dry run. This operation WILL NOT INSTALL or modify anything."
			echo ""
			dryrun=1
		;;
		
		d | debug) archprefix="dbg-"
			force=1
			echo "Warning! installing DEBUG software version. This option is NOT recommended unless you absolutely have to use it."
			echo ""
		;;
		
		f | force ) force=1
		;;
		
		h | help) usagefull
			exit 0
		;;
		
		v | version) scriptinfo
			exit 0
		;;

		: ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument"
			flagOptErr=1
		;;

		??* ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option"
			flagOptErr=1
		;;

		? ) echo "${SCRIPT_NAME}: -$OPTION: unknown option"
			flagOptErr=1
		;;
				
	esac
done
shift $((${OPTIND} - 1))
[ $flagOptErr -eq 1 ] && usage 1>&2 && exit 1

lowercase(){
	echo "$1" | sed "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/"
}

makedir(){
	if [ ! -z "$dryrun" ] ; then
		echo "   [mkdir] ${1}"
	else
		[ ! -d "$1" ] && mkdir -p "$1"
	fi
}
removedir(){
	if [ ! -z "$dryrun" ] ; then
		echo "   [ rm  ] ${1}"
	else
		rm -rf $1
	fi
}
move(){
	if [ ! -z "$dryrun" ] ; then
		echo "   [ mv  ] ${1} -----> $2"
	else
		mv "$1" "$2"
	fi
}
copy(){
	if [ ! -z "$dryrun" ] ; then
		echo "   [ cp  ] ${1} -----> $2"
	else
		cp "$1" "$2"
	fi
}
download(){
	if [ -z "$dl" ]; then 
		echo ""
		echo "ERROR: curl or wget is required to download files"
		echo "Please install either curl or wget and start install again"
		echo ""
		exit 1
	fi
	if [ ! -z "$dryrun" ] ; then 
		echo "   [downl] $1 -----> $2"
	else
		$($dl -o "$2" $1)
	fi
}
checksum(){
	if [ -z "$dryrun" ] ; then 
		local cftemp=$(pwd)
		cd "$1"
		$SHASUM -c <<< "$2" 2> /dev/null
		local rv=$?
		cd "${cftemp}"
		if [ $rv -ne 0 ] ; then
			echo "Checksum validation failed. Aborting install..."
			exit 1
		fi
	fi
}

OS=`lowercase \`uname\``
case ${OS} in
	darwin)
		SED=gsed
		SHASUM="shasum -a 256 -s"
		arch="darwin-x64"
	;;
	linux)
		SED=sed
		SHASUM="sha256sum --status --ignore-missing"
		case $(uname -m) in
			i386)    arch="i386" ;;
			i586)    arch="i386" ;;
			i686)    arch="i386" ;;
			x86_64)  arch="amd64" ;;
			aarch64) arch="arm64" ;;
			arm64)   arch="arm64" ;;
			armv8)   arch="arm64" ;;
			arm)     arch="armhf" ;;
			armv7l)  arch="armhf" ;;
			armv61)  arch="armhf" ;;
			armv6l)  arch="armhf" ;;
		esac
	;;
	*)
		echo "[${OS}] OS is not yet supported. Please report your OS details to atom@atomminer.com"
		exit 1
	;;
esac

if [ -x "$(command -v curl)" ]; then dl="curl -sL"
elif [ -x "$(command -v wget)" ]; then dl="wget -qO-"
else 
	echo ""
	echo "ERROR: curl or wget is required to download files"
	echo "Please install either curl or wget and start install again"
	echo ""
	exit 1
fi

if [ -z "${arch}" ] ; then
	echo ""
	echo "Unknown architecture: $(uname -m). Aborting install"
	exit 1
fi

installpath=

echo "Getting latest release version info..."
remotetmp=$($dl https://static.atomminer.com/software/dev/version.txt)
remoteversion=
remotedate=
remotecommit=
unixdatenow=$(date +%s)
unixdatenow_wms=$(date +%s%N)
downloadurl=
# echo "Found release \"${remotecommit}\" on the server"

remoteversion=$(grep "version:" <<< "$remotetmp" | awk -F ": " '{gsub(/[ \t]+$/, "", $2);print $2}')
remotedate=$(grep "updated:" <<< "$remotetmp" | awk -F ": " '{gsub(/[ \t]+$/, "", $2);print $2}')
remotecommit=$(grep "Commit:" <<< "$remotetmp" | awk -F ": " '{gsub(/[ \t]+$/, "", $2);print $2}')

# safe way to get home if $HOME is not present or changed
[ "${OS}" == "darwin" ] && homedir=$(dscacheutil -q user -a name "$USER"|grep 'dir:'|awk -F " " '{print $2}')
[ "${OS}" == "linux" ] && homedir=$(getent passwd "$USER" | cut -d: -f6)
[ -z "${homedir}" ] && homedir=$HOME
if [ -z "${homedir}" ] ; then
	echo ""
	echo "HOME env var is not defined. Can't continue."
	echo "Please define HOME to the writeable home location and re-run installation"
	exit 1
fi

if [ -d /var/atomminer ] ; then
	if [ -w /var/atomminer ] ; then 
		installpath=/var/atomminer
	else 
		echo "Found /var/atomminer location but it is not writeable. Please uninstall old copy of atomminer-cli and re-run install"
		echo "To manually remove atomminer package please run:"
		echo ""
		echo "    sudo rm -rf /var/atomminer && sudo rm -f /usr/bin/atomminer-cli"
		echo ""
		exit 1
	fi
fi

# if [ -d "${homedir}/.atomminer" ] ; then
# 	installpath="$homedir/.atomminer"
# fi

if [ -z "${installpath}" ] ; then
	echo "Cant find install location. Please reinstall atomminer-cli and try again"
	exit 1
fi

#echo "Checking currently installed version..."
installedversion=
installedcommit=
if [ ! -z "${installpath}" ] ; then
	[ -f "${installpath}/version.txt" ] && installedcommit=$(awk -F "=" '/commit/ {gsub(/[ \t]+$/, "", $2);print $2}' "${installpath}/version.txt")
	[ -f "${installpath}/version.txt" ] && swversion=$(awk -F "=" '/version/ {gsub(/[ \t]+$/, "", $2);print $2}' "${installpath}/version.txt")
	[ -f "${installpath}/version.txt" ] && swdate=$(awk -F "=" '/date/ {gsub(/[ \t]+$/, "", $2);print $2}' "${installpath}/version.txt")
else
	echo "No active atomminer installations found. Will install to ${homedir}/.atomminer"
	installpath="${homedir}/.atomminer"
fi

if [ "${installedcommit}" = "${remotecommit}" ]  && [ $force -eq 0 ]; then 
	export AM_INSTALL=$installpath
	echo ""
	echo "Your AtomMiner software is up to date. Install location ${installpath}/"
	echo ""
	exit 0;
fi

[ -d "${installpath}/tmp" ] && removedir "${installpath}/tmp/*"
[ ! -d "${installpath}/tmp/bin" ] && makedir "${installpath}/tmp/bin"
binurl="https://static.atomminer.com/software/dev/${archprefix}${arch}/atomminer-cli"
binsum="https://static.atomminer.com/software/dev/${archprefix}${arch}/SHA256SUMS"

echo "Downloading new version ${remotecommit} for ${arch} architecture..."
download "${binurl}" "${installpath}/tmp/atomminer-cli"
checksum "${installpath}/tmp/" "$($dl $binsum)"

if [ "${OS}" = "linux" ] ; then
	if [ ! -z "$dryrun" ] ; then 
		echo "   [ dep ] Installing required packages"
	else
		foundlibs=0
		if [ -x "$(command -v dpkg)" ]; then
			if (dpkg -l | grep libusb[-$] > /dev/null); then foundlibs=1; fi
		elif [ -x "$(command -v rpm)" ]; then
			if (rpm -qa | grep libusb[-$] > /dev/null); then foundlibs=1; fi
		elif [ -x "$(command -v pacman)" ]; then
			if (pacman -Qs libusb > /dev/null); then foundlibs=1; fi
		elif [ -x "$(command -v yum)" ]; then
			if [ yum list installed libusb >/dev/null ]; then foundlibs=1; fi
		fi
		if [ $foundlibs -eq 0 ]; then
			echo "   Installing required packages..."
			if   [ -x "$(command -v apk)" ];     then sudo apk add --no-cache libusb
			elif [ -x "$(command -v apt-get)" ]; then sudo apt-get install libusb-1.0-0
			elif [ -x "$(command -v dnf)" ];     then sudo dnf install libusb
			elif [ -x "$(command -v zypper)" ];  then sudo zypper install libusb
			elif [ -x "$(command -v pacman)" ];  then sudo pacman -S libusb-dev 
			elif [ -x "$(command -v yum)" ];     then sudo yum install libusb
			else 
				echo ""
				echo "FAILED TO INSTALL PACKAGE: Package manager not found. Aborting install..."
				echo "Please install libusb-1.0 manually and start installation again"
				echo ""
				exit 1
			fi
			if [ $? -ne 0 ] ; then
				echo ""
				echo "Dependencies failed to install. Aborting install..."
				echo "Please install libusb-1.0 manually and start installation again"
				echo ""
				exit 1
			fi
		fi
	fi
	if [ ! -f /etc/udev/rules.d/88-atomminer.rules ] || [ $force -ne 0 ]; then
		if [ ! -z "$dryrun" ] ; then 
			echo "   [rules] Adding udev USB rules"
		else
		sudo -- sh -c '
cat <<EOF >/etc/udev/rules.d/88-atomminer.rules
# AtomMiner USB driver for AM01 device (C) AtomMiner
# Rules written by AtomMiner ( atom@atomminer.com )
# Rules version: 0.0.14

SUBSYSTEM=="usb", ATTR{idProduct}=="0e1e|0fc2", ATTR{idVendor}=="16d0", MODE="0666", ATTR{power/autosuspend}="-1"
EOF
udevadm trigger && udevadm control --reload-rules
'
		fi
	fi
fi

if [ -z "$dryrun" ] ; then 
	if [ ! -f "${installpath}/tmp/atomminer-cli" ]; then
		echo "Something went wrong. Aborting"
		exit 1
	fi
	chmod +x "${installpath}/tmp/atomminer-cli"
fi

echo "All checks are done. Backing up current install and moving new files in place..."

[ ! -d "${installpath}/backup" ] && makedir "${installpath}/backup"
[ -d "${installpath}/backup" ] && removedir "${installpath}/backup/amcli-*"

[ -f "${installpath}/atomminer-cli" ]  && move "${installpath}/atomminer-cli"  "${installpath}/backup/amcli-cli-${unixdatenow}"
[ -f "${installpath}/atomminer.conf" ] && copy "${installpath}/atomminer.conf" "${installpath}/backup/amcli-conf-${unixdatenow}"

move "${installpath}/tmp/atomminer-cli" "${installpath}/atomminer-cli"

if [ ! -f "${installpath}/atomminer.conf" ] ; then
	echo ""
	echo "No default config file found. Creating empty ${installpath}/atomminer.conf with donation address..."
	echo "~~~~~~~~~~ Please edit atomminer.conf with your registered email."
	echo "~~~~~~~~~~ Config file path: ${installpath}/atomminer.conf"
	echo "~~~~~~~~~~ How to create config: https://conf.atomminer.com/"
	echo ""
	if [ ! -z "$dryrun" ] ; then 
		echo "   [ new ] ${installpath}/atomminer.conf" 
	else
		echo '{"user":"donate@atomminer.com"}' > "${installpath}/atomminer.conf"
	fi
fi

removedir "${installpath}/tmp"

if [ ! -z "$dryrun" ] ; then 
	echo "   [ new ] ${installpath}/version.txt" 
else
	# echo "${remotetmp}" > "${installpath}/version.txt"
	echo "commit=${remotecommit}" > "${installpath}/version.txt"
	echo "version=${remoteversion}" >> "${installpath}/version.txt"
	echo "date=${remotedate}" >> "${installpath}/version.txt"

fi
remotetmp=

if [ ! -z "$dryrun" ] ; then 
	installedcommit=$remotecommit
	swversion=$remoteversion
	swdate=$remotedate
else
	[ -f "${installpath}/version.txt" ] && installedcommit=$(awk -F "=" '/commit/ {gsub(/[ \t]+$/, "", $2);print $2}' "${installpath}/version.txt")
	[ -f "${installpath}/version.txt" ] && swversion=$(awk -F "=" '/version/ {gsub(/[ \t]+$/, "", $2);print $2}' "${installpath}/version.txt")
	[ -f "${installpath}/version.txt" ] && swdate=$(awk -F "=" '/date/ {gsub(/[ \t]+$/, "", $2);print $2}' "${installpath}/version.txt")
fi
echo ""
echo "New AtomMiner software ${swversion}-${installedcommit} is installed"
echo "Please restart your atomminer-cli for changes to take effect."
echo ""

exit 0
