#!/bin/bash


if [[ $(/usr/bin/id -u) -ne 0 ]]; then
	echo 'Please run snap as root'
	exit 2
fi


version='0.3.5'
path='/'
i_max='8'


_refuse_delete_first_snapshot='1'
_refusing_revert_snapshot='1'


_check(){
	if apfs_snapshot -l "${path}" &>>/dev/null; then
		command='apfs_snapshot'
	fi
	if snaputil -l "${path}" &>>/dev/null; then
		command='snaputil'
	fi
	if snappy -lf "${path}" &>>/dev/null; then
		command='snappy'
	fi
	if [[ -z "${command}" ]]; then
		echo 'Please install apfs_snapshop or snappy or snaputil'
		exit 3
	fi
}

_usage(){
	if [[ "${_refusing_revert_snapshot}" -eq 0 ]]; then
		_R='
	-R <NAME>		Revert to snapshot named NAME'
	fi
	echo "Usage: snap [OPTIONS...]
  or   snap -f <volumePath> [OPTIONS...]
  or   snap -p <variable>=[true/false]
	-h			Print this help
	-l			List snapshots on filesystem
	-c <NAME>		Create a snapshot named NAME
	-d <NAME>		Delete a snapshot named NAME
	-r <NAME> <NEWNAME>	Rename a snapshot named NAME to name NEWNAME${_R}
	-x <NAME>		Set the target snapshot name to be the iOS system-snapshot
	-m <NAME>		Mount snapshot named NAME to /mnt2
	-u 			Unmount all snapshots and partitions mounted to /mnt2
	-s			Print the iOS system-snapshot name
	-v			Print the version and other info
	-p			Print all preference variables or set preference variables
	-o 			Count the number of snapshots on the current device
	-n 			Print what command is currently used"
}

_status(){
	case "${status}" in
		0)
			echo 'Success'
		;;
		*)
			echo 'Failure'
		;;
	esac
}

_check_status(){
	case "${status}" in
		0)
			sleep 0
		;;
		*)
			exit 1
		;;
	esac
}

_exit_status(){
	if [[ -n "${status2}" ]]; then
		exit 1
	fi
	case "${status}" in
		0)
			unset status
		;;
		*)
			exit 1
		;;
	esac
}

_check_path(){
	if readlink -f / &>>/dev/null; then
		status='0'
	else
		echo 'Error: readlink'
		status='1'
	fi
	if [[ -d "${path}" ]]; then
		status='0'
	else
		echo "This directory does not exist: ${path}"
		status='1'
	fi
	_check_status
	i='0'
	while [[ "${i}" -le "${i_max}" ]]; do
		let 'i++'
		path_0=`readlink "${path}"`
		if [[ -z "${path_0}" ]]; then
			path=`readlink -f "${path}"`
			i='9'
		else
			path="/${path_0}"
		fi
	done
	if [[ -n `mount | grep " on ${path} ("` ]]; then
		if [[ -n `mount | grep " on ${path} (apfs"` ]]; then
			status='0'
		else
			echo "This is not an APFS partition: ${path}"
			status='1'
		fi
	else
		echo "This is not a mount point: ${path}"
		status='1'
	fi
}

_check_mnt2(){
	if [[ -n `mount | grep "${b}@/dev/disk0s1s1 on "` ]]; then
		i='0'
		while [[ "${i}" -le "${i_max}" ]]; do
			let 'i++'
			if umount -f "${b}@/dev/disk0s1s1"; then
				i='9'
			fi
		done
	fi
	if [[ -e "/mnt2" ]]; then
		sleep 0
	else
		if [[ -d "/mnt2" ]]; then
			rm -rf "/mnt2"
		fi
	fi
	mkdir -p "/mnt2/"
}

_list(){
	case "${command}" in
		apfs_snapshot)
			apfs_snapshot -l "${path}"
		;;
		snaputil)
			snaputil -l "${path}"
		;;
		snappy)
			snappy -lf "${path}" | sed '1d'
		;;
	esac
}

_count(){
	path_1='/'
	path_2='/private/var/'
	case "${command}" in
		apfs_snapshot)
			count_1=`apfs_snapshot -l "${path_1}" | sed -n '$='`
			count_2=`apfs_snapshot -l "${path_2}" | sed -n '$='`
		;;
		snaputil)
			count_1=`snaputil -l "${path_1}" | sed -n '$='`
			count_2=`snaputil -l "${path_2}" | sed -n '$='`
		;;
		snappy)
			count_1=`snappy -lf "${path_1}" | sed '1d' | sed -n '$='`
			count_2=`snappy -lf "${path_2}" | sed '1d' | sed -n '$='`
		;;
	esac
	if [[ -z "${count_1}" ]]; then
		count_1="0"
	fi
	if [[ -z "${count_2}" ]]; then
		count_2="0"
	fi
	if [[ "${count_1}" -eq 0 ]] || [[ "${count_1}" -eq 1 ]]; then
		snapshot_1="snapshot"
	else
		snapshot_1="snapshots"
	fi
	if [[ "${count_2}" -eq 0 ]] || [[ "${count_2}" -eq 1 ]]; then
		snapshot_2="snapshot"
	else
		snapshot_2="snapshots"
	fi
}

