#!/usr/bin/perl

use CGI;			# CGI
use CGI qw/:standard/;
use Getopt::Std;
use Data::Dumper;
use POSIX;

require "config.pl";

# getopts
&getopts("mtT:", \%options);
$target = $ARGV[0];
# $target = "../../types/robottype_eb2007.ortegen";
if (@ARGV < 1) {
	&print_usage();
	exit(1);
}
if (@ARGV > 1) {
	$force_roboorte_name = $ARGV[1];
}

&main();

#
# main()
#
sub main {
	my(@ortegen);
	my(@files);
	my($name);
	my(@libnames);

	if ( -d $target ) {
		@files = &get_files($target);
	} else {
		push(@files, $target);
		$one_file = 1;
	}

	foreach $ortegenfile (@files) {
		$ortegenfilename = $ortegenfile;
		$ortegenfilename =~ s#(.*[/]+)?([^/]+).ortegen#$2#gs;

		if (defined($force_roboorte_name) and $one_file) {
			$roboorte_name = $force_roboorte_name;
		} else {
			$roboorte_name = $ortegenfilename;
		}

		@ortegen = &parse_ortegen($ortegenfile);
# 		print Dumper(@ortegen);
		@ortegen = &fill_default_values(@ortegen);
# 		print Dumper(@ortegen);

		&generate_roboorte_header(@ortegen);

		&generate_roboorte_library(@ortegen);

		if ($options{"t"} or $options{"T"}) {
			&generate_test_publisher(@ortegen);
			&generate_test_subscriber(@ortegen);
		}

		push(@libnames, $roboorte_name);
		push(@ortegenfilenames, $ortegenfilename);
	}

	if ($options{"m"}) {
		&generate_makefile(@libnames);
	}

	return 0;
}

#
#
#
sub print_usage {

print << "(END OF PRINT USAGE)";
Robot\'s ORTE library and test programs generator.

Usage: $0 <ORTEGEN directory or file> [force_roboorte_name]

\t-m		create Makefile
\t-t		create test files
\t-T <dir>	test directory

Example:
1) generate libraries, test programs and makefiles for all ORTEGEN file in
the directory <some_dir>, place all test files to directory test/

\t $0 -T test -m <some_dir>

2) generate libraries, test programs and makefiles and use name <some_name>
for this programs

\t $0 -t -m ../../types/file.ortegen <some_name>

(END OF PRINT USAGE)

	return 0;
}

#
# Get ORTEGENs files list from a directory
#
# IN :
# OUT:
sub get_files {
	my($dir) = @_;
	
	my(@file_list) = glob($dir."/*.ortegen");
	
	return @file_list;
}

#
# parse ORTE ORTEGEN file and return a data structure containing ORTE topics and their parameters
#
# IN	file name
# OUT 	data structure
#
sub parse_ortegen {
	my ($ortegenfile) = @_;
	my ($content);
	my (@ortegen);

	# read 
        unless (open(INFILE, "$ortegenfile")) {
                print "Unable to open file $ortegenfile\n";
                exit(1);
	}
	while (<INFILE>) {
	    $line = $_;
	    # removing C/C++ style comments
	    $line =~ s#/\*[^*]*\*+([^/*][^*]*\*+)*/|//[^\n]*|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#$2#gs;
	    # removing shell comments
	    $line =~ s!#.*!!gs;

	    next if $line =~ /^[\s]*$/;

	    my (%ortevar);

	    @fields = qw/topic type strength deadline persistance minsep pubdelay/;
	    foreach $field(@fields) {
		# Read the field value
		($val) = $line =~ /\b$field=([\S]+)/;
		# Delete the field
		$line =~ s/$field=([\S]+)//g;
		
		$ortevar{$field} = $val;
	    }
	    if (!$ortevar{"type"}) { die("$ortegenfile:$.: type not defined\n"); }
	    if (!$ortevar{"topic"}) { die("$ortegenfile:$.: topic not defined\n"); }

	    $line =~ s/\s+/ /gs;
	    if ($line =~ /\S/) { die ("$ortegenfile:$.: unknown fields '$line'\nValid fields are: @fields\n"); }

	    push(@ortegen, \%ortevar);
	}

	close(INFILE);
	return @ortegen;
}

