#!/bin/sh
#
# Copyright (C) 2004-2009 FAUmachine Team <info@faumachine.org>.
# This program is free software. You can redistribute it and/or modify it
# under the terms of the GNU General Public License, either version 2 of
# the License, or (at your option) any later version. See COPYING.
#

header() {
	echo "/*"
	echo " * WARNING:"
	echo " *"
	echo " * Automatically generated from ${input}."
	echo " */"
}

opt=${1}
shift
name=${1}
shift
input=${1}
shift

NAME=`echo ${name} | tr 'a-z' 'A-Z'`

gen_sig_name_conn_h() {
	header
	echo ""
	echo "#ifndef __SIG_${NAME}_CONN_H_INCLUDED"
	echo "#define __SIG_${NAME}_CONN_H_INCLUDED"
	echo ""
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| awk '{ print $1 }' \
	| sort -u \
	| while read type ; do
		echo '#include "'${type}.h'"'
	done

	echo ""

	# sig_name_conn
	echo "struct sig_${name}_conn {"
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo "	struct ${type} *${ident};"
	done
	echo "};"

	echo ""

	# sig_name_conn_merge
	echo "struct sig_${name}_conn_merge {"
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo "	struct ${type}_merge *${ident};"
	done
	echo "};"

	echo ""

	# merge
	echo "extern struct sig_${name}_conn_merge *"
	echo "sig_${name}_conn_merge(struct sig_${name}_conn *s0, struct sig_${name}_conn *s1);"
	# split
	echo "extern void"
	echo "sig_${name}_conn_split(struct sig_${name}_conn_merge *m);"
	# create
	echo "extern struct sig_${name}_conn *"
	echo "sig_${name}_conn_create(const char *name);"
	# destroy
	echo "extern void"
	echo "sig_${name}_conn_destroy(struct sig_${name}_conn *b);"
	# suspend
	echo "extern void"
	echo "sig_${name}_conn_suspend(struct sig_${name}_conn *b, FILE *fp);"
	# resume
	echo "extern void"
	echo "sig_${name}_conn_resume(struct sig_${name}_conn *b, FILE *fp);"
	echo ""
	echo "#endif /* __SIG_${NAME}_CONN_H_INCLUDED */"
}

gen_sig_name_conn_c() {
	header
	echo ""
	echo '#include "config.h"'
	echo ""
	echo '#include <assert.h>'
	echo '#include <stdio.h>'
	echo ""
	echo '#include "glue.h"'
	echo ""
	echo '#include "'sig_${name}_conn.h'"'

	echo ""

	# merge
	echo "struct sig_${name}_conn_merge *"
	echo "sig_${name}_conn_merge(struct sig_${name}_conn *s0, struct sig_${name}_conn *s1)"
	echo "{"
	echo "	struct sig_${name}_conn_merge *m;"
	echo ""
	echo "	m = shm_alloc(sizeof(*m));"
	echo "	assert(m);"
	echo ""
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo "	m->${ident} = ${type}_merge(s0->${ident}, s1->${ident});"
	done
	echo ""
	echo "	return m;"
	echo "}"

	echo ""

	# split
	echo "void"
	echo "sig_${name}_conn_split(struct sig_${name}_conn_merge *m)"
	echo "{"
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
	echo "	${type}_split(m->${ident});"
	done
	echo ""
	echo "	shm_free(m);"
	echo "}"

	echo ""

	# create
	echo "struct sig_${name}_conn *"
	echo "sig_${name}_conn_create(const char *name)"
	echo "{"
	echo "	struct sig_${name}_conn *b;"
	echo "	char n[1000];"
	echo ""
	echo "	b = shm_alloc(sizeof(*b));"
	echo "	assert(b);"
	echo ""
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo '	sprintf(n, "%s-'${ident}'", name);'
		echo "	b->${ident} = ${type}_create(n);"
	done
	echo ""
	echo "	return b;"
	echo "}"

	echo ""

	# destroy
	echo "void"
	echo "sig_${name}_conn_destroy(struct sig_${name}_conn *b)"
	echo "{"
	echo "	assert(b);"
	echo ""
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo "	${type}_destroy(b->${ident});"
	done
	echo ""
	echo "	shm_free(b);"
	echo "}"

	echo ""

	# suspend
	echo "void"
	echo "sig_${name}_conn_suspend(struct sig_${name}_conn *b, FILE *fp)"
	echo "{"
	echo "	generic_suspend(b, sizeof(*b), fp);"
	echo ""
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo "	${type}_suspend(b->${ident}, fp);"
	done
	echo "}"

	echo ""

	# resume
	echo "void"
	echo "sig_${name}_conn_resume(struct sig_${name}_conn *b, FILE *fp)"
	echo "{"
	echo "	generic_resume(b, sizeof(*b), fp);"
	echo ""
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo "	${type}_resume(b->${ident}, fp);"
	done
	echo "}"
}

