#!/bin/bash
# validne opcije su:
#	-f <ulazna datoteka>	- datoteka koja sadrzi kod koji ce se interpretirati/transliterirati
#	-o <izlazna datoteka>	- datoteka koja sadrzi transliterirani kod, tip konverzije odredjuje -t opcija
#	-t <tip konverzije>	- za ovu opciju moraju biti specificirane i ulazna i izlazna datoteka. izlazna datoteka se, ukoliko postoji,
#				  prepisuje, a ako ne postoji, stvara se, a ukoliko se ne moze stvoriti, prijavljuje se greska.
#				  validne konverzije su:
#					bfpp	- brainfuck++		ulazna se ASCII bfpp datoteka prevodi u binarni oblik
#					bf	- brainfuck		ulazna se ASCII bf datoteka prevodi u bfx ASCII kod
#					bfx	- brainfuck extended	ulazna se ASCII bfx datoteka prevodi u bfpp kod
#					bfb	- bf binary		ulazna se binarna datoteka prevodi u bfpp kod
#				* ako bfpp datoteka ima neparan broj bajtova, konacna ce flat binary datoteka imati jednu nulu viska za
#				  posljednji znak (\x0$t u kodu), sto se ne moze izbjeci jer jedan bfpp mnemonik zauzima pola bajta
#				* ako nije specificirana -x opija vrsi se intpretiranje zadanoga formata
#	-x			- ne interpretiraj kod, vec vrsi transliteraciju odredjenu opcijom -t
# Dakle, tip konverzije odredjuje odredisni format zapisa, s tim da on-the-fly konverzija nije moguca. za izvodjenje

while getopts ":xf:t:o:" opcija; do
	case $opcija in
		x ) xlate=1;;
		f ) in_datoteka=$OPTARG;;
		t ) tip=$OPTARG;;
		o ) out_datoteka=$OPTARG;;
		* ) echo "Unknown option \"$opcija\"";;
	esac
done
shift $(($OPTIND - 1))

usage="Usage: $(basename $0) [-f in_file] -[bx] [-t type(bf, bfpp, bfc)] [-o out_file]"

# provjera validnosti kombinacije parametara. sa -x opcijom mora biti specificirana i ulazna i izlazna datoteka koje moraju postojati odnosno stvaraju se.
if [ "$xlate" = "1" ]; then
	if [ -z "$in_datoteka" ] || [ -z "$out_datoteka" ] || [ ! -r "$in_datoteka" ]; then
		echo $usage && exit 1
	fi
	if [ -f $out_datoteka ]; then
		: > "$out_datoteka"
	else
		touch "$out_datoteka"
		if [ $? -eq 1 ]; then
			echo "Error: unable to create $out_datoteka" && exit 1
		fi
	fi
	chmod +w "$out_datoteka"
	if [ ! -w "$out_datoteka" ]; then
		echo "Eroor: cannot write to file \"$out_datoteka\"" && exit 1
	fi
	chmod +r "$in_datoteka"
	if [ ! -r "$in_datoteka" ]; then
		echo "Error: cannot read from file \"$in_datoteka\"" && exit 1
	fi
	# sa -x opcijom mora biti specificiran i tip
	if [ -z "$tip" ]; then
		echo $usage && exit 1
	fi
# u slucaju da vrsimo interpretiranje, mora biti specificiran i tip i validna ulazna datoteka
else
	if [ -z "$tip" ]; then
		echo $usage && exit 1
	fi
	chmod +r "$in_datoteka"
	if [ ! -r "$in_datoteka" ]; then
		echo "Error: cannot read from file \"$in_datoteka\"" && exit 1
	fi
fi

kod=( $(cat "$in_datoteka") )
kod=$(echo ${kod[@]} | tr -d \ )
hexy=""
pozicija=0
if [ "$xlate" = "1" ]; then
	case "$tip" in
		bfpp	)
			while : ; do
				t=${kod:pozicija:2}
				if [ "$t" = "" ]; then
					break
				fi
				hexy="$hexy\\x$t"
				(( pozicija+=2 ))
			done
			echo -e -n "$hexy" > $out_datoteka
			exit 0
			;;
		bf	) echo "jednog dana....";;
		bfx	) echo "jednog dana....";;
		bfb	) echo "jednog dana....";;
		*	) echo "Error: unknown input type \"$tip\"";;
	esac
	exit 0
