#!/bin/bash

# Author : epithetic

function InstallThis {
	#Main loop, iterate through files in destination directory
	#Set IFS so we handle spaces
	OIFS="$IFS"
	IFS=$'\n'
	for file in `find "$source" -type f -name "*.hdiff"`
	do
		IFS="$OIFS"
		fileok=0
		skipbackup=0
		fullpath="$(realpath "$file")"
		filename="$(basename "$fullpath")"
		relsourcefile="$(realpath --relative-to="$source" "$fullpath")"
		relsourcepath="$(dirname "$relsourcefile")"
		destfile="$(echo "$dest/$relsourcefile" | sed "s/\.hdiff//g" | sed "s/\@//g")"
		destname="$(basename "$destfile")"
		# Save the original destfile to check against it later in remaps
		o_destfile="$(echo "$dest/$relsourcefile" | sed "s/\.hdiff//g" | sed "s/\@//g")"
		destpath="$dest/$relsourcepath"
		backuppath="$dest/unip-backup/$relsourcepath"
		if [[ "$mode" == 'install' ]]; then
			extractpath="$dest/tmp-$unipname/$relsourcepath"
			extractfile="$(echo "$dest/tmp-$unipname/$relsourcefile" | sed "s/\.hdiff//g" | sed "s/\@//g")"
		else
			extractpath="$dest/$unipname/$relsourcepath"
			extractfile="$(echo "$dest/$unipname/$relsourcefile" | sed "s/\.hdiff//g" | sed "s/\@//g")"
		fi
		backupfile="$(echo "$dest/unip-backup/$relsourcefile" | sed "s/\.hdiff//g")"

		
		# Create backup folder if it doesn't exist
		if [[ ! -d "$dest/unip-backup" ]]; then
			mkdir -p "$dest/unip-backup"
		fi
		
		# Check if we're remapping
		if [[ "$filename" == "@"* ]]; then
			# Skip backup for remaps, otherwise we might run into trouble later
			skipbackup=1
			
			# Read remap file
			if [[ ! -f "$source/$unipname-unip.remap" ]]; then InvalidUnip; fi
			# Convert to Windows unip path
			winpath="$(echo "$relsourcefile" | sed "s/\//\\\/g" | sed "s/ /█/g")"
			remap="$(grep -F "$winpath" "$source/$unipname-unip.remap" | sed 's/\r$//')"
			if [[ -z "$remap" ]]; then
				echo "Remap assignment not found."
				InvalidUnip
			fi
			linearray=($remap)
			windestpath="${linearray[1]}"
			reldestfile="$(echo "$windestpath" | sed "s/█/ /g" | sed "s/\\\/\//g")"
			destfile="$dest/$reldestfile"
			destname="$(basename "$destfile")"
			# extractfile path is correct AS IS (remapped output)
		fi
		
		# If source file appears to not exist, do we have a case mismatch?
		if [[ ! -f "$destfile" ]]; then
			winfind="$(find "$destpath" -maxdepth 1 -type f -iname "$destname")"
			if [[ -f "$winfind" ]]; then
				echo "Case mismatch corrected: $destfile --> $winfind" >> unipatch-log.txt
				destfile="$winfind"
			fi
		fi

		# Now check if source file exists
		if [[ -f "$destfile" ]]; then
			fileok=1
		else
			# Ignore missing files.
			echo "Warning: $relsourcefile not found in original files. Ignoring."
			echo "WARNING: $relsourcefile not found in original files. Ignoring." >> unipatch-log.txt
			warncount=$((warncount + 1))
		fi
		
		# Process file if ok
		if [[ "$fileok" -eq 1 ]]; then
			echo "${mode}ing file $relsourcefile..."
			# Proper backup code if we don't have to worry about case sensitivity
			#if [[ "$skipbackup" -eq 0 ]]; then
				#if [[ "$mode" == 'install' ]]; then
					# Create backup path
				#	if [[ ! -d "$backuppath" ]]; then
				#		mkdir -p "$backuppath"
				#	fi
					# Copy original file to safety, do not touch if it already exists
				#	if [[ ! -f "$backupfile" ]]; then
				#		echo "backing up file $destfile"
				#		cp "$destfile" "$backupfile"
				#	fi
				#fi
			#fi
			
			# extract unip first regardless
			if [[ ! -d "$extractpath" ]]; then
				mkdir -p "$extractpath"
			fi
			
			# actually diff the source files
			bin/$os/hpatchz -f "$destfile" "$fullpath" "$extractfile" >> unipatch.tmp
			
			# Did hdiffz succeed?
			statusok="$(grep -F "patch ok" "unipatch.tmp")"
			if [[ -z "$statusok" ]]; then
				echo "ERROR in processing $destfile, check unipatch-log.txt"
				echo "ERROR in processing $destfile. File probably does not match correct version." >> unipatch-log.txt
				errcount=$((errcount + 1))
			else
				echo "processed $destfile successfully."
			fi
			cat unipatch.tmp >> unipatch-log.txt
			rm unipatch.tmp
			
			if [[ "$mode" == 'install' ]]; then
				# if destination file doesn't exist, add it to delete queue for backup
				if [[ ! -f "$o_destfile" ]]; then
					windeletepath="$(realpath --relative-to="$work" "$o_destfile" | sed "s/\//\\\/g" | sed "s/ /█/g")"
					echo -n "$windeletepath " >> unip-backup/delete.log
					echo -e "\r" >> unip-backup/delete.log
				fi
			fi
			
		fi
		
	done
	IFS="$OIFS"
	
	if [[ "$mode" == 'install' ]]; then
		
		# For each file we're going to copy, back it up and make sure there's no case mismatched double
		echo -e "\nbacking up files..."
		for file in `find "tmp-$unipname" -type f`
		do
			copyfile="$(realpath "$file")"
			copyname="$(basename "$copyfile")"
			relcopyfile="$(realpath --relative-to="tmp-$unipname" "$copyfile")"
			relcopypath="$(dirname "$relcopyfile")"
			winfind="$(find "$dest/$relcopypath" -maxdepth 1 -type f -iname "$copyname")"
			if [[ -f "$winfind" ]]; then
				echo "cleaning up $winfind" >> unipatch-log.txt
				mkdir -p "unip-backup/$relcopypath"
				cp -n "$winfind" "unip-backup/$relcopypath"
				rm "$winfind"
			fi
		done

		echo -e "\ncopying files..."
		# Overwrite files if installing, then delete temp folder
		cd "$dest"
		
		cp -arfv "tmp-$unipname/"* . >> unipatch-log.txt
		rm -rfv "tmp-$unipname" >> unipatch-log.txt
		
		# Add ID to installed log
		winunipname="$(echo "$unipname" | sed "s/ /█/g")"
		if [[ -f "unipatch-installed.txt" ]]; then
			installedreq="$(sed -n '1p' "unipatch-installed.txt" | sed 's/\r$//')"
			echo -e "$installedreq$winunipname \r" > unipatch-installed.txt
		else
			echo -e "$winunipname \r" > unipatch-installed.txt
		fi
		
		cd "$work"
	fi
	
	echo -e "\nFinished processing installer $unipname-unip with $warncount warnings and $errcount errors."
	
}