sub fill_default_values {
    my (@ortegen) = @_;
    
    foreach $var (@ortegen) {
	if (!$var->{"persistence"}) { $var->{"persistence"} = $publication_persistence; }
	if (!$var->{"strength"})    { $var->{"strength"} =    $publication_strength; }
	if (!$var->{"deadline"})    { $var->{"deadline"} =    $subscription_deadline; }
	if (!$var->{"minsep"}) 	   { $var->{"minsep"} = 	    $minimum_separation; }
	if (!$var->{"pubdelay"}) { $var->{"pubdelay"} = $publication_callback_delay; }
	if (!$var->{"deadline"}) { $var->{"deadline"} = $subscription_deadline; }
    }
    return @ortegen;
}

#
#
#
sub generate_roboorte_header {
	my(@ortegen) = @_;
	my($headerfile) = $library_name_prefix.$roboorte_name.".h";

	unless (open (OUTFILE, ">$headerfile")) {
		print "Unable to create file $headerfile\n";
		exit(1);
	}

print OUTFILE << "(END)";
/**
 * Automatically generated RoboORTE library.
 * \@warning DO NOT EDIT!!! Instead modify the *.ortegen and config file.
 */

#ifndef $library_name_prefix$roboorte_name\_H
#define $library_name_prefix$roboorte_name\_H

#include <stdio.h>

#include <orte.h>
#include <robottype.h>
#include <$ortegenfilename.h>
#include <roboorte.h>

#undef ROBOORTE_ROBOT_NAME
#define ROBOORTE_ROBOT_NAME	$roboorte_name

#ifndef ROBOORTE_ROBOT_NAME
#error No ROBOORTE_ROBOT_NAME defined!
#endif

/* ORTE data */
ROBOORTE_DATA {
\tORTEDomain *$orte_domain;
\tint strength;

\t/* orte data types */
(END)
		
	foreach $var (@ortegen) {
print OUTFILE << "(END)";
\tstruct $var->{"type"}_type $var->{"topic"};
(END)
	}

print OUTFILE << "(END)";

\t/* publishers */
(END)

	foreach $var (@ortegen) {
print OUTFILE << "(END)";
\tORTEPublication *$publication_prefix$var->{"topic"};
(END)
	}

print OUTFILE << "(END)";
};

#ifdef __cplusplus
extern "C" {
#endif 
ROBOORTE_INIT();
ROBOORTE_DESTROY();

(END)

	foreach $var (@ortegen) {
print OUTFILE << "(END)";
ROBOORTE_PUBLISHER_CREATE($var->{"topic"});
(END)
	}

print OUTFILE << "(END)";

(END)

	foreach $var (@ortegen) {
print OUTFILE << "(END)";
ROBOORTE_PUBLISHER_DESTROY($var->{"topic"});
(END)
	}

print OUTFILE << "(END)";

(END)

	foreach $var (@ortegen) {
print OUTFILE << "(END)";
ROBOORTE_SUBSCRIBER_CREATE($var->{"topic"});
(END)
	}

print OUTFILE << "(END)";
#ifdef __cplusplus
}
#endif 

#endif /* $library_name_prefix$roboorte_name\_H */
(END)

	close(OUTFILE);

	return 0;
}

