#!/bin/sh

#set -x

exuberant-ctags --kinds-all=* --totals=yes -e -R

ERR() {
    printf '[ERROR] %s\n' "$1"
    [ "$2" ] && exit "$2"
}

INFO() {
    printf '[INFO] %s\n' "$1"
    [ "$2" ] && exit "$2"
}

: "${CC:=gcc}"

SRCDIR='src'
OBJDIR='.obj'
BINDIR='bin'
TARGET=''

clena_op='N'
debug_op='N'
tags_op='N'
deps_op='Y'

while [ "$1" ]
do
    case "$1" in
        --no-debug) debug_op='N';;
        --debug|-d) debug_op='Y';;
        --clean|-c) clean_op='Y';;
        --tags|-t)  tags_op='Y';;
        --no-deps)  deps_op='N';;

        --srcdir|-sd) SRCDIR="$2"; shift ;;
        --objdir|-od) OBJDIR="$2"; shift ;;
        --bindir|-bd) BINDIR="$2"; shift ;;
        --cc)         CC="$2"; shift     ;;

        -*|--*) ERR "Invalid flag: $1" 1;;

        *) TARGET="$1" ;;
    esac
    shift
done

if [ "$clean_op" = 'Y' ]
then
    rm -rfv "$OBJDIR" "$BINDIR"
    exit 0
fi

if [ "$tags_op" = 'Y' ]
then
    INFO 'Genereting etags for all .c and .h files in project'
    find -name '*.c' -type f -exec etags -a {} \;
    find -name '*.h' -type f -exec etags -a {} \;
    exit 0
fi

if [ -z "$TARGET" ]
then
    ERR '*MUST* Provide the target as the last argument.' 1
fi

INCS="-I include/"

CFLAGS="${CFLAGS} -m64 ${INCS} -pedantic -Wall -Wextra -Wshadow
-Wcast-align -Wunused -Wconversion -Wmisleading-indentation
-Wduplicated-cond -Wduplicated-branches -Wlogical-op
-Wdouble-promotion -Wformat=2 -Wbad-function-cast
-Wmissing-declarations -Wmissing-parameter-type
-Wmissing-prototypes -Wnested-externs -Werror"

CC="${CC} -std=c99"

LDFLAGS="${LDFLAGS} -lc -lm"

if [ "$debug_op" = 'Y' ]
then
	CFLAGS="${CFLAGS} -Og -g"
	LDFLAGS="${LDFLAGS} -g"
else
	CFLAGS="${CFLAGS} -O3"
fi

CFLAGS="${CFLAGS} -Og -g"

compile() {
    obj="${OBJDIR}/$1.o"
    src="${SRCDIR}/$1.c"
    [ -d "$(dirname "$obj")" ] || mkdir -pv "$(dirname "$obj")"
    # [ "$obj" -ot "$src" ] || { INFO "$obj Already up to date" ; return 0; }
    INFO "[CC] $obj $src"
    $CC -c $CFLAGS -o "$obj" "$src"
}

link() {
    bin="${BINDIR}/$1"
    objs="${OBJDIR}/$1.o"
    deps="$2"

    for dep in $deps
    do
        objs="$objs $OBJDIR/$dep.o"
    done

    [ -d "$BINDIR" ] || mkdir -pv "$BINDIR"

    for obj in $objs
    do
        # [ "$bin" -ot "$obj" ] && {
            INFO "[LD] $bin $objs";
            $CC $LDFLAGS -o "$bin" $objs;
            return 0;
        # }
    done
    INFO "$bin Already up to date"
    return 0
}

deps="$(grep '#include ".*"' "${SRCDIR}"/"$TARGET".c | \
        sed 's/#include "\(.*\)"/\1/' | \
        tr -d '.h')"

compile "$TARGET" || exit 1

if [ "$deps_op" = 'Y' ]
then
    for dep in $deps
    do
	compile "$dep" || exit 1
    done
fi

link "$TARGET" "$dep" || exit 1

exec ./"$BINDIR"/"$TARGET"
