#!/bin/sh
set -o errexit

# The first argument is the shell script that initializes the variables:
# sourceDir
# outputDir
# tmpDir
# addBuildCompatibilityLibDir
# systemPackages - lists of the hpkg packages copied/updated into /system/packages
# otherPackages - lists of the hpkg packages copied/updated into other (optional) places
# repositories - all repository files
# downloadDir
# The following are only for image types:
# installDir
# isImage
# imagePath
# imageSize
# imageLabel
# resolvePackageDependencies
# noDownloads
# updateAllPackages
# updateOnly
# dontClearImage
# isVMwareImage
#
# addattr
# copyattr
# getPackageDependencies
# package
# rc
# rmAttrs
# unzip
# The following are only for image types:
# bfsShell
# fsShellCommand
# makebootable
# resattr
# vmdkimage
# The following is only for cd types:
# generate_attribute_stores
# isCD
#
if [ $# -gt 0 ]; then
	. $1
	shift
fi

if [ ! $isCD ]; then
	# If the haiku image path is a symlink resolve it now (makebootable needs the
	# path of the actual device path under Linux).
	normalizedImagePath=''
	if readlink -f "$imagePath" > /dev/null 2>&1 ; then
		normalizedImagePath=$(readlink -f "$imagePath")
	elif realpath "$imagePath" > /dev/null 2>&1 ; then
		normalizedImagePath=$(realpath "$imagePath")
	elif greadlink -f "$imagePath" > /dev/null 2>&1 ; then
		normalizedImagePath=$(greadlink -f "$imagePath")
	fi
	if [ -n "$normalizedImagePath" ]; then
		imagePath="$normalizedImagePath"
	fi
fi

# this adds the build library dir to LD_LIBRARY_PATH
eval "$addBuildCompatibilityLibDir"

# map the shell commands
if [ $isCD ]; then
	outputDir=$tmpDir/cdsource

	sPrefix=
	tPrefix="$outputDir/"
	cd=cd
	scd=:
	cp="$copyattr -d"
	copyAttrs="$copyattr"
	ln=ln
	mkdir=mkdir
	rm=rm
	mkindex="mkindex -d $tPrefix"
elif [ $isImage ]; then
	# If FIFOs are used for the communication with the FS shell, prepare them.
	if $fsShellCommand --uses-fifos; then
		fifoBasePath=/tmp/build_haiku_image-$$-fifo
		toFSShellFifo=${fifoBasePath}-to-shell
		fromFSShellFifo=${fifoBasePath}-from-shell

		rm -f $toFSShellFifo $fromFSShellFifo
		mkfifo $toFSShellFifo $fromFSShellFifo

		# Open the FIFOs such that they are ready for the fsShellCommand. This
		# also makes sure that they remain open until this script exits. When we
		# exit while the FS shell is still running and waiting for commands,
		# closing of our file descriptors will break the FIFOs and the FS shell
		# will exit, too.
		# Note: A bit of trickery is needed since opening one end blocks until
		# someone opens the other end.
		sleep 3<$fromFSShellFifo 1 &
		exec 6>$fromFSShellFifo 3<$fromFSShellFifo
		sleep 5<$toFSShellFifo 1 &
		exec 4>$toFSShellFifo 5<$toFSShellFifo

		# Remove the FIFO files again -- we have the open FDs, so they can
		# still be used and this makes sure they won't hang around any further.
		rm -f $toFSShellFifo $fromFSShellFifo

		# Remap the fsShellCommand and bfsShell such that they don't inherit the
		# wrong FDs. For both fsShellCommand and bfsShell FD 3 is the input from
		# the respectively other program, FD 4 is the output to it.
		actualFSShellCommand="$fsShellCommand"
		actualBFSShell="$bfsShell"

		fsShellCommandWrapper()
		{
			$actualFSShellCommand 3<&3 4>&4 5>&- 6>&- "$@"
		}

		bfsShellWrapper()
		{
			$actualBFSShell 3>&5 4<&6 "$@"
		}

		fsShellCommand=fsShellCommandWrapper
		bfsShell=bfsShellWrapper
	fi

	# set up the other commands
	sPrefix=:
	tPrefix=/myfs/
	cd="$fsShellCommand cd"
	scd="$fsShellCommand cd"
	cp="$fsShellCommand cp -f"
	copyAttrs="$fsShellCommand cp -a"
	ln="$fsShellCommand ln"
	mkdir="$fsShellCommand mkdir"
	rm="$fsShellCommand rm"
	mkindex="$fsShellCommand mkindex"
else
	sPrefix=
	# TODO: This should come from the environment.
	tPrefix="$installDir/"
	cd=cd
	scd=:
	cp="$copyattr -d"
	copyAttrs="$copyattr"
	ln=ln
	mkdir=mkdir
	rm=rm
	mkindex="mkindex -d $tPrefix"
fi


is_in_list()
{
	local element
	for element in $2; do
		if [ "$1" = "$element" ]; then
			return 0
		fi
	done
	return 1
}


extractFile()
{
	# extractFile <archive> <directory> <extractedSubDir>
	archiveFile=$1
	targetExtractedDir=$2
	extractedSubDir=$3

	extractDir=$tmpDir/extract
	$rmAttrs -rf "$extractDir"
	mkdir -p "$extractDir"

	case "$archiveFile" in
		*.zip)
			echo "Extracting $archiveFile ..."
			$unzip -q -d "$extractDir" "$archiveFile"
			;;
		*.tgz|*.tar.gz)
			echo "Extracting $archiveFile ..."
			tar -C "$extractDir" -xf "$archiveFile"
			;;
		*.hpkg)
			echo "Extracting $archiveFile ..."
			if [ -n "$extractedSubDir" ]; then
				$package extract -C "$extractDir" "$archiveFile" \
					"$extractedSubDir"
			else
				$package extract -C "$extractDir" "$archiveFile"
			fi
			;;
		*)
			echo "Unhandled archive extension in build_haiku_image" \
				"extractFile()"
			exit 1
			;;
	esac

	if [ -f $extractDir/.OptionalPackageDescription ]; then
		cat $extractDir/.OptionalPackageDescription >> $copyrightsFile
		echo >> $copyrightsFile
		rm $extractDir/.OptionalPackageDescription
	fi

	$cp -r "${sPrefix}$extractDir/$extractedSubDir/." \
		"${tPrefix}$targetExtractedDir"

	$rmAttrs -rf "$extractDir"
}


