#!/usr/bin/env sh

cd "$(dirname "$0")" || exit 1

mode="${1}"
network="rtl8139"

qemu_with_flags() {
	if [ "${mode}" = "image" ] || [ "${mode}" = "image_debug" ]; then
		# TODO: Find out why kvm install is incredibly slow
		SDL_VIDEO_X11_DGAMOUSE=0 qemu-system-i386 -no-reboot -vga std -smp "$(nproc)" -serial mon:stdio -rtc base=localtime -m 256M -net nic,model=${network},macaddr=42:42:42:42:42:42 -net user "$@"
	else
		SDL_VIDEO_X11_DGAMOUSE=0 qemu-system-i386 -enable-kvm -cpu host -no-reboot -vga std -smp "$(nproc)" -serial stdio -rtc base=localtime -m 256M -net nic,model=${network},macaddr=42:42:42:42:42:42 -net user "$@"
	fi
}

compile_with_flags() {
	if [ "${mode}" = "image" ] || [ "${mode}" = "image_debug" ]; then
		GCC_COLORS=1 ccache i686-elf-gcc -std=gnu99 -ffreestanding -nostdlib -Wall -Wextra -Wno-unused-parameter -D INSTALL_MELVIX "$@" || exit 1
	else
		GCC_COLORS=1 ccache i686-elf-gcc -std=gnu99 -ffreestanding -nostdlib -Wall -Wextra -Wno-unused-parameter "$@" || exit 1
	fi

}

make_cross() {
	if [ ! -d "./cross/" ]; then
		# Create directory
		mkdir -p cross
		cd cross || exit 1
		DIR=$(pwd)

		# Get sources
		mkdir "${DIR}/src" && cd "${DIR}/src" || exit 1
		echo "Downloading..."
		curl -sSL "https://ftp.gnu.org/gnu/binutils/binutils-2.32.tar.xz" | tar xJ
		curl -sSL "https://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.xz" | tar xJ

		# Prepare compiling
		mkdir -p "${DIR}/opt/bin"
		export PREFIX="${DIR}/opt"
		export TARGET=i686-elf
		export PATH="$PREFIX/bin:$PATH"

		# Compile binutils
		mkdir "${DIR}/src/build-binutils" && cd "${DIR}/src/build-binutils" || exit 1
		../binutils-2.32/configure --target="$TARGET" --prefix="$PREFIX" --with-sysroot --disable-nls --disable-werror
		make
		make install

		# Compile GCC
		mkdir "${DIR}/src/build-gcc" && cd "${DIR}/src/build-gcc" || exit 1
		../gcc-9.2.0/configure --target="$TARGET" --prefix="$PREFIX" --disable-nls --enable-languages=c,c++ --without-headers
		make all-gcc
		make all-target-libgcc
		make install-gcc
		make install-target-libgcc

		cd "${DIR}/.." || exit 1
	else
		# Should be sourced to take effect
		cd cross || exit 1
		DIR=$(pwd)
		export PREFIX="${DIR}/opt"
		export TARGET=i686-elf
		export PATH="$PREFIX/bin:$PATH"
		cd ..
	fi
}

