#!/usr/bin/env sh
# MIT License, Copyright (c) 2020 Marvin Borner

set -e

cd "$(dirname "$0")"

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

qemu_with_flags() {
    SDL_VIDEO_X11_DGAMOUSE=0 qemu-system-i386 -no-reboot -vga std -serial stdio -rtc base=localtime -m 256M -net nic,model=${network},macaddr=42:42:42:42:42:42 -net user "$@"
}

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

        # Get sources
        mkdir "${DIR}/src" && cd "${DIR}/src"
        echo "Downloading..."
        curl -sSL "https://ftp.gnu.org/gnu/binutils/binutils-2.34.tar.xz" | tar xJ
        curl -sSL "https://ftp.gnu.org/gnu/gcc/gcc-9.3.0/gcc-9.3.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"
        ../binutils-2.34/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"
        ../gcc-9.3.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}/.."
    fi
}

make_disk() {
    rm -rf disk && mkdir -p disk/font/

    cd disk/font/
    VERSION="1.8.1"
    wget -q "https://github.com/fcambus/spleen/releases/download/$VERSION/spleen-$VERSION.tar.gz"
    tar xf "spleen-$VERSION.tar.gz"
    mv spleen-"$VERSION"/*.psfu .
    rm -rf "spleen-$VERSION"*

    cp /usr/share/kbd/consolefonts/ter-p32b.psf.gz . &&
        cp /usr/share/kbd/consolefonts/ter-p32n.psf.gz . &&
        gunzip ter-p32* || echo "Terminus font not found!"

    cd ../../
}

make_build() {
    if ! [ -d "disk/" ]; then
        echo "Creating disk..."
        make_disk
    fi

    mkdir -p build/
    rm -rf build/*

    printf "\nBuilding...\n"
    make

    # Create disk image
    dd if=/dev/zero of=build/disk.img bs=1k count=16k status=none
    sudo mke2fs -q build/disk.img
    dd if=build/boot.bin of=build/disk.img conv=notrunc status=none
    cp build/kernel.bin . # For nicer disk img
    ./ext2util/ext2util -x build/disk.img -wf kernel.bin -i 5 >/dev/null
    rm kernel.bin

    mkdir -p mnt/
    sudo mount build/disk.img mnt/
    sudo cp -r disk/* mnt/
    sudo cp build/apps/* mnt/
    sudo umount mnt/
    rm -rf mnt/

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

make_test() {
    qemu_with_flags -drive file=build/disk.img,format=raw,index=1,media=disk
}

make_debug() {
    qemu_with_flags -drive file=build/disk.img,format=raw,index=1,media=disk -s -S
}

make_disasm() {
    objdump -drwC -Mintel build/debug.o --visualize-jumps=color | less -R
    #hexdump -C build/kernel.bin | less -R
}

make_append_commands() {
    s=""
    while read -r data; do
        s="${s}
${data}"
    done

    echo "$s" |
        sed -n "/Compiled $1/,/Compiled $2/p" |
        grep -wE 'gcc' |
        grep -w '\-c' |
        jq -nR '[inputs|{directory:"'"$(pwd)/$3"'/", command:., file: match(" [^ ]+$").string[1:]}]' \
            >>compile_commands.json
}

make_sync() {
    ctags -R --exclude=.git --exclude=build --exclude=disk --exclude=cross --exclude=ext2util .

    rm -f compile_commands.json
    output=$(make --always-make --dry-run)
    echo "$output" | make_append_commands libc libk libc
    echo "$output" | make_append_commands libk libgui libgui
    echo "$output" | make_append_commands libgui kernel kernel
    echo "$output" | make_append_commands kernel apps apps
    tr <compile_commands.json '\n' '\r' | sed -e 's/\r]\r\[/,/g' | tr '\r' '\n' >tmp
    mv tmp compile_commands.json
}

make_clean() {
    rm -rf build/
    make clean
}

if [ "${mode}" = "cross" ]; then
    make_cross
elif [ "${mode}" = "build" ]; then
    make_cross
    make_clean
    make_build
elif [ "${mode}" = "clean" ]; then
    make_clean
elif [ "${mode}" = "again" ]; then
    make_test
elif [ "${mode}" = "disasm" ]; then
    make_cross
    make_build
    make_disasm
elif [ "${mode}" = "sync" ]; then
    make_sync
elif [ "${mode}" = "disk" ]; then
    make_disk
elif [ "${mode}" = "debug" ]; then
    make_cross
    make_clean
    make_build
    make_sync &
    make_debug
elif [ "${mode}" = "test" ] || [ "${mode}" = "" ]; then
    make_cross
    make_clean
    make_build
    make_sync &
    make_test
else
    echo "Usage: ./run {cross | clean | build | test | debug | again | disasm | sync | disk}"
    printf "\nDescription of options:\n"
    printf "cross\t\tBuilds the cross compiler\n"
    printf "clean\t\tRemoves the compiled files\n"
    printf "build\t\tBuilds the whole project (cross+clean)\n"
    printf "test\t\tEmulates Melvix with QEMU (cross+clean+build)\n"
    printf "debug\t\tEmulates Melvix with QEMU and debug options (cross+clean+build)\n"
    printf "again\t\tOpens QEMU again using the previous build\n"
    printf "disasm\t\tDisassembles the main kernel binary\n"
    printf "sync\t\tSyncs the 'tags' and 'compile_commands.json' file\n"
    printf "disk\t\tPrepares the userspace disk (e.g. fonts)\n"
    printf "*\t\tAnything else prints this help\n\n"
    echo "The default option is 'test'"
fi