function MissingParam {
	echo "Missing parameters: run-unip.sh extract|install unippath [destination]"
	echo "When unspecified, destination defaults to current directory."
	exit
}

function MissingDir {
	echo "One or both of the specified directories do not exist."
	exit
}

function InvalidUnip {
	echo "Input does not seem to be a valid unip folder."
	exit
}

# Check for 64bit OS, expand to other architectures as needed, this will choose bin/ path
arch=`uname -m`
if [ "$arch" == 'x86_64' ]; then
  os="x64"
else
  os="x86"
fi

if [[ "$1" != 's' ]]; then
	clear
	echo "---------------------------------------"
	echo "--- unipatch v0.8 by epithetic --------"
	echo "---------------------------------------"
	echo -e "\n\n"
fi

if [[ -z "$1" ]] || [[ -z "$2" ]]; then MissingParam; fi

source="$2"
dest="$3"
work="$(pwd -P)"

# Default destination to working directory
if [[ -z "$3" ]]; then
	dest="$(pwd -P)"
fi

if [[ "$1" == 'extract' ]]; then
	mode='extract'
elif [[ "$1" == 'install' ]]; then
	mode='install'
elif [[ "$1" == '?' ]] || [[ "$1" == 's' ]]; then
	mode='read'
else
	MissingParam
