#!/usr/bin/env sh

#
# Argument Parsing
#

echo "Build Configuration"
echo "-------------------"

BUILD="build"
OUT="out"
SRC="src"
INCLUDE="src/include"
TOOLS="tools/src"
SCHEMA="Schema"
CYTOPLASM="Cytoplasm"

CFLAGS="-O1 -D_DEFAULT_SOURCE -I${INCLUDE} -I${BUILD}"
LIBS="-lm -pthread -lCytoplasm"


# Set default args for all platforms
SCRIPT_ARGS="--prefix=/usr/local --bin-name=telodendria --version=1.7.0-alpha4"

if [ -f "${CYTOPLASM}/configure" ]; then
    SCRIPT_ARGS="${SCRIPT_ARGS} --cytoplasm=${CYTOPLASM}"
else
    SCRIPT_ARGS="${SCRIPT_ARGS} --cytoplasm=" # No cytoplasm path.
fi

# Set compiler depending on the platform.
case "$(uname)" in
    Linux|NetBSD)
        # These systems typically use GCC.
        SCRIPT_ARGS="${SCRIPT_ARGS} --cc=gcc"
        ;;
    OpenBSD|FreeBSD)
        # These systems typically use Clang.
        SCRIPT_ARGS="${SCRIPT_ARGS} --cc=clang"
        ;;
    *)
        # Use default compiler which is required to be present on
        # all POSIX-compliant systems.
        SCRIPT_ARGS="${SCRIPT_ARGS} --cc=c99"
        ;;
esac

# Append any additional args specified by user
SCRIPT_ARGS="${SCRIPT_ARGS} $@"

echo "Processing options..."
echo "Ran with arguments: $SCRIPT_ARGS"

# Process all arguments
for arg in $SCRIPT_ARGS; do
    case "$arg" in
        --cc=*)
            CC=$(echo "$arg" | cut -d '=' -f 2-)
            case "${CC}" in
                gcc*|clang*)
                    # "Fancy" compilers that support a plethora of additional flags we
                    # want to enable if present.
                    CFLAGS="-Wall -Wextra -Werror -pedantic -std=c99 -O3 ${CFLAGS}"
                    LDFLAGS="${LDFLAGS} -flto -fdata-sections -ffunction-sections -s -Wl,-gc-sections"
                    ;;
            esac
            ;;
        --prefix=*)
            PREFIX=$(echo "$arg" | cut -d '=' -f 2-)
            ;;
        --bin-name=*)
            BIN_NAME=$(echo "$arg" | cut -d '=' -f 2-)
            ;;
        --version=*)
            VERSION=$(echo "$arg" | cut -d '=' -f 2-)
            ;;
        --enable-debug)
            DEBUG="-O0 -g"
            ;;
        --disable-debug)
            DEBUG=""
            ;;
        --cytoplasm=*)
            CYTOPLASM=$(echo "$arg" | cut -d '=' -f 2-)
            if [ -n "${CYTOPLASM}" ]; then
                if [ ! -f "${CYTOPLASM}/configure" ]; then
                    echo "Path for Cytoplasm does not appear to actually contain Cytoplasm source:"
                    echo "${CYTOPLASM}"
                    exit 1
                fi

                CFLAGS="${CFLAGS} -I${CYTOPLASM}/include"
                LDFLAGS="-L${CYTOPLASM}/out/lib ${LDFLAGS}"
            fi
            ;;
        *)
            echo "Invalid argument: $arg"
            exit 1
            ;;
    esac
done

CFLAGS="${CFLAGS} '-DTELODENDRIA_VERSION=\"${VERSION}\"' ${DEBUG}"
LDFLAGS="${LDFLAGS} ${LIBS}"

#
# Makefile generation
#

collect() {
	from="$1"
	orig_ext="$2"
	new_ext="$3"
	prefix="$4"
	exec="$5"

	find "${from}" -name "*${orig_ext}" -type f | while IFS= read -r src; do
		src=$(echo "$src" | sed -e "s|^${from}||g")
		obj=$(echo "$src" | sed -e "s|${orig_ext}\$|${new_ext}|g")

		obj="${prefix}${obj}"
		src="${from}${src}"

		"${exec}" "${src}" "${obj}"
	done
}

prefix() {
    prefix="$1"
    shift

    for thing in $@; do
        printf "${prefix}${thing} "
    done
}

cytoplasm_tool() {
    tool="$1"

    if [ -n "${CYTOPLASM}" ]; then
        echo "LD_LIBRARY_PATH=${CYTOPLASM}/out/lib ${CYTOPLASM}/out/bin/$tool"
    else
        echo "$tool"
    fi
}

print_src() {
	printf '%s ' "$1"
}

print_obj() {
	printf '%s ' "$2"
}

get_deps() {
    src="$1"

    ${CC} -I${INCLUDE} -I${BUILD} $(if [ -n "${CYTOPLASM}" ]; then echo "-I${CYTOPLASM}/include"; fi) -E "$src" \
        | grep '^#'                          \
        | awk '{print $3}'                   \
        | cut -d '"' -f 2                    \
        | sort                               \
        | uniq                               \
        | grep -v '^[/<]'                    \
        | grep -e "^${SRC}/" -e "^${BUILD}/" \
        | while IFS= read -r dep; do
        printf "%s " "$dep"
    done
}