#
#
#
sub generate_roboorte_library {
	my(@ortegen) = @_;
	my($libraryfile) = $library_name_prefix.$roboorte_name.".c";

	unless (open (OUTFILE, ">$libraryfile")) {
		print "Unable to create file $libraryfile\n";
		exit(1);
	}

print OUTFILE << "(END)";
/**
 * Automatically generated RoboORTE library.
 * \@warning DO NOT EDIT!!! Instead modify the *.ortegen and config file.
 */

#include "$library_name_prefix$roboorte_name.h"

/* ---------------------------------------------------------------------- 
 * CREATE PUBLISHERS
 * ---------------------------------------------------------------------- */

(END)

	foreach $var (@ortegen) {
	    ($pmsec, $psec) = POSIX::modf($var->{"persistence"});
	    $pmsec = POSIX::floor($pmsec*1000);
	    ($dmsec, $dsec) = POSIX::modf($var->{"pubdelay"});
	    $dmsec = POSIX::floor($dmsec*1000);
print OUTFILE << "(END)";
ROBOORTE_PUBLISHER_CREATE($var->{"topic"})
{
\tNtpTime persistence, delay;

\t$var->{"type"}_type_register($data_arg_name->$orte_domain);
\tNtpTimeAssembFromMs(persistence, $psec, $pmsec);
\tNtpTimeAssembFromMs(delay, $dsec, $dmsec); 
\t$data_arg_name->$publication_prefix$var->{"topic"} = ORTEPublicationCreate(
\t\t\t$data_arg_name->$orte_domain, "$var->{"topic"}", "$var->{"type"}",
\t\t\t&$data_arg_name->$var->{"topic"}, &persistence, $data_arg_name->strength, 
\t\t\t$callback_name, $callback_arg, &delay);
}

(END)
	}


print OUTFILE << "(END)";
/* ---------------------------------------------------------------------- 
 * DESTROY PUBLISHERS
 * ---------------------------------------------------------------------- */

(END)

	foreach $var (@ortegen) {
print OUTFILE << "(END)";
ROBOORTE_PUBLISHER_DESTROY($var->{"topic"})
{
\tORTEPublicationDestroy($data_arg_name->$publication_prefix$var->{"topic"});
}

(END)
	}

	
print OUTFILE << "(END)";
/* ---------------------------------------------------------------------- 
 * CREATE SUBSCRIBERS
 * ---------------------------------------------------------------------- */

(END)

	foreach $var (@ortegen) {
	    ($mmsec, $msec) = POSIX::modf($var->{"minsep"});
	    $mmsec = POSIX::floor($mmsec*1000);
	    ($dmsec, $dsec) = POSIX::modf($var->{"deadline"});
	    $dmsec = POSIX::floor($dmsec*1000);
print OUTFILE << "(END)";
ROBOORTE_SUBSCRIBER_CREATE($var->{"topic"})
{
\tORTESubscription *s;
\tNtpTime deadline, minimumSeparation;
\t
\t$var->{"type"}_type_register($data_arg_name->$orte_domain);
\t
\tNtpTimeAssembFromMs(deadline, $dsec, $dmsec);
\tNtpTimeAssembFromMs(minimumSeparation, $msec, $mmsec);
\ts = ORTESubscriptionCreate(
\t\t\t$data_arg_name->$orte_domain, $subscription_mode, $subscription_type, 
\t\t\t"$var->{"topic"}", "$var->{"type"}", 
\t\t\t&$data_arg_name->$var->{"topic"}, &deadline, &minimumSeparation,
\t\t\t$callback_name, $callback_arg, $multicast_ip_address);
}

(END)
	}


print OUTFILE << "(END)";

/* ---------------------------------------------------------------------- 
 * ORTE INITILIZATION AND DESTROY
 * ---------------------------------------------------------------------- */

ROBOORTE_INIT()
{
\tint rv = 0;
\t
\tORTEInit();

\tif ($data_arg_name->strength < 0)
\t\t$data_arg_name->strength = $publication_strength;

\tORTEVerbositySetOptions("ALL.0");
\t$data_arg_name->$orte_domain = ORTEDomainAppCreate(ORTE_DEFAULT_DOMAIN,
\t					NULL, NULL, ORTE_FALSE);
\tif (!$data_arg_name->$orte_domain) {
\t\tprintf("ORTEDomainAppCreate failed!\\n");
\t\trv = -1;
\t}
\t
\treturn rv;
}

ROBOORTE_DESTROY()
{
\treturn ORTEDomainAppDestroy($data_arg_name->$orte_domain);
}
(END)

	close(OUTFILE);

	return 0;
}

