#!/bin/bash

# configure warning
WARNING="This file is automatically generated, do not modify!"

# Automatically generate list of exported functions
(
    echo "// ${WARNING}" 
    sed -n 's/EXPORT(\([^,{]*\),{/EXPORT_FUNCTION(\1)/p' lib/*.cc 
) > lib/exports.h
echo "  Found $(grep -c EXPORT lib/exports.h) exported functions"
echo "================================================================================"

# if exports changed, re-compile lib.o
if [ -f lib/exports.h~ ];
then
    diff lib/exports.h lib/exports.h~ > /dev/null
    if [[ "$?" != "0" ]];
    then
	rm -f */objs/lib.o
    fi
fi
cp lib/exports.h lib/exports.h~

# Automatically generate interface for exported fermion operators
(
    echo "// ${WARNING}" 
    sed '1,/BEGIN_EXPORT_UNARY_REALD/d;/END_EXPORT_UNARY_REALD/,$d' lib/operators/types.h |
    awk '{ print "UNOP_REALD(" $1 "," NR + 1000 ")" }' 
    sed '1,/BEGIN_EXPORT_UNARY_VOID/d;/END_EXPORT_UNARY_VOID/,$d' lib/operators/types.h |
    awk '{ print "UNOP_VOID(" $1 "," NR + 2000 ")" }' 
    sed '1,/BEGIN_EXPORT_UNARY_DAG_VOID/d;/END_EXPORT_UNARY_DAG_VOID/,$d' lib/operators/types.h |
    awk '{ 
           print "UNOP_VOID_DAG0(" $1 "," NR + 3000 ")";
           print "UNOP_VOID_DAG1(" $1 "," NR + 4000 ")";
    }' 
) > lib/operators/register.h
(
    echo "// ${WARNING}"
    sed '1,/BEGIN_EXPORT_DIRDISP_VOID/d;/END_EXPORT_DIRDISP_VOID/,$d' lib/operators/types.h |
    awk '{ print "DIRDISPOP_VOID(" $1 "," NR + 5000 ")" }'
) > lib/operators/register_dirdisp.h
(
    echo "// ${WARNING}"
    sed '1,/BEGIN_EXPORT_DERIV_DAG_VOID/d;/END_EXPORT_DERIV_DAG_VOID/,$d' lib/operators/types.h |
    awk '{ 
           print "DERIVOP_VOID_DAG0(" $1 "," NR + 6000 ")";
           print "DERIVOP_VOID_DAG1(" $1 "," NR + 7000 ")"; 
    }'
) > lib/operators/register_deriv.h

(
    echo "# ${WARNING}"
    echo "def register(reg, op):"
    sed '1,/BEGIN_EXPORT_UNARY_REALD/d;/END_EXPORT_UNARY_REALD/,$d' lib/operators/types.h |
    awk '{ print "    reg." $1 " = lambda dst, src: op.apply_unary_operator(" NR + 1000 ", dst, src)" }' 
    sed '1,/BEGIN_EXPORT_UNARY_VOID/d;/END_EXPORT_UNARY_VOID/,$d' lib/operators/types.h |
    awk '{ print "    reg." $1 " = lambda dst, src: op.apply_unary_operator(" NR + 2000 ", dst, src)" }' 
    sed '1,/BEGIN_EXPORT_UNARY_DAG_VOID/d;/END_EXPORT_UNARY_DAG_VOID/,$d' lib/operators/types.h |
    awk '{ 
           print "    reg." $1 " = lambda dst, src: op.apply_unary_operator(" NR + 3000 ", dst, src)";
           print "    reg." $1 "Dag = lambda dst, src: op.apply_unary_operator(" NR + 4000 ", dst, src)";
    }' 
    sed '1,/BEGIN_EXPORT_DIRDISP_VOID/d;/END_EXPORT_DIRDISP_VOID/,$d' lib/operators/types.h |
    awk '{ print "    reg." $1 " = lambda dst, src, dir, disp: op.apply_dirdisp_operator(" NR + 5000 ", dst, src, dir, disp)" }'
    sed '1,/BEGIN_EXPORT_DERIV_DAG_VOID/d;/END_EXPORT_DERIV_DAG_VOID/,$d' lib/operators/types.h |
    awk '{
           print "    reg." $1 " = lambda mat, dst, src: op.apply_deriv_operator(" NR + 6000 ", mat, dst, src)";
	   print "    reg." $1 "Dag = lambda mat, dst, src: op.apply_deriv_operator(" NR + 7000 ", mat, dst, src)";
    }'
) > ../gpt/qcd/fermion/register.py