compile_obj() {
	src="$1"
	obj="$2"

	pref="${obj}: $(get_deps ${src})"
    echo "$pref $(collect ${SCHEMA}/ .json .h ${BUILD}/Schema/ print_obj)"
	echo "${TAB}@mkdir -p $(dirname ${obj})"
	echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${src}\""
}

compile_bin() {
	src="$1"
	out="$2"

    depObjs=$(prefix ${BUILD}/ CanonicalJson.o Telodendria.o)
    
    echo "${out}: ${src}"
    echo "${TAB}@mkdir -p ${OUT}/bin"
    echo "${TAB}\$(CC) \$(CFLAGS) -o \"${out}\" \"${src}\" $depObjs \$(LDFLAGS)"
}

compile_doc() {
	src="$1"
	out="$2"

    if echo "${src}" | grep "Schema" > /dev/null; then
        return
    fi

	echo "${out}: ${src}"
	echo "${TAB}@mkdir -p ${OUT}/man/man3"
	echo "${TAB}$(cytoplasm_tool hdoc) -D \"Os=${BIN_NAME}\" -i \"${src}\" -o \"${out}\""
}

print_doc() {
    if echo "${src}" | grep "Schema" > /dev/null; then
        return
    fi
	printf '%s ' "$2"
}

compile_schema() {
    src="$1"
    out="$2"

    obj="${BUILD}/Schema/${out}.o"

    echo "${BUILD}/Schema/${out}.h:"
    echo "${TAB}@mkdir -p ${BUILD}/Schema"
    echo "${TAB}$(cytoplasm_tool j2s) -s \"${src}\" -h \"${BUILD}/Schema/${out}.h\" -c \"${BUILD}/Schema/${out}.c\""

    echo "${BUILD}/Schema/${out}.c:"
    echo "${TAB}@mkdir -p ${BUILD}/Schema"
    echo "${TAB}$(cytoplasm_tool j2s) -s \"${src}\" -h \"${BUILD}/Schema/${out}.h\" -c \"${BUILD}/Schema/${out}.c\""

    echo "${obj}: ${src} ${BUILD}/Schema/${out}.c"
    echo "${TAB}@mkdir -p ${BUILD}/Schema"
    echo "${TAB}\$(CC) \$(CFLAGS) -fPIC -c -o \"${obj}\" \"${BUILD}/Schema/${out}.c\""
}

install_out() {
	src="$1"
	out="$2"

	echo "${TAB}install -D \"$src\" \"$out\""
}

install_man() {
	src="${OUT}/man/man3/${BIN_NAME}-$(basename $1 .h).3"
	out="$2"

	echo "${TAB}install -D \"$src\" \"$out\""
}

uninstall_out() {
	src="$1"
	out="$2"

	echo "${TAB}rm \"$out\""
}

echo "Generating Makefile..."

OBJS="$(collect ${SRC}/ .c .o ${BUILD}/ print_obj) $(collect ${SCHEMA}/ .json .o ${BUILD}/Schema/ print_obj)"
TAB=$(printf '\t')

cat << EOF > Makefile
.POSIX:

# Generated by '$0' on $(date).
# This file should generally not be manually edited.

CC = ${CC}
PREFIX = ${PREFIX}
CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS}

all: ${BIN_NAME} docs tools
docs: $(collect ${INCLUDE}/ .h .3 ${OUT}/man/man3/${BIN_NAME}- print_doc)
tools: $(collect ${TOOLS}/ .c '' ${OUT}/bin/ print_obj)

format:
${TAB}find . -name '*.c' | while IFS= read -r src; do \\
${TAB}  if indent "\$\$src"; then \\
${TAB}    rm \$\$(basename "\$\$src").BAK; \\
${TAB}  fi \\
${TAB}done

license:
${TAB}find . -name '*.[ch]' | while IFS= read -r src; do \\
${TAB}  srcHeader=\$\$(grep -n -m 1 '^ \*/' "\$\$src" | cut -d ':' -f 1); \\
${TAB}  head -n\$\$srcHeader \$\$src | \\
${TAB}    diff -u -p - "LICENSE.txt" | \\
${TAB}    patch "\$\$src" | grep -v "^Hmm"; \\
${TAB}done

${BIN_NAME}: ${OUT}/bin/${BIN_NAME}

install: ${BIN_NAME}
${TAB}install -D ${OUT}/bin/${BIN_NAME} \$(PREFIX)/bin/${BIN_NAME}

uninstall:
${TAB}rm \$(PREFIX)/bin/${BIN_NAME}

clean:
${TAB}rm -r "${BUILD}" "${OUT}"

${OUT}/bin/${BIN_NAME}: ${OBJS}
${TAB}@mkdir -p "${OUT}/bin"
${TAB}\$(CC) -o "${OUT}/bin/${BIN_NAME}" ${OBJS} \$(CFLAGS) \$(LDFLAGS)

$(collect ${SCHEMA}/ .json '' '' compile_schema)
$(collect ${SRC}/ .c .o ${BUILD}/ compile_obj)
$(collect ${TOOLS}/ .c '' ${OUT}/bin/ compile_bin)
$(collect ${INCLUDE}/ .h .3 ${OUT}/man/man3/${BIN_NAME}- compile_doc)

$(
if [ -n "${CYTOPLASM}" ]; then
    echo "cytoplasm:"
    echo "${TAB}cd ${CYTOPLASM} && ./configure && \$(MAKE)"
fi
)

EOF

echo "Done. Run 'make' to build ${BIN_NAME}."