#!/bin/sh

PROTO_MAGIC="HAL_PROTO"
STATUS_OK="OK"
MAJOR_VERS="0"
MINOR_VERS="1"

CUR_STATE="MAGIC"
CUR_COMMAND=""
PLEN=""
NARG="0"

#set -x

# Open /tmp/write for OUTPUT as fd=4
#exec 4>/tmp/read

# $1 is the length of payload $2 is the payload
send_packet()
{
	local HEADER=""

	HEADER="$(printf "%s" $PROTO_MAGIC)\n"
	HEADER=$HEADER"$(printf "%X" $1)\n"
	HEADER=$HEADER"\n"
	
	# Between the header and the payload we have extra '\n'
	# so "\n\n" is a signature of the header end
	#cat <<- EOF_COMMAND >&4 
	cat <<- EOF_COMMAND > /tmp/read
	$(echo -n -e "$HEADER""$2")
	EOF_COMMAND
}

# $1 is command type: WE, RE, WP, RP
common_operation()
{
	local PAYLOAD=""
	local STR_LEN=""

	PAYLOAD="$1\nOK\n"
	STR_LEN=$(expr length $PAYLOAD)
	# A small hack when we calculate the length of the string we count
	# '\n' as 2 symbols now substract 1 one for each '\n'
	STR_LEN=$(expr $STR_LEN - 2)

	send_packet $STR_LEN $PAYLOAD

	echo "--> $1: send a command"
}

# Functions below manage the other side program. We send commands and
# wait for reponses.
write_outputs()
{	
	local PAYLOAD=""
	local STR_LEN=""
	# In our test we want writing only a memory statistic (used memory) it
	# occupies 4 bytes.
	local LEN="4"
	local OFFS="4"
	# Fetch and format sending data.
	local MEM_STAT=$(free | tail -n +2 | head -n 1 | awk '{ print $3 }')
	# Form the message payload
	PAYLOAD="WO\nOK\n"
	PAYLOAD=$PAYLOAD"$(printf "%X" $LEN)\n"
	PAYLOAD=$PAYLOAD"$(printf "%X" $OFFS)\n"
	PAYLOAD=$PAYLOAD"$(printf "%08X" $MEM_STAT)\n"

	STR_LEN=$(expr length $PAYLOAD)
	STR_LEN=$(expr $STR_LEN - 5)

	send_packet $STR_LEN $PAYLOAD

	echo "--> WO: send a command"
	echo "--> WO: write $LEN bytes at $OFFS offset, value $(printf "%X" $MEM_STAT)"
}

write_inputs()
{	
	local PAYLOAD=""
	local STR_LEN=""
	# In our test we want writing only a memory statistic (used memory) it
	# occupies 4 bytes.
	local LEN="4"
	local OFFS="4"
	# Fetch and format sending data.
	local MEM_STAT=$(free | tail -n +2 | head -n 1 | awk '{ print $3 }')
	# Form the message payload
	PAYLOAD="WI\nOK\n"
	PAYLOAD=$PAYLOAD"$(printf "%X" $LEN)\n"
	PAYLOAD=$PAYLOAD"$(printf "%X" $OFFS)\n"
	PAYLOAD=$PAYLOAD"$(printf "%08X" $MEM_STAT)\n"

	STR_LEN=$(expr length $PAYLOAD)
	STR_LEN=$(expr $STR_LEN - 5)
	
	send_packet $STR_LEN $PAYLOAD

	echo "--> WI: send a command"
	echo "--> WI: write $LEN bytes at $OFFS offset, value $(printf "%X" $MEM_STAT)"
}

# $1 is command type RI or RO
read_image()
{
	local PAYLOAD=""
	local STR_LEN=""
	# We want get back what we have written
	local LEN="4"
	local OFFS="4"

	PAYLOAD="$1\nOK\n"
	PAYLOAD=$PAYLOAD"$(printf "%X" $LEN)\n"
	PAYLOAD=$PAYLOAD"$(printf "%X" $OFFS)\n"

	STR_LEN=$(expr length $PAYLOAD)
	STR_LEN=$(expr $STR_LEN - 4)

	send_packet $STR_LEN $PAYLOAD	

	echo "--> $1: send a command"
	echo "--> $1: read $LEN bytes at $OFFS offset"
}

# Set important variables to the innocent states that we can accept a new
# packet and correctly parse it
reset_variables()
{
	CUR_STATE="MAGIC"
	CUR_COMMAND=""
	NARG="0"
}

connect()
{
	local PAYLOAD=""
	local STR_LEN=""

	PAYLOAD="CN\nOK\n"
	STR_LEN=$(expr length $PAYLOAD)
	STR_LEN=$(expr $STR_LEN - 2)
	
	send_packet $STR_LEN $PAYLOAD

	echo "--> CN: send a command"	
}