make_build() {
	echo "Building..."
	mkdir -p ./build/kernel && mkdir -p ./build/userspace

	# Assemble ASM files
	find ./src/kernel/ -name \*.asm >./build/tmp
	while read -r line; do
		stripped=$(echo "${line}" | sed -r 's/\//_/g')
		stripped=${stripped#??????}
		stripped=${stripped%%???}o
		nasm -f elf ./"${line}" -o ./build/kernel/asm_"${stripped}" || exit 1
	done <./build/tmp
	rm ./build/tmp

	# Make all kernel C files
	find ./src/kernel/ -name \*.c >./build/tmp
	while read -r line; do
		stripped=$(echo "${line}" | sed -r 's/\//_/g')
		stripped=${stripped#??????}
		stripped=${stripped%%?}o
		compile_with_flags -Os -s -c ./"${line}" -I ./src -D ${network} -o ./build/kernel/"${stripped}"
	done <./build/tmp
	rm ./build/tmp

	# Link kernel ASM and C objects
	compile_with_flags -Os -s ./build/kernel/*.o -T ./src/kernel/linker.ld -I ./src -o ./build/melvix.bin

	# Modules
	# TODO: Find out why no font optimizations cause strange glitches
	compile_with_flags -Os -c ./src/resources/font.c -o ./build/font.o
	i686-elf-objcopy -O binary ./build/font.o ./build/font.bin
	rm ./build/font.o

	# Userspace
	# TODO: Find out why userspace optimizations (-Os) cause fatal errors
	find ./src/userspace/ -name \*.c >./build/tmp
	while read -r line; do
		stripped=$(echo "${line}" | sed -r 's/\//_/g')
		stripped=${stripped#??????}
		stripped=${stripped%%?}o
		compile_with_flags -O2 -c ./"${line}" -I ./src/userspace -o ./build/userspace/"${stripped}"
	done <./build/tmp
	rm ./build/tmp
	compile_with_flags -emain -O2 ./build/userspace/*.o -I ./src/userspace -o ./build/user.bin

	# Create ISO
	mkdir -p ./iso/boot/grub/
	cp ./build/melvix.bin ./iso/boot/kernel.bin
	cp ./src/bootloader/grub.cfg ./iso/boot/grub/
	cp ./build/user.bin ./iso/user.bin
	cp ./build/font.bin ./iso/font.bin
	grub-mkrescue -o ./build/melvix.iso ./iso/
	dd if=/dev/zero of=./build/ext2_hda.img bs=1k count=100000 >/dev/zero
	mkfs -t ext2 -i 1024 -b 1024 -F ./build/ext2_hda.img >/dev/zero
	fdisk ./build/ext2_hda.img <<EOF
x
c
10
h
16
s
63
r
n
p
1
2048
199999
w
EOF

	cp ./build/ext2_hda.img ./build/ext2_hdb.img
	cp ./build/ext2_hda.img ./build/ext2_hdc.img
	cp ./build/ext2_hda.img ./build/ext2_hdd.img

	mkdir mnt/
	sudo mount ./build/ext2_hda.img mnt/
	echo "Bananenkuchen" | sudo tee -a mnt/test.txt
	sudo umount mnt
	rm -r mnt/

	printf "Build finshed successfully!\n\n"
}

make_test() {
	qemu_with_flags -cdrom ./build/melvix.iso -hda ./build/ext2_hda.img -hdb ./build/ext2_hdb.img -hdd ./build/ext2_hdd.img -boot d
}

make_debug() {
	qemu_with_flags -s -cdrom ./build/melvix.iso #-drive file=./build/hdd10M.img,format=raw
}

make_image_debug() {
	qemu_with_flags -cdrom ./build/melvix.iso #-drive file=./build/hdd10M.img,format=raw
	#qemu_with_flags -drive file=./build/hdd10M.img,format=raw
}

make_image() {
	#start=$(date +%s.%N)
	#qemu_with_flags -nographic -cdrom ./build/melvix.iso -drive file=./build/hdd10M.img,format=raw >install.log
	#end=$(date +%s.%N)
	#cat install.log
	#printf "[LOG END]\n\n"
	#tail install.log | grep -q "Installation successful!" && echo Booted and installed within "$(echo "$end - $start" | bc -l)" seconds || echo Installation failed! && exit 1
	#rm install.log
	echo "This test is currently disabled"
}

make_sync() {
	rm tags compile_commands.json
	ctags -R --exclude=.git --exclude=build --exclude=iso --exclude=cross .
	mkdir -p cmake
	cd cmake || exit 1
	cmake .. >/dev/null
	mv compile_commands.json ..
	cd .. || exit 1
}

make_tidy() {
	find ./src -type f -name "*.sh" | xargs -l -i sh -c 'shfmt -w {}'
	shfmt -w ./run
	find ./src -type f -regex '.*\.\(c\|h\)' -exec clang-format -i {} \;
	find ./src -type f | xargs -l -i sh -c 'printf %s "$(< {})" > {}'
}

make_font() {
	cd ./src/resources/ || exit 1
	sh conv.sh
	cd ../../ || exit 1
}

make_clean() {
	rm -rf ./build ./iso
}

if [ "${mode}" = "cross" ]; then
	make_cross
elif [ "${mode}" = "build" ]; then
	make_cross
	make_clean
	make_build
elif [ "${mode}" = "clean" ]; then
	make_clean
elif [ "${mode}" = "test" ]; then
	make_cross
	make_clean
	make_build
	make_test
elif [ "${mode}" = "debug" ]; then
	make_debug
elif [ "${mode}" = "image_debug" ]; then
	make_cross
	make_clean
	make_build
	make_image_debug
elif [ "${mode}" = "image" ]; then
	make_cross
	make_clean
	make_build
	make_image
elif [ "${mode}" = "sync" ]; then
	make_sync
elif [ "${mode}" = "tidy" ]; then
	make_tidy
elif [ "${mode}" = "font" ]; then
	make_font
	make_tidy
else
	echo "Please use the following syntax:"
	echo "./run {cross | build | clean | test | debug | image | sync | tidy | font}"
fi