#!/bin/bash

# dependances :
# mustash flatcc

# template will be stored in :
# prefix/afb-idl/afb-flatbuffers/templates

# sous execs
# prefix/libexec/afbidl


corename() {
	local n="$(basename "$1")"
	echo -n "${n%.*}"
}

PROGRAM=$0
PGNAME=$(corename $0)
PGDIR=$(dirname $0)
: ${TEMPLATE_PATH:=$PWD}

usage() {
	echo "Usage :"
	echo " "
	echo "  $PROGRAM FLATBUFFERS_SCHEMA [OPTION...]"
	echo "  $PROGRAM -h | --help"
	echo " "
	echo "Options :"
	echo " "
	echo "  -c | --converter            generate a converter"
	echo "  -a | --api                  generate an api skeleton"
	echo "  -l | --client               generate an client skeleton"
	echo "  -m | --cmake                generate the CMakeLists.txt"
	echo "  -h | --help                 display help"
	echo "  -o | --out-prefix PREFIX    specify output prefix. By default output"
	echo "                              prefix is based on flatbuffer schema name"
	echo " "
	echo "Note : if no target are specified all targets are generated"
	exit 1
}

err() {
	echo "$*" >&2
	exit 1
}

declare -a TPATH
while [ -n "$TEMPLATE_PATH" ]; do
	p="${TEMPLATE_PATH%%:*}"
	TPATH+=("$p")
	[ "$p" =  "$TEMPLATE_PATH" ] && break;
	TEMPLATE_PATH="${TEMPLATE_PATH#*:}"
done
SUBPATH="afb-idl/afb-flatbuffers"
TPATH+=("$PWD" "$HOME/.config/$SUBPATH" "$PGDIR/../share/$SUBPATH" "$PGDIR")
find_template() {
	for d in "${TPATH[@]}"; do
		f="$d/$1"
		[ -f "$f" ] && { echo "$f"; return 0; }
	done
	return 1
}

header() {
	echo "/*"
	echo " * Generated by $1 based on $2 as schema and $3 as template"
	echo " */"
	echo " "
}

generate() {
	# generate header
	header "$PGNAME" "$1" "$2"

	# Finaly generate the c code with mustach
	# baised on the json input file and the template
	# and put it into output file
	mustach "$1" "$2"
}

fbgen() {
	local what="$1" out="$2" jspec="$3"

	# determine what to generate
	case "$what" in
		cvt) ext=cvt; ixt=types;; # generate a converter
		cli) ext=cli; ixt=apis;;  # generate a client
		api) ext=api; ixt=apis;;  # generate an api
		*) err "invalid call";;   # otherwise return an error
	esac
	
	# Parse the output file name <ofile>-fb${ext}.<wg>
	# for example "sample-fbapi.c" will give :
	# $ofile = "sample"
	# $wg = "c"
	# NB: by default <wg> is "c"
	[ -z "$out" ] && err "no arg"
	ofile="$out"
	wg="${ofile##*.}"
	wg="${wg:-c}"
	ofile="${ofile%.*}"
	ofile="${ofile%-fb${ext}}"

	# If an input file is given in second parametter use it,
	# otherway checkout for a file based on output name suffix
	inspec="${jspec:-${ofile}-rpc-types.json}"

	# Return error if not found
	[ -f "$inspec" ] || err "spec $inspec not found"

	# search for a template named fd-template-<ext>.<wg> in working directory
	# then in afb-ild config directory and finaly in <executable dir>/../share/afbidl
	if ! templ=$(find_template "fb-template-$ext.$wg"); then
		# return error if no template is found
		err "template fb-template-$ext.$wg not found"
	fi

	# generate the file
	generate "$inspec" "$templ" > "$ofile-fb$ext.$wg"
}

POSITIONAL=()
declare -A param

while [[ $# -gt 0 ]]; do
	key="$1"

	case $key in
		-c|--converter)
		param[converter]=YES
		shift # past argument
		;;

		-a|--api)
		param[api]=YES
		param[converter]=YES
		shift # past argument
		;;

		-l|--client)
		param[client]=YES
		shift # past argument
		;;

		-o|--out-prefix)
		out_prefix=$2
		shift 2 # past argument
		;;

		-m|--cmake)
		param[cmake]=YES
		shift
		;;

		-h|--help)
		usage
		;;

		*)    # unknown option
		POSITIONAL+=("$1") # save it in an array for later
		shift # past argument
		;;
	esac
done

