#!/bin/bash

REP_CC=cc
REP_CXX=c++
REP_INCLUDE='-I$(HOME)/projects/include -I/usr/include'
REP_LIBRARIES=
REP_TARGET=target

SMAKE_DIR=~/dev/bash/smake/etc/smake
HELP_FILE=$SMAKE_DIR/help.smk
ENV_FILE=$SMAKE_DIR/env.smk
BUILD_FILE=$SMAKE_DIR/build.smk
RULES_FILE=$SMAKE_DIR/rules.smk

# Debug
DEBUG=1

# Parameters processing
TEMP=`getopt -o hc:x:i:l:t: --long help,cc:,cxx:,include:,libraries:,target: -- "$@"`
eval set -- "$TEMP"

include_changed=false

while true ; do
	case "$1" in
		-h|--help) echo "Usage: smake.sh [key]... [goal]..." ;
			echo "Keys:"
			echo -e "-h, --help\t\t\tShow this help and exit."
			echo -e "-c [CC], --cc [CC]\t\tUse CC as C compiler."
			echo -e "-x [CXX], --cxx [CXX]\t\tUse CXX as C++ compiler." 
			echo -e "-i [INC], --include [INC]\tSet INC as include path."
			echo -e "-l [LIB], --libraries [LIB]\tSet LIB as libraries that must be linked with."
			echo -e "-t [TGT], --target [TGT]\tSet TGT as target name."
			echo
			echo -e "This program works on any Linux with GNU Baurne's shell"
			echo -e "Report bugs to <mecareful@gmail.com>"
			exit 0 ;
			;;
		-c|--cc) REP_CC=$2 ; echo "CC=$REP_CC" ; shift 2 ;;
		-x|--cxx) REP_CXX=$2 ; echo "CXX=$REP_CXX" ; shift 2 ;;
		-i|--include) [ $include_changed == false ] && REP_INCLUDE="" && include_changed=true;  REP_INCLUDE="$REP_INCLUDE `echo $2 | sed "s~^${HOME}~\$\(HOME\)~g ; s~/*$~~g"`" ; shift 2 ;;
		-l|--libraries) REP_LIBRARIES=$2 ; echo "LIBRARIES=$REP_LIBRARIES"; shift 2 ;;
		-t|--target) REP_TARGET=$2 ; echo "TARGET=$REP_TARGET"; shift 2 ;;
		--) shift ; break ;;
		*) echo "Internal error!" ; exit 1 ;;
	esac
done

# ======= Show INCLUDES =======
REP_INCLUDE="`echo $REP_INCLUDE | sed 's~ ~\n~g' | sort -u | tr '\n' ' '`"
echo "INCLUDE=$REP_INCLUDE"; 

# ======= Help =======
cat $HELP_FILE > Makefile
echo >> Makefile

# ======= Test for target =======
TARGET_SRC=
for ext in c cpp cxx cc; do
	[ -f "$REP_TARGET.$ext" ] && TARGET_SRC=$REP_TARGET.$ext && break
done
[ "$TARGET_SRC" == "" ] && echo "source file for $REP_TARGET not found" && exit -1

# ======= Environment =======
sed "s~REP_CC~$REP_CC~ ; s~REP_CXX~$REP_CXX~ ; \
     s~REP_LIBRARIES~$REP_LIBRARIES~ ; s~REP_TARGET~$REP_TARGET~" $ENV_FILE >> Makefile

i=1
for d in $REP_INCLUDE; do
	echo "INCLUDE$i=$d" >> Makefile
	i=$((i+1))
done

echo -n "INCLUDE=" >> Makefile

i=1
for d in $REP_INCLUDE; do
	if [ $i != 1 ]; then
		echo -n ' ' >> Makefile
	fi
	echo -n '-I$(INCLUDE'$i')' >> Makefile
	i=$((i+1))
done
echo >> Makefile

echo >> Makefile

# ======= Build =======
cat $BUILD_FILE >> Makefile

echo >> Makefile

# ======= Rules =======
cat $RULES_FILE >> Makefile

REP_INCLUDE=`echo $REP_INCLUDE | sed "s~-I~~g ; s~\\$(HOME)~${HOME}~g"`
flist[0]=$TARGET_SRC
fpath[0]=$TARGET_SRC
nfiles=1
nparsed=0
fdeplist[0]=`remove_c_comments.pl $TARGET_SRC | grep -P '^[\t ]*#include[\t ]*"' | sed 's~[^"]*"\([^"]*\)".*~\1~' | sort -u`
files_not_found=
while [ $nfiles != $nparsed ]; do
	for f in ${fdeplist[$nparsed]}; do
		extension=`basename $f | sed 's~.*\.~~g'`
		f=`echo $f | sed "s~.$extension$~~"`
		extensions=$extension
		[[ "$extension" == h || "$extension" == hpp || "$extension" == "hxx" || "$extension" == "hh" ]] && extensions="$extension c cpp cxx cc"
		already_in_list=false
		for ext in $extensions; do 
			for i in `seq 0 $((nfiles))`; do
				if [ "${flist[$i]}" == "$f.$ext" ]; then
					already_in_list=true
					break;
				fi
			done
			[ $already_in_list == true ] && continue 

			F=
			for d in . $REP_INCLUDE; do
				if [ -f "$d/$f.$ext" ]; then
					F="$d/$f.$ext"
				elif [ -f "$d/`basename $f.$ext`" ]; then
					F="$d/`basename $f.$ext`"
				fi
				if [ "$F" != "" ]; then
					flist[$nfiles]=$f.$ext
					fpath[$nfiles]=$F
					fdeplist[$nfiles]=`remove_c_comments.pl $F | grep -P '^[\t ]*#include[\t ]*"' | sed 's~[^"]*"\([^"]*\)".*~\1~' | sort -u`
					let nfiles++
					break
				fi
			done
			[[ "$F" == "" && "$ext" == "$extension" ]] && files_not_found=`echo "$files_not_found\n$f.$ext" | sort -u`
			[[ "$F" != "" && "$ext" != "$extension" ]] && break
		done
		[ $already_in_list == true ] && continue 
	done
	let nparsed++
done

# ======= Target rules =======
echo 'target_objs = \' >> Makefile

carry=false
for i in `seq 0 $((nfiles-1))`; do
	extension=`basename ${flist[$i]} | sed 's~.*\.~~g'`
	f=`echo ${flist[$i]} | sed "s~.$extension$~~"`
	[[ "$extension" != c &&	"$extension" != cpp
	   && "$extension" != cxx && "$extension" != cc ]] && continue
	[ $carry == true ] && echo ' \' >> Makefile
	echo -ne "\t`basename $f.o`" >> Makefile
	carry=true
done
echo >> Makefile
echo >> Makefile

echo '$(TARGET): $(target_objs)' >> Makefile
echo -e '\t$(CC) $(LDFLAGS) -o $@ $(target_objs)' >> Makefile
echo >> Makefile
echo >> Makefile

# ======= Object's rules =======
for i in `seq 0 $((nfiles-1))`; do
	extension=`basename ${flist[$i]} | sed 's~.*\.~~g'`
	f=`echo ${flist[$i]} | sed "s~.$extension$~~"`
	[[ "$extension" != c &&	"$extension" != cpp
	   && "$extension" != cxx && "$extension" != cc ]] && continue
	echo -n `basename $f.o:` >> Makefile

	dep_lst=${fdeplist[$i]}
	_dep_lst=
	while [ "$dep_lst" != "$_dep_lst" ]; do
		_dep_lst="$dep_lst"

		for fl in $_dep_lst; do
			for j in `seq 0 $((nfiles-1))`; do
				if [ "$fl" == "${flist[$j]}" ]; then
					dep_lst="$dep_lst ${fdeplist[$j]}"
				fi
			done
		done

		dep_lst=`echo $dep_lst | sed 's~ ~\n~g' | sort -u`
	done

	dep_lst="${flist[$i]} $dep_lst"

	for fl in $dep_lst; do
		for j in `seq 0 $((nfiles-1))`; do
			if [ "${flist[$j]}" == "$fl" ]; then
				echo ' \' >> Makefile
				echo -ne "\t" >> Makefile
				fname=${fpath[$j]}
				k=1
				for d in $REP_INCLUDE ; do
					_fname=`echo ${fpath[$j]} | sed "s~^$d~\$\(INCLUDE$k\)~" `
					[ "$_fname" != "${fpath[$j]}" ] && fname=$_fname
					let k++
				done

				fname=`echo $fname | sed "s~^${HOME}~\$\(HOME\)~g ; s~^\./~~g"`

				echo -n "$fname" >> Makefile
				break
			fi
		done
	done

	echo >> Makefile
	echo >> Makefile
done

# ======= Warning =======
files_not_found=`echo -e "$files_not_found" | sort -u`
if [ "$files_not_found" != "" ]; then
	echo WARNING: Include files not found: $files_not_found
fi