gen_conn_name_c() {
	gender=$1

	header
	echo ""
	echo '#include <assert.h>'
	echo '#include <stdio.h>'
	echo '#include <string.h>'
	echo ""
	echo '#include "glue.h"'
	echo ""
	echo '#include "'conn_${name}_${gender}.h'"'
	echo ""

	# cpssp
	echo "struct cpssp {"
	echo "	char name[1024];"
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo "	struct ${type}_merge *forward_${ident};"
	done
	echo "};"

	if [ "${name}" = "cardbus" -a "${gender}" = "female" ] ; then
		echo ""

		# change callback
		echo "static void"
		echo "conn_${name}_change(void *_cpssp, const char *_name)"
		echo "{"
		echo "	struct cpssp *cpssp = _cpssp;"
		echo "	char name[1024];"
		echo ""
		echo "	/* Disconnect from old counterpart (if any). */"
		echo '	system_port_disconnect(cpssp->name, "dev");'
		echo ""
		echo "	if (*name == 0) {"
		echo "		return;"
		echo "	}"
		echo ""
		echo "	/* Connect to new counterpart (if any). */"
		echo '	strcpy(name, ":");'
		echo '	strcat(name, _name);'
		echo '	system_port_connect(cpssp->name, "dev", name, "dev");'
		echo "}"
	fi

	echo ""

	# create
	echo "void *"
	echo "conn_${name}_${gender}_create("
	echo "	const char *name,"
	echo "	struct sig_manage *port_manage,"
	if [ "${name}" = "cardbus" -a "${gender}" = "female" ] ; then
		echo "	struct sig_string *port_change,"
	fi
	echo "	struct sig_${name}_conn *port_dev,"
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| head -n -1 \
	| while read type ident ; do
		echo "	struct ${type} *port_${ident},"
	done
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| tail -n 1 \
	| while read type ident ; do
		echo "	struct ${type} *port_${ident}"
	done
	echo ")"
	echo "{"
	if [ "${name}" = "cardbus" -a "${gender}" = "female" ] ; then
		echo "	static const struct sig_string_funcs change_funcs = {"
		echo "		.set = conn_${name}_change,"
		echo "	};"
	fi
	echo "	struct cpssp *cpssp;"
	echo ""
	echo "	cpssp = shm_alloc(sizeof(*cpssp));"
	echo "	assert(cpssp);"
	echo ""
	echo "	system_name_push(name);"
	echo "	strcpy(cpssp->name, system_path());"
	echo ""
	cat ${input} \
	| grep -v '^#' \
	| grep -v '^$' \
	| while read type ident ; do
		echo "	cpssp->forward_${ident} = ${type}_merge(port_${ident}, port_dev->${ident});"
	done
	if [ "${name}" = "cardbus" -a "${gender}" = "female" ] ; then
		echo ""
		echo "	sig_string_connect(port_change, cpssp, &change_funcs);"
	fi
	echo ""
	echo "	system_name_pop();"
	echo ""
	echo "	return cpssp;"
	echo "}"

	echo ""

	# destroy
	echo "void"
	echo "conn_${name}_${gender}_destroy(void *_cpssp)"
	echo "{"
	echo "	struct cpssp *cpssp = _cpssp;"
	echo ""
	echo "	shm_free(cpssp);"
	echo "}"

	echo ""
	
	# suspend
	echo "void"
	echo "conn_${name}_${gender}_suspend(void *_cpssp, FILE *fp)"
	echo "{"
	echo "	struct cpssp *cpssp = _cpssp;"
	echo ""
	echo "	generic_suspend(cpssp, sizeof(*cpssp), fp);"
	echo "}"

	echo ""

	# resume
	echo "void"
	echo "conn_${name}_${gender}_resume(void *_cpssp, FILE *fp)"
	echo "{"
	echo "	struct cpssp *cpssp = _cpssp;"
	echo ""
	echo "	generic_resume(cpssp, sizeof(*cpssp), fp);"
	echo "}"
}

if [ "${opt}" = "-s" ] ; then
	gen_sig_name_conn_h > sig_${name}_conn.h
	gen_sig_name_conn_c > sig_${name}_conn.c

elif [ "${opt}" = "-c" ] ; then
	gen_conn_name_c female > conn_${name}_female.c
	gen_conn_name_c male > conn_${name}_male.c
fi