_create(){
	case "${command}" in
		apfs_snapshot)
			if apfs_snapshot -c "${b}" "${path}" &>>/dev/null; then
				status='0'
			fi
		;;
		snaputil)
			if snaputil -c "${b}" "${path}" &>>/dev/null; then
				status='0'
			fi
		;;
		snappy)
			if snappy -f "${path}" -c "${b}" &>>/dev/null; then
				status='0'
			fi
		;;
	esac
}

_delete(){
	case "${command}" in
		apfs_snapshot)
			if apfs_snapshot -d "${b}" "${path}" &>>/dev/null; then
				status='0'
			fi
		;;
		snaputil)
			if snaputil -d "${b}" "${path}" &>>/dev/null; then
				status='0'
			fi
		;;
		snappy)
			if snappy -f "${path}" -d "${b}" &>>/dev/null; then
				status='0'
			fi
		;;
	esac
}

_rename(){
	case "${command}" in
		apfs_snapshot)
			if apfs_snapshot -r "${b}" -n "${c}" "${path}" &>>/dev/null; then
				status='0'
			fi
		;;
		snaputil)
			if snaputil -n "${b}" "${c}" "${path}" &>>/dev/null; then
				status='0'
			fi
		;;
		snappy)
			if snappy -f "${path}" -r "${b}" -t "${c}" &>>/dev/null; then
				status='0'
			fi
		;;
	esac
}

_rename_x(){
	case "${command}" in
		snappy)
			if snappy -f "${path}" -xr "${b}" &>>/dev/null; then
				status='0'
			fi
		;;
		*)
			_rename
		;;
	esac
}

_revert(){
	case "${command}" in
		apfs_snapshot)
			if apfs_snapshot -b "${b}" "${path}" &>>/dev/null; then
				status='0'
			fi
		;;
		snaputil)
			if snaputil -r "${b}" "${path}" &>>/dev/null; then
				status='0'
			fi
		;;
		snappy)
			if snappy -f "${path}" -v "${b}" &>>/dev/null; then
				status='0'
			fi
		;;
	esac
}

_echo_name_x(){
	case "${command}" in
		snappy)
			snappy -s | sed 's/System Snapshot: //g'
		;;
		*)
			echo "com.apple.os.update-`ioreg -p IODeviceTree -l | grep boot-manifest-hash | sed 's#    | |   "boot-manifest-hash" = <##g' | sed 's#>##g' | tr 'a-z' 'A-Z'`"
		;;
	esac
}

_mount(){
	_check_mnt2 &>>/dev/null
	if mount_apfs -s "${b}" "${path}" "/mnt2" &>>/dev/null; then
		status='0'
	fi
}

_umount(){
	i='0'
	while [[ "${i}" -le "${i_max}" ]]; do
		let 'i++'
		if umount -f "/mnt2" &>>/dev/null; then
			i='9'
		fi
	done
}


_check
case "${1}" in
	-f | --filesystem)
		shift
		path="${1}"
		_check_path
		shift
		_check_status
	;;