# If nothing is specified build all
if [[ ${#param[@]} -eq 0 ]]; then
	param[converter]=YES
	param[api]=YES
	param[client]=YES
	param[cmake]=YES
fi

# Get flatbuffer schema as imput
if [[ ${#POSITIONAL[@]} -eq 0 ]]; then
	echo "Flatbuffers schema is missing !"
	usage
fi

set -- "${POSITIONAL[@]}" # restore positional parameters
fbschema="$1"
fbsname="$(corename $fbschema)"
prjname="$fbsname"

# Set output prefix based on input if not specifyed with option --output-prefix
if [[ -z "$out_prefix" ]]; then 
	out_prefix="$fbsname"

# if output prefix is a directory add input name as file prefix
elif [[ "${out_prefix%/}" != "$out_prefix" ]]; then
	out_prefix+="$fbsname"
fi
outname="$(corename "$out_prefix")"

echo "selected params : ${!param[@]}"
echo "fb schema       : $fbschema"
echo "out prefix      : $out_prefix"

# check dependencies
find_in_libexec() {
	local name="$1" d f
	for d in "$PGDIR" "$PGDIR/../libexec" "$PGDIR/libexec"; do
		f="$d/$name"
		[ -x "$f" ] && { echo -n "$f"; break; }
	done
}
BFBS2JSON="$(find_in_libexec bfbs2json)"
JFBSEXTR="$(find_in_libexec jfbsextr)"
[ -z "$BFBS2JSON" ] && err "required bfbs2json is unreachable"
[ -z "$JFBSEXTR" ] && err "required jfbsextr is unreachable"
[ -z "$(which mustach)" ] && err "required mustach is not installed"
[ -z "$(which flatcc)" ] && err "required flatcc is not installed"

###############################
# Generate intermediate files #
###############################

TEMPDIR="/tmp/$PGNAME.$$"
trap "rm -r '$TEMPDIR'" EXIT
[ ! -e "$TEMPDIR" ] && mkdir -p "$TEMPDIR"

# binary flatbuffer schema
BFBS="$TEMPDIR/$fbsname.bfbs"
flatcc --schema -o "$TEMPDIR" "$fbschema"
[ -e "$BFBS" ] || err "fail to generate binary flatbuffer schema $BFBS"

ROOTJSON=$(cat << EOC
{
	"schema": "$(basename $fbschema)",
	"schema-full": "$fbschema",
	"project-name": "$prjname",
	"generate-json": true,
	"date": "$(date)"
}
EOC
)
# Json description of schema
RPC_JSON="$TEMPDIR/$fbsname.json"
$BFBS2JSON "$BFBS" | $JFBSEXTR --pretty --root "$ROOTJSON" > "$RPC_JSON"
[ -e "$RPC_JSON" ] || err "Fail to generate json extract of schema $RPC_JSON"

###################
# generate output #
###################

# if out directory doesn't exist create it
if [ "${out_prefix%/*}" != "$out_prefix" ]; then
	mkdir -p "${out_prefix%/*}" || err "Failed to create directory ${out_prefix%/*}"
fi

# flatcc header
echo "generating : $out_prefix-flatcc.h"
flatcc -c -a --json --outfile="$out_prefix-flatcc.h" "$fbschema"

# converter
if [[ -n "${param[converter]}" ]]; then
	echo "generating : $out_prefix-fbcvt.c"
	fbgen cvt "$out_prefix-fbcvt.c" "$RPC_JSON"
	echo "generating : $out_prefix-fbcvt.h"
	fbgen cvt "$out_prefix-fbcvt.h" "$RPC_JSON"
fi

# api
if [[ -n "${param[api]}" ]]; then
	echo "generating : $out_prefix-fbapi.c"
	fbgen api "$out_prefix-fbapi.c" "$RPC_JSON"
	echo "generating : $out_prefix-fbapi.h"
	fbgen api "$out_prefix-fbapi.h" "$RPC_JSON"
fi

# client
if [[ -n "${param[client]}" ]]; then
	echo "generating : $out_prefix-fbcli.c"
	fbgen cli "$out_prefix-fbcli.c" "$RPC_JSON"
	echo "generating : $out_prefix-fbcli.h"
	fbgen cli "$out_prefix-fbcli.h" "$RPC_JSON"
fi

# cmake
if [[ -n "${param[cmake]}" ]]; then
	echo "generating : $(dirname "$out_prefix")/CMakeLists.txt"
	template=$(find_template fb-template-CMakeLists.txt)
	mustach $RPC_JSON $template > "$(dirname "$out_prefix")/CMakeLists.txt"
fi