#
#
#
sub generate_makefile {
	my(@files) = @_;
	my($makefile) = "Makefile.omk";

	unless (open (OUTFILE, ">$makefile")) {
		print "Unable to create $makefile\n";
		exit(1);
	}

	if ($testdir = $options{"T"}) {
		$testmakefile = "$testdir/$makefile";
		unless (open (MKOUTFILE, ">$testmakefile")) {
			print "Unable to create $testmakefile\n";
			exit(1);
		}
print MKOUTFILE << "(END)";
# 
# Automatically generated Makefile file.
# \@warning DO NOT EDIT!!!
# 
# -*- makefile -*-
#  Robot's ORTE library

(END)
	} else {
		*MKOUTFILE = *OUTFILE;
print OUTFILE << "(END)";
# Automatically generated Makefile file.
# \@warning DO NOT EDIT!!!
# 
# -*- makefile -*-
#  Robot's ORTE library

(END)
	}

	my($i) = 0;
	foreach $filename (@files) {
		$testfilename1 = $test_publisher_name_prefix.$filename;
		$testfilename2 = $test_subscriber_name_prefix.$filename;
		$libraryname = $library_name_prefix.$filename;

print OUTFILE << "(END)";
lib_LIBRARIES += $libraryname
$libraryname\_SOURCES = $libraryname.c
include_HEADERS += $libraryname.h

(END)

		if ($options{"t"} or $options{"T"}) {
print MKOUTFILE << "(END)";
test_PROGRAMS += $testfilename1
$testfilename1\_SOURCES = $testfilename1.c
$testfilename1\_LIBS = pthread $libraryname robottype $ortegenfilenames[$i] orte

test_PROGRAMS += $testfilename2
$testfilename2\_SOURCES = $testfilename2.c
$testfilename2\_LIBS = pthread $libraryname robottype $ortegenfilenames[$i] orte

(END)
		}
		$i++;
	}

	if ($options{"T"}) {
print OUTFILE << "(END)";
SUBDIRS = test

clean-custom:
\t\$(Q)rm -rf *.c *.h Makefile.omk test/
(END)
		`cp -p Makefile $testdir`
	} else {
print OUTFILE << "(END)";
clean-custom:
\t\$(Q)rm -rf *.c *.h Makefile.omk
(END)
	}

	close(OUTFILE);
	if ($options{"T"}) {
		close(MKOUTFILE);
	}

	return 0;
}