fi

stari_tty=$(stty -g)
stty -icanon
declare -a a
a[0]=0
p=0
ugn_brojac=0				# LIFO
ugn_poz=
prefix=""
tmp_preskok=0
trap 'stty $stari_tty; exit' 0 1 2 3 15

procesiraj_mnemonik() {
	if [[ -n "$prefix" ]]; then
		case "$1" in
			[!c-fC-F]	)
				echo "Error: Invalid construct: \"$prefix\" prefixed $1 at position $pozicija" && exit 1 ;;
			*	)	;;
		esac
	fi
	case "$1" in
		0	)		# [
			if [ -z "${a[$p]}" ] || [ ${a[$p]} -eq 0 ]; then		# uvijet nije zadovoljen, pici dalje..
				while [ "${kod:pozicija:1}" != "1" ] || [ $tmp_preskok -ne 0 ]; do
					if [ "${kod:pozicija:1}" = "0" ]; then
						(( tmp_preskok++ ))
					elif [ "${kod:pozicija:1}" = "1" ]; then
						(( tmp_preskok-- ))
						if [ $tmp_preskok -eq 0 ] && [ "${kod:pozicija:1}" = "1" ] ; then
							break
						fi
					fi
					(( pozicija++ ))
				done
			else 
				ugn_poz[$ugn_brojac]=$pozicija
				(( ugn_brojac++ ))
			fi ;;
		1	)		# ]
			if [ $ugn_brojac -eq 0 ]; then
				echo "Error: unmatched ] @ position \"$pozicija\", terminating...."
				exit 1
			fi
			if [ -z "${a[$p]}" ] || [ ${a[$p]} -eq 0 ]; then		# uvijet nije zadovoljen, pici dalje..
				(( ugn_brojac-- ))
			else
				pozicija=${ugn_poz[(( $ugn_brojac-1 ))]}
				(( ugn_brojac-- ))
				(( pozicija-- ))
			fi ;;
		[3-9]	) prefix="$1";;
		[eE]	) (( p++ ));;	# >
		[fF]	)		# <
			if [ "$p" -eq 0 ]; then
				echo "Error: end od array reached @ position $pozicija"
				exit 1
			else (( p-- ))
			fi;;
		[cC]	) (( a[p]++ ));;	# +
		[dD]	) (( a[p]-- ));;	# -
		[bB]	) char=$(head -c1)
			if [ -z $char ]; then
				char=$'\xA'
			fi
			a[$p]=$(perl -e "print ord ('$char');");;
		[aA]	)		# .
			if [ ${a[$p]} -le 0 ]; then
				echo "printam \""${a[$p]}"\" na poziciji \"$pozicija\""
			else
				perl -e "printf('%c', "${a[$p]}");"
			fi ;;
		2	) debug;;		# #
		" "	);;
		*	) echo "Error: unknown mnemonic \"$1\", terminating..." && exit 1 ;;
	esac;
}

debug() {
	printf "mnemonik=\"$t\" p=%d a[p]=%d\n" $p ${a[$p]}
}

case "$tip" in
	bfpp	)	
		while : ; do
			t=${kod:pozicija:1}
			if [ "$t" = "" ]; then
				exit 0
			fi
			if [ "$prefix" = "" ]; then
				procesiraj_mnemonik "$t"
			else
				for pp in $(seq 1 $prefix); do
					procesiraj_mnemonik "$t"
				done;
				prefix=""
			fi
			(( pozicija++ ))
		done
		if [ $ugn_brojac -ne 0 ]; then
			echo "Error: unmatched [ at EOF" && exit 1
		fi ;;
	bf	) echo "jednog dana...." ;;
	bfx	) echo "jednog dana...." ;;
	bfb	) echo "jednog dana...." ;;
	*	) echo "Error: unknown input type \"$tip\"" ;;
esac
	exit 0
stty "$stari_tty"