# Automatically generated python files should be formatted with black
python3 -m black -t py36 ../gpt/qcd/fermion/register.py 1> /dev/null 2> /dev/null

# get spins and colors and basis sizes
SPINS=$(egrep "^SPIN\(" lib/spin_color.h | awk 'BEGIN{ FS="(" }{ print $2+0 }')
COLORS=$(egrep "^COLOR\(" lib/spin_color.h | awk 'BEGIN{ FS="(" }{ print $2+0 }')
BASIS_SIZES=$(egrep "^BASIS_SIZE\(" lib/basis_size.h | awk 'BEGIN{ FS="(" }{ print $2+0 }')

echo "  SPINS       = $(echo ${SPINS})"
echo "  COLORS      = $(echo ${COLORS})"
echo "  BASIS_SIZES = $(echo ${BASIS_SIZES})"
echo "================================================================================"

# get all tensors that we need
TENSORS=$(
    echo "iSinglet"
    for s in ${SPINS}
    do
	echo "iMSpin${s}"
	echo "iVSpin${s}"

	for c in ${COLORS}
	do
	    echo "iMSpin${s}Color${c}"
	    echo "iVSpin${s}Color${c}"
	done
    done
    for c in ${COLORS}
    do
	echo "iMColor${c}"
	echo "iVColor${c}"
    done
    for n in ${BASIS_SIZES}
    do
	echo "iVSinglet${n}"
	echo "iMSinglet${n}"
    done
)

# generate tensors.h
(
echo "// ${WARNING}"
for t in ${TENSORS}
do
    echo "PER_TENSOR_TYPE($t)"
done
) > lib/tensors.h

Ntensors=$(($(cat lib/tensors.h | wc -l)-1))
echo "  Instantiating ${Ntensors} tensor types"
echo "================================================================================"

# Automatically generate template instantiations
echo "// ${WARNING}" > lib/instantiate/instantiate.h

# per precision
for precision in "vComplexF,single" "vComplexD,double"
do

    precision_vector=$(echo $precision | awk 'BEGIN{ FS="," }{ print $1 }')
    precision_tag=$(echo $precision | awk 'BEGIN{ FS="," }{ print $2 }')

    for tensor_name in ${TENSORS}
    do
	tensor_root=$(echo ${tensor_name} | tr -d "[:digit:]")
	tensor_args=$(echo ${tensor_name} | tr -s "[:alpha:]" " ")
	tensor_arg_1=$(echo ${tensor_args} | awk '{ print $1 }')
	tensor_arg_2=$(echo ${tensor_args} | awk '{ print $2 }')

	echo "INSTANTIATE(${precision_vector},${precision_tag},${tensor_name})" >> lib/instantiate/instantiate.h

	for f in lib/instantiate/templates/*.template
	do

	    g=lib/instantiate/$(basename $f .template)_${precision_tag}_${tensor_name}.cc

	    if [ $f -nt $g ];
	    then
		(
		    echo "// ${WARNING}"
		    cat $f |
		    sed "s/{precision_vector}/${precision_vector}/g" |
		    sed "s/{precision_tag}/${precision_tag}/g" |
		    sed "s/{tensor_name}/${tensor_name}/g" 
		) > $g

		git add $g
	    fi

	done

	for f in lib/instantiate/templates/${tensor_root}/*.template
	do

	    g=lib/instantiate/$(basename $f .template)_${precision_tag}_${tensor_name}.cc

	    if [ $f -nt $g ];
	    then
		(
		    echo "// ${WARNING}"
		    cat $f |
		    sed "s/{precision_vector}/${precision_vector}/g" |
		    sed "s/{precision_tag}/${precision_tag}/g" |
		    sed "s/{tensor_name}/${tensor_name}/g" |
		    sed "s/{tensor_arg_1}/${tensor_arg_1}/g" |
		    sed "s/{tensor_arg_2}/${tensor_arg_2}/g" 
		) > $g

		git add $g
	    fi

	done

    done

done