#
#
#
sub generate_test_publisher {
	my(@ortegen) = @_;
	my($testfile) = $test_publisher_name_prefix.$roboorte_name.".c";
	my($testdir);

	if ($testdir = $options{"T"}) {
		$testfile = "$testdir/$testfile";
		`mkdir -p $testdir`;
	}

	unless (open (OUTFILE, ">$testfile")) {
		print "Unable to create file $testfile\n";
		exit(1);
	}

print OUTFILE << "(END)";
/**
 * Automatically generated test file for RoboORTE libraries.
 * \@warning DO NOT EDIT!!! Instead modify the *.ortegen and config file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <orte.h>
#include <$library_name_prefix$roboorte_name.h>

/* ---------------------------------------------------------------------- 
 * PUBLISHER CALLBACKS
 * ---------------------------------------------------------------------- */

(END)

	foreach $var (@ortegen) {
print OUTFILE << "(END)";
void send_$var->{"topic"}_cb(const ORTESendInfo *info, void *vinstance, 
\t\t\tvoid *sendCallBackParam)
{
}

(END)
	}

print OUTFILE << "(END)";

/* Thread process. Do the job. */
void *process(void *arg)
{
\tstruct $roboorte_name\_orte_data *orte = (struct $roboorte_name\_orte_data*)arg;
\tint i = 0;

\twhile (1) {
\t\tswitch(i) {
(END)

	my($i) = 0;
	foreach $var (@ortegen) {
		my(@var) = (@{$var->{"var"}});
print OUTFILE << "(END)";
\t\t\t/* $var->{"topic"} */
\t\t\tcase $i:
\t\t\t\tprintf("$var->{"topic"}:\\n");
(END)
		foreach $var (@var) {
			if ($var->{"type"} eq "double") {
print OUTFILE << "(END)";
\t\t\t\torte->$var->{"topic"}.$var->{"topic"} = ($var->{"type"})(rand() % 255);
\t\t\t\tprintf("\\t$var->{"topic"} = %f\\n", orte->$var->{"topic"}.$var->{"topic"});
(END)
			} elsif ( $var->{"type"} eq "octet" ) {
print OUTFILE << "(END)";
\t\t\t\torte->$var->{"topic"}.$var->{"topic"} = (unsigned char)(rand() % 255);
\t\t\t\tprintf("\\t$var->{"topic"} = 0x%02x\\n", orte->$var->{"topic"}.$var->{"topic"});
(END)
			} elsif ( $var->{"type"} eq "unsigned short" ) {
print OUTFILE << "(END)";
\t\t\t\torte->$var->{"topic"}.$var->{"topic"} = ($var->{"type"})(rand() % 255);
\t\t\t\tprintf("\\t$var->{"topic"} = %d\\n", orte->$var->{"topic"}.$var->{"topic"});
(END)
			} else {
print OUTFILE << "(END)";
\t\t\t\torte->$var->{"topic"}.$var->{"topic"} = (int)(rand() % 255);
\t\t\t\tprintf("\\t$var->{"topic"} = %d\\n", orte->$var->{"topic"}.$var->{"topic"});
(END)
			}
		}

		if (defined($publication_callback_enabled)) {
print OUTFILE << "(END)";
\t\t\t\t/*ORTEPublicationSend(orte->$publication_prefix$var->{"topic"});*/
\t\t\t\tbreak;

(END)
		} else {
print OUTFILE << "(END)";
\t\t\t\tORTEPublicationSend(orte->$publication_prefix$var->{"topic"});
\t\t\t\tbreak;

(END)
		}

		$i++;
	}

print OUTFILE << "(END)";
\t\t\tdefault:
\t\t\t\tbreak;
\t\t}
\t\ti++;
\t\ti = i % $i;
\t\tusleep($publisher_thread_usleep);
\t}
}

/* Start thread */
void start_thread(struct $roboorte_name\_orte_data *orte)
{
\tint retcode;
\tpthread_t th;
\tvoid * retval;

\tretcode = pthread_create(&th, NULL, process, orte);
\tif (retcode != 0) 
\t\tprintf("Failed to create thread %d\\n", retcode);

\tretcode = pthread_join(th, &retval);
\tif (retcode != 0) 
\t\tprintf("Failed to join thread %d\\n", retcode);
}

struct $roboorte_name\_orte_data orte;

/* SIGINT handler */
static void int_handler(int sig)
{
\t$roboorte_name\_roboorte_destroy(&orte);
\texit(0);
}

int main()
{
\tsrand(time(0));

\tsignal(SIGINT, int_handler);

\torte.strength = $publication_strength;

\t/* orte initialization */
\t$roboorte_name\_roboorte_init(&orte);

\t/* creating publishers */
(END)

	foreach $var (@ortegen) {
		if (defined($publication_callback_enabled)) {
			$publication_callback = "send_".$var->{"topic"}."_cb";
		} else {
			$publication_callback = "NULL";
		}

print OUTFILE << "(END)";
\t$roboorte_name\_publisher_$var->{"topic"}_create(&orte, $publication_callback, &orte);
(END)
	}

print OUTFILE << "(END)";

\tprintf("Publishers: sending data.\\n");

\t/* start thread and do the job */
\tstart_thread(&orte);

\t/* orte domain destroy */
\t$roboorte_name\_roboorte_destroy(&orte);

\treturn 0;
}

(END)

	close(OUTFILE);

	return 0;
}