# When we test the server API, the loop only checks the answer for our
# commands. Bellow we send all commands one by one. The loop will parse
# answers.

connect
common_operation "WP"
write_inputs
common_operation "WE"
common_operation "RP"
read_image "RI"
common_operation "RE"

#connect
#common_operation "RP"
#read_image "RI"
#read_image "RO"
#common_operation "RE"

while read STR
do

	case "$CUR_STATE" in
	MAGIC)
		if [ "$STR" != "$PROTO_MAGIC" ] ; then
			CUR_STATE="ERROR"
			ERRSTRING="magic not found"
		else
			CUR_STATE="PACKET_LEN"
		fi
		;;
	PACKET_LEN)
		# In this implementation we don't care about a payload length,
		# just save it and come to the next step
		PLEN="0x$STR"
		echo "--> PAYLOAD LENGTH: $PLEN"
		CUR_STATE="COMMAND"
		;;
	COMMAND)
		case "$CUR_COMMAND" in
		CN)
			# Count arguments and handle according its number
			# There are 3 arguments for CONNECT:
			# 1) command status;
			# 2) major protocol version;
			# 3) minor protocol version;
			# 4) inputs size;
			# 5) outputs size;
			# 6) protocol role (master or slave).

			NARG=$(expr $NARG + 1)

			case "$NARG" in
			1)
				# Check the status just in case
				if [ "$STR" != "$STATUS_OK" ] ; then
					CUR_STATE="ERROR_MSG"
				else
					echo "<-- $CUR_COMMAND: status is OK"
				fi
			;;
			2)
				if [ "$STR" -ne $MAJOR_VERS ] ; then
					CUR_STATE="ERROR"
					ERRSTRING="major version is wrong"
				else
					echo "<-- $CUR_COMMAND: major version is OK"
				fi
			;;
			3)
				if [ $STR -ne $MINOR_VERS ] ; then
					CUR_STATE="ERROR"
					ERRSTRING="minor version is wrong"
				else
					echo "<-- $CUR_COMMAND: minor version is OK"
				fi
			;;
			4)
				echo "<-- $CUR_COMMAND: inputs size $STR"
			;;
			5)
				echo "<-- $CUR_COMMAND: outputs size $STR"
			;;
			6)
				echo "<-- $CUR_COMMAND: protocol role is $STR"
				# Set our state back to MAGIC to be
				# ready for a new packet
				reset_variables
			;;
			esac
		;;
	# Below there are only responses for our managing action.
		WO|WI)
			# There are 3 arguments for this command:
			# 1) command status;
			# 2) length of message that was written to the outputs
			# or inputs image;
			# 3) offset in the outputs or inputs image.
			NARG=$(expr $NARG + 1)

			case "$NARG" in
			1)
				if [ "$STR" != "$STATUS_OK" ] ; then
					CUR_STATE="ERROR_MSG"
				else
					echo "<-- $CUR_COMMAND: status is OK"
					reset_variables
				fi	
			;;
			esac
		;;
		RO|RI)
			# There are 4 arguments for this command:
			# 1) command status;
			# 2) length of message that was written to the outputs
			# image;
			# 3) offset in the outputs image;
			# 4) data bytes from ouputs image. 
			
			NARG=$(expr $NARG + 1)
			
			case "$NARG" in
			1)
				if [ "$STR" != "$STATUS_OK" ] ; then
					CUR_STATE="ERROR_MSG"
				else
					echo "<-- $CUR_COMMAND: status is OK"
				fi
			;;
			2)
				# Get the length
				echo "<-- $CUR_COMMAND: read message length is $STR"
			;;
			3)
				# Get offset
				echo "<-- $CUR_COMMAND: read message offset is $STR"
			;;
			4)
				# Get data
				echo "<-- $CUR_COMMAND: read message data is $STR"
				reset_variables
			;;
			esac
		;;
		WP|RP|WE|RE)
			# There is only one argument:
			# 1) command status.
    
			NARG=$(expr $NARG + 1)

			case "$NARG" in
			1)  
				if [ "$STR" != "$STATUS_OK" ] ; then
					CUR_STATE="ERROR_MSG"
				else
					echo "<-- $CUR_COMMAND: status is OK"
					reset_variables
				fi  
			;;  
			esac
		;;
		*)
			CUR_COMMAND=$STR
			echo "<-- $CUR_COMMAND:"
		;;
		esac	
		;;
	ERROR_MSG)
		CUR_STATE="ERROR"
		ERRSTRING=$STR
		;;
	ERROR)
		echo "error: $CUR_COMMAND $ERRSTRING"
		exit 1
		;;
	esac

done < /tmp/write

#exec 4<&-

exit 0