downloadFile()
{
	url=$1
	path=$2

	if [ ! -f "$path" ]; then
		if [ "$noDownloads" = "1" ]; then
			echo "ERROR: Would need to download $url, but HAIKU_NO_DOWNLOADS "
				"is set!"
			exit 1
		fi
		wget -O "$path" "$url"
	fi
}


packageFileName()
{
	$package info -f "%fileName%" "$1"
}


mkdir -p $tmpDir
copyrightsFile=$tmpDir/copyrights
$rmAttrs -f $copyrightsFile

if [ $isCD ]; then
	# setup output dir
	$rmAttrs -rf "$outputDir"
	mkdir -p "$outputDir"
fi

# create the image and mount it
if [ $isImage ]; then
	echo

	imageOffsetFlags=
	if [ $isVMwareImage ]; then
		imageOffsetFlags="--start-offset 65536"
	fi

	if [ ! $updateOnly ]; then
		echo "Creating image ..."

		imageFlags="-i${imageSize}M"
		if [ ! "$dontClearImage" ]; then
			imageFlags="$imageFlags -c"
		fi

		if [ $isVMwareImage ]; then
			$vmdkimage -h 64k $imageFlags "$imagePath"
		else
			$createImage $imageFlags "$imagePath"
		fi

		$bfsShell --initialize $imageOffsetFlags "$imagePath" \
			"$imageLabel" "block_size 2048"
		$makebootable $imageOffsetFlags "$imagePath"
	fi

	$bfsShell -n $imageOffsetFlags "$imagePath" > /dev/null &
	sleep 1

	# Close FDs 5 and 6. Those represent the pipe ends that are used by the
	# FS shell. Closing them in the shell process makes sure an unexpected death
	# of the FS shell causes writing to/reading from the other ends to fail
	# immediately.
	exec 5>&- 6>&-

	# bail out, if mounting fails
	$cd .
fi