#
#
#
sub generate_test_subscriber {
	my(@ortegen) = @_;
	my($testfile) = $test_subscriber_name_prefix.$roboorte_name.".c";

	if ($testdir = $options{"T"}) {
		$testfile = "$testdir/$testfile";
		`mkdir -p $testdir`;
	}

	unless (open (OUTFILE, ">$testfile")) {
		print "Unable to create file $testfile\n";
		exit(1);
	}

print OUTFILE << "(END)";
/**
 * Automatically generated test file for RoboORTE libraries.
 * \@warning DO NOT EDIT!!! Instead modify the *.ortegen and config file.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <orte.h>
#include <$library_name_prefix$roboorte_name.h>


void print_time()
{
\tstruct timeval tv;
\ttime_t curtime;
\tchar buffer[30];

\tgettimeofday(&tv, NULL);
\tcurtime = tv.tv_sec;
\tstrftime(buffer, 30, "[%T.", localtime(&curtime));
\tprintf("%s%ld]\\n", buffer, tv.tv_usec);
}

/* ---------------------------------------------------------------------- 
 * SUBSCRIBER CALLBACKS
 * ---------------------------------------------------------------------- */

(END)

	foreach $var (@ortegen) {
print OUTFILE << "(END)";
void rcv_$var->{"topic"}_cb(const ORTERecvInfo *info, void *vinstance,
\t\t\tvoid *recvCallBackParam)
{
\tstruct $roboorte_name\_orte_data *orte = 
\t\t\t(struct $roboorte_name\_orte_data *)recvCallBackParam;
\t
\tswitch (info->status) {
\t\tcase NEW_DATA:
(END)
		if (defined($subscriber_print_data)) {
print OUTFILE << "(END)";
\t\t\tprint_time();
\t\t\tprintf("\\t$var->{"topic"}:\\n");
(END)
			my(@var) = (@{$var->{"var"}});
			foreach $var (@var) {
print OUTFILE << "(END)";
\t\t\t\tprintf("\\t\\t");
(END)
				if ($var->{"type"} eq "double") {
print OUTFILE << "(END)";
\t\t\t\tprintf("$var->{"topic"} = %f\\n", orte->$var->{"topic"}.$var->{"topic"});
(END)
				} elsif ( $var->{"type"} eq "octet" ) {
print OUTFILE << "(END)";
\t\t\t\tprintf("$var->{"topic"} = 0x%02x\\n", orte->$var->{"topic"}.$var->{"topic"});
(END)
				} else {
print OUTFILE << "(END)";
\t\t\t\tprintf("$var->{"topic"} = %d\\n", orte->$var->{"topic"}.$var->{"topic"});
(END)
				}
			}
		}
                        
print OUTFILE << "(END)";
\t\t\tbreak;
\t\tcase DEADLINE:
\t\t\tprintf("ORTE deadline occurred - $var->{"topic"} receive\\n");
\t\t\tbreak;
\t}
}

(END)
	}

print OUTFILE << "(END)";
struct $roboorte_name\_orte_data orte;

/* SIGINT handler */
static void int_handler(int sig)
{
\t$roboorte_name\_roboorte_destroy(&orte);
\texit(0);
}

int main()
{
\tsignal(SIGINT, int_handler);

\t/* orte initialization */
\t$roboorte_name\_roboorte_init(&orte);

\t/* creating subscribers */
(END)

	foreach $var (@ortegen) {
print OUTFILE << "(END)";
\t$roboorte_name\_subscriber_$var->{"topic"}_create(&orte, rcv_$var->{"topic"}_cb, &orte);
(END)
	}

	unless (defined($subscriber_print_data)) {
print OUTFILE << "(END)";
\tprintf("Subscribers: debug print turned off.\\n");
(END)
	}
print OUTFILE << "(END)";

\tprintf("Subscribers: waiting for data.\\n");
\twhile (1) {
\t\tusleep($subscriber_wait_usleep);
\t}

\t/* orte domain destroy */
\t$roboorte_name\_roboorte_destroy(&orte);

\treturn 0;
}

(END)

	close(OUTFILE);

	return 0;
}

1