fi

if [[ ! -d "$source" ]] || [[ ! -d "$dest" ]]; then MissingDir; fi

warncount=0
errcount=0

if [[ "$1" != 's' ]]; then
	date="$(date '+%d/%m/%Y %H:%M:%S')"
	echo -e "\nPreparing to $mode from $source...\n"

	echo "----------------------------------------" >> unipatch-log.txt
	echo "$date | Preparing to $mode from $source" >> unipatch-log.txt
	echo "----------------------------------------" >> unipatch-log.txt
	echo "" >> unipatch-log.txt
fi



# Extract actual unipname from path (regardless of absolute/relative)
unipname="$(basename "$source" | sed "s/-unip//g")"

if [[ ! -f "$source/$unipname-unip.info" ]]; then InvalidUnip; fi


# Read unip info file
unipver="$(sed -n '1p' "$source/$unipname-unip.info" | sed 's/\r$//')"
uniptitle="$(sed -n '2p' "$source/$unipname-unip.info" | sed 's/\r$//')"
unipdesc="$(sed -n '3p' "$source/$unipname-unip.info" | sed 's/\r$//')"
unipreq="$(sed -n '4p' "$source/$unipname-unip.info" | sed 's/\r$//')"


# Check for version string
if [[ "${unipver}" != *"UNIP"* ]];then InvalidUnip; fi


if [[ "$1" != 's' ]]; then
	echo "$unipver installer found!"
fi

echo -e "\n$uniptitle"
echo "---------------------------------------"
echo -e "$unipdesc\n"


# Check for dependencies
if [[ -z "$unipreq" ]] || [[ "$unipreq" == " " ]]; then
	#no dependencies
	if [[ "$mode" == 'read' ]] && [[ "$1" != 's' ]]; then
		echo "OK."
		exit
	elif [[ "$mode" == 'extract' ]] || [[ "$mode" == 'install' ]]; then
		InstallThis
		exit
	fi
else
	if [[ -f "unipatch-installed.txt" ]]; then
		installedreq="$(sed -n '1p' "unipatch-installed.txt"| sed 's/\r$//')"
		echo "requires : $unipreq"
		echo -e "installed: $installedreq\n"
		if [[ "$mode" == 'read' ]]; then
			echo "OK."
			exit
		fi
		# Check if required string is last bit of installed
		if [[ "$installedreq" == *"$unipreq" ]]; then
			echo -e "Last installed patches match prerequisites. Continuing.\n"
			InstallThis
			exit
		elif [[ "$installedreq" == *"$unipreq"* ]]; then
			echo "Stated dependencies are installed, but following patches may result in incompatibilities."
			while true; do
				read -p "Do you want to try to run the installer anyway? [Y/N]" yn
				case "$yn" in
					[Yy]* ) break;;
					[Nn]* ) exit;;
					* ) echo "Please answer yes or no.";;
				esac
			done
			InstallThis
			exit
		else
			while true; do
				echo "Cannot verify that stated dependencies are installed, or in compatible order."
				read -p "Do you want to try to run the installer anyway? [Y/N]" yn
				case "$yn" in
					[Yy]* ) break;;
					[Nn]* ) exit;;
					* ) echo "Please answer yes or no.";;
				esac
			done
			InstallThis
			exit
		fi
	# We don't have an install log
	else
		echo "requires : $unipreq"
		if [[ "$mode" == 'read' ]]; then
			echo "OK."
			exit
		fi
		while true; do
			echo "Stated dependencies don't appear to be installed."
			read -p "Do you want to try to run the installer anyway? [Y/N]" yn
			case "$yn" in
				[Yy]* ) break;;
				[Nn]* ) exit;;
				* ) echo "Please answer yes or no.";;
			esac
		done
		InstallThis
		exit
	fi
fi

#If we're not done by here something went wrong.
exit