# Clean out the old packages directory, if updating all packages.
if [ -n "$updateAllPackages" ]; then
	echo "Removing old packages ..."
	$rm -rf "${tPrefix}system/packages"
	$mkdir -p "${tPrefix}system/packages"
fi

if [ ! $updateOnly ] && [ ! $isCD ]; then
echo "Creating filesystem indices..."
	$mkindex -t string Audio:Album
	$mkindex -t string Audio:Artist
	$mkindex -t string Media:Genre
	$mkindex -t string Media:Title
	$mkindex -t string MAIL:account
	$mkindex -t string MAIL:cc
	$mkindex -t string MAIL:chain
	$mkindex -t string MAIL:from
	$mkindex -t string MAIL:name
	$mkindex -t string MAIL:pending_chain
	$mkindex -t string MAIL:priority
	$mkindex -t string MAIL:reply
	$mkindex -t string MAIL:label
	$mkindex -t string MAIL:status
	$mkindex -t string MAIL:subject
	$mkindex -t string MAIL:thread
	$mkindex -t string MAIL:to
	$mkindex -t string META:address
	$mkindex -t string META:city
	$mkindex -t string META:company
	$mkindex -t string META:county
	$mkindex -t string META:email
	$mkindex -t string META:fax
	$mkindex -t string META:group
	$mkindex -t string META:hphone
	$mkindex -t string META:name
	$mkindex -t string META:nickname
	$mkindex -t string META:state
	$mkindex -t string META:url
	$mkindex -t string META:wphone
	$mkindex -t string META:zip

	$mkindex -t int32 Media:Rating
	$mkindex -t int32 Media:Year
	$mkindex -t int32 MAIL:account_id
	$mkindex -t int32 MAIL:read
	$mkindex -t int32 MAIL:when
	$mkindex -t int32 MAIL:draft
	$mkindex -t int32 MAIL:flags
fi

echo "Populating image ..."
while [ $# -gt 0 ]; do
	. $1
	shift
done


# resolve package dependencies
if [ -n "$resolvePackageDependencies" ]; then
	echo "Resolving package dependencies ..."

	packageUrls=`$getPackageDependencies $repositories -- $systemPackages`
	for packageUrl in $packageUrls; do
		packageFileName=`basename $packageUrl`
		if [ "$otherPackages" != "${otherPackages#*$packageFileName}" ]; then
			echo "ERROR: $packageFileName is a dependency of a package installed in /system/packages," \
				"but it is in another (i.e. unactivated) package directory!"
			exit 1
		fi
		packageFilePath="$downloadDir/$packageFileName"
		downloadFile $packageUrl "$packageFilePath"
		$cp "${sPrefix}$packageFilePath" "${tPrefix}system/packages"
		systemPackages="$systemPackages $packageFilePath"
	done

	# validate dependencies of optional packages
	packageUrls=`$getPackageDependencies $repositories -- $systemPackages $otherPackages`
	packageFileNames=""
	for packageUrl in $packageUrls; do
		packageFileNames="$packageFileNames `basename $packageUrl`"
	done
	if [ ! -z "$packageFileNames" ]; then
		echo "ERROR: Some of the unactivated (i.e. optional) packages have the following unmet dependencies:"
		echo $packageFileNames
		exit 1
	fi
fi

# install default settings for packages
for packageFile in $systemPackages; do
	if $package list -p $packageFile | grep -E '^settings/' > /dev/null; then
		extractFile $packageFile system/settings settings
	fi
done


if [ $isCD ]; then
	# generate the attribute stores
	echo "Generating attribute stores ..."
	$generate_attribute_stores "$tPrefix"

	echo "Copying boot image ..."
	$cp "$cdBootFloppy" "$outputDir"

	if command -v xorriso; then
		# build the iso image using xorriso
		echo "Building CD image..."
		xorriso -as mkisofs -r -b `basename $cdBootFloppy` \
			-V "$cdLabel" -o "$cdImagePath" "$tPrefix"
	else
		echo "you need xorriso to create a CD image."
		exit 1
	fi

	# cleanup output dir
	$rmAttrs -rf "$outputDir"
fi

# unmount
if [ $isImage ]; then
	echo "Unmounting ..."
	$fsShellCommand sync
	$fsShellCommand quit
fi