esac
b="${2}"
c="${3}"
case "${1}" in
	-h | --help)
		_usage
		exit 0
	;;
	-l | --list)
		echo "Will list snapshots on \"${path}\" fs"
		_list
	;;
	-c | --create)
		if [[ -z "${2}" ]]; then
			echo 'Please enter a name to create a snapshot'
			exit 4
		else
			echo "Will create snapshot named \"${b}\" on fs \"${path}\""
			echo -n "status: "
			_create
			_status
			_exit_status
		fi
	;;
	-d | --delete)
		if [[ -z "${2}" ]]; then
			echo 'Please enter a name to delete the snapshot'
			exit 4
		else
			shift
			ones=`snap -l | sed -n 2p`
			for b in "${@}"; do
				unset status
				echo "Will delete snapshot named \"${b}\" on fs \"${path}\""
				echo -n "status: "
				if [[ "${_refuse_delete_first_snapshot}" -eq 1 ]]; then
					if [[ "${b}" = "${ones}" ]]; then
						echo 'Failure'
						echo 'Error: Refuse to complete this operation'
						echo 'Error: Disable the deletion of the first snapshot of the device for security reasons'
					else
						_delete
						_status
					fi
				else
					_delete
					_status
				fi
				if [[ "${status}" = '0' ]]; then
					sleep 0
				else
					status2='1'
				fi
			done
			_exit_status
		fi
	;;
	-r | --rename)
		if [[ -z "${2}" ]]; then
			echo 'Please enter the snapshot name you want to rename the snapshot'
			exit 4
		else
			if [[ -z "${3}" ]]; then
				echo 'Please enter the renamed name'
				exit 4
			else
				echo "Will rename snapshot named \"${b}\" on fs \"${path}\" to \"${c}\""
				echo -n "status: "
				_rename
				_status
				_exit_status
			fi
		fi
	;;
	-x | --to-system)
		if [[ -z "${2}" ]]; then
			echo 'Please enter the snapshot name to be set as the default snapshot'
			exit 4
		else
			c="com.apple.os.update-`ioreg -p IODeviceTree -l | grep boot-manifest-hash | sed 's#    | |   "boot-manifest-hash" = <##g' | sed 's#>##g' | tr 'a-z' 'A-Z'`"
			echo "Will rename snapshot named \"${b}\" on fs \"${path}\" to \"${c}\""
			echo -n "status: "
			_rename_x
			_status
			_exit_status
		fi
	;;
	-s | --show-name)
		_echo_name_x
	;;
	-R | --revert)
		if [[ -z "${2}" ]]; then
			echo 'Please enter the name of the snapshot to be set as the next boot recovery'
			exit 4
		else
			if [[ "${_refusing_revert_snapshot}" -eq 1 ]]; then
				echo 'snap refuses to restore the snapshot because it may cause file system damage'
				exit 1
			fi
			echo "Will revert snapshot \"${b}\" on fs \"${path}\""
			echo -n "status: "
			_revert
			_status
			_exit_status
		fi
	;;
	-m | --mount)
		if [[ -z "${2}" ]]; then
			echo 'Please enter the name of the snapshot to be mounted'
			exit 4
		else
			echo "Will mount snapshot named \"${b}\" on fs \"${path}\" to \"/mnt2\""
			echo -n "status: "
			_mount
			_status
			_exit_status
		fi
	;;
	-u | --umount)
		if [[ -e "/mnt2" ]]; then
			if [[ `mount` =~ " on /mnt2 (" ]]; then
				echo "Will unmount all mount on \"/mnt2\""
				echo -n "status: "
				_umount
				if [[ `mount` =~ " on /mnt2 (" ]]; then
					echo 'Failure'
					status='1'
				else
					status='0'
				fi
				_status
			else
				echo 'This directory is not a mount point'
				status='1'
			fi
			if [[ -z `ls /mnt2` ]]; then
				rm -rf "/mnt2"
			fi
		else
			echo 'no such directory: /mnt2'
			status='1'
		fi
		_exit_status
	;;
	-v | --version)
		_snap_command=`which "${0}"`
		_snap_package_name=`dpkg -S "${_snap_command}"`
		_dpkg_version=`dpkg -s ${_snap_package_name%%:*} 2>&1 | grep Version | sed 's#Version: ##g'`
		if [[ -z "${_dpkg_version}" ]]; then
			echo "snap  version: ${version}"
		else
			if [[ "${version}" = "${_dpkg_version}" ]]; then
				echo "snap  version: ${version}"
			else
				echo 'snap  version:'
				echo "Internal: ${version}"
				echo "dpkg: ${_dpkg_version}"
			fi
		fi
		echo ''
		echo 'Preferences: '
		echo -n 'Refuse to delete the first snapshot: '
		if [[ "${_refuse_delete_first_snapshot}" -eq 1 ]]; then
			echo 'true'
		else
			echo 'false'
		fi
		echo -n 'Refuse to restore snapshot: '
		if [[ "${_refusing_revert_snapshot}" -eq 1 ]]; then
			echo 'true'
		else
			echo 'false'
		fi
	;;
	-p | --preferences)
		if [[ -z "${2}" ]]; then
			echo -n 'refuse-to-delete-the-first-snapshot: '
			if [[ "${_refuse_delete_first_snapshot}" -eq 1 ]]; then
				echo 'true'
			else
				echo 'false'
			fi
			echo -n 'refuse-to-restore-snapshot: '
			if [[ "${_refusing_revert_snapshot}" -eq 1 ]]; then
				echo 'true'
			else
				echo 'false'
			fi
		else
			if [[ "${2}" =~ "refuse-to-delete-the-first-snapshot" ]]; then
				aaa="${2##*=}"
				if [[ "${aaa}" = 'true' ]]; then
					aaa='1'
				elif  [[ "${aaa}" = 'false' ]]; then
					aaa='0'
				else
					exit 1
				fi
				bbb=`grep -n '_refuse_delete_first_snapshot=' "${0}"`
				bbb="${bbb%%:*}"
				sed -i. "${bbb}c _refuse_delete_first_snapshot='${aaa}'" "${0}" &>>/dev/null
			fi
			if [[ "${2}" =~ "refuse-to-restore-snapshot" ]]; then
				aaa="${2##*=}"
				if [[ "${aaa}" = 'true' ]]; then
					aaa='1'
				elif  [[ "${aaa}" = 'false' ]]; then
					aaa='0'
				else
					exit 1
				fi
				bbb=`grep -n '_refusing_revert_snapshot=' "${0}"`
				bbb="${bbb%%:*}"
				sed -i. "${bbb}c _refusing_revert_snapshot='${aaa}'" "${0}" &>>/dev/null
			fi
		fi
		rm -rf "${0}."
	;;
	-o | --count)
		_count
		echo "There are \"${count_1}\" ${snapshot_1} on fs \"${path_1}\""
		echo "There are \"${count_2}\" ${snapshot_2} on fs \"${path_2}\""
	;;
	-n | --command)
		echo "${command}"
	;;
	*)
		_usage
		exit 1
	;;
esac
