#!/opt/local/bin/perl

# This file is part of the nesC compiler.
#    Copyright (C) 2002 Intel Corporation
# 
# The attached "nesC" software is provided to you under the terms and
# conditions of the GNU General Public License Version 2 as published by the
# Free Software Foundation.
# 
# nesC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with nesC; see the file COPYING.  If not, write to
# the Free Software Foundation, 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

$prefix = "/opt/local";
$exec_prefix = "${prefix}";
$nescc = "$exec_prefix/bin/nescc";
$NCDIR = "${exec_prefix}/lib/ncc";

push @INC, "$NCDIR";

for ($i = 0; $i <= $#ARGV; $i++) {
    $_ = $ARGV[$i];

    if (/^-/) {
	if (/^-java-classname=(.*)$/) {
	    $java_classname = $1;
	}
	elsif (/^-java-extends=(.*)$/) {
	    $java_extends = $1;
	}
	elsif (/^-csharp-classname=(.*)$/) {
	    $csharp_classname = $1;
	}
	elsif (/^-csharp-extends=(.*)$/) {
	    $csharp_extends = $1;
	}
	elsif (/^-python-classname=(.*)$/) {
	    $python_classname = $1;
	}
	elsif (/^-python-extends=(.*)$/) {
	    $python_extends = $1;
	}
	elsif (/^-c-prefix=(.*)$/) {
	    $c_prefix = $1;
	}
	elsif (/^-o/) {
	    ($i, $ofile) = &extractarg($i);
	}
	elsif (/^-nescc=(.*)$/) {
	    $nescc = $1;
	}
	# pass ncc options on through
	# (yes, -o was captured above. This is "generic" code)
	elsif (/^-[oILxubV]/) {
	    # pass option and arg through unchanged
	    $opt = substr $_, 1, 1;
	    ($i, $arg) = &extractarg($i);
	    push @args, "-$opt$arg";
	}
	elsif ($i < $#ARGV &&
	       (/^-idirafter$/ || /^-include$/ || /^-imacros$/ ||
		/^-iprefix$/ || /^-iwithprefix$/ || /^-iwithprefixbefore$/ ||
		/^-isystem$/ || /^-Xlinker$/)) {
	    # convert argument filename which is in next arg
	    push @args, $_;
	    push @args, $ARGV[++$i];
	}
	else {
	    push @args, $_;
	    $verbose = 1 if /^-v$/;
	}
    }
    else {
	&usage(0) if defined($message_type);
	$message_type = $_ if defined($cfile) && !defined($message_type);
	$cfile = $_ if defined($target) && !defined($cfile);
	$target = $_ if !defined($target);
    }
}
&usage("no target specified") if !defined($target);
&usage("no message format file specified") if !defined($cfile);
&usage("no message specified") if !defined($message_type);

unshift @args, "-fsyntax-only";
unshift @args, "$nescc";
push @args, "-fnesc-msg=$message_type";
push @args, "-x";
push @args, "nesc";
push @args, $cfile;

print STDERR join(" ", @args), "\n" if $verbose;
open(NESC, '-|', @args) or die "ncc not found";
@spec = <NESC>;
close NESC;

if ($?) {
    print STDERR "failed to parse message file $cfile\n";
    exit 1;
}

if (!-f "$NCDIR/gen$target.pm") {
    print STDERR "Unknown tool $target\n";
    exit 2;
}

if ($ofile) {
    close STDOUT;
    if (!open STDOUT, ">$ofile") {
	print STDERR "failed to create $ofile\n";
	exit 1;
    }
}

require "gen$target.pm";
&gen($classname, @spec);
$completed = 1;

sub usage()
{
    my ($error) = @_;

    print STDERR "$error\n\n" if $error;

    print STDERR "Usage: $0 [options] tool msg-format-file message-type\n";
    print STDERR "  general options are\n";
    print STDERR "    -o <file>                         Specify output file\n";
    print STDERR "                                      (default is stdout)\n";
    print STDERR "    and any nescc option.\n\n";
    print STDERR "  target specific options are\n";
    print STDERR "    java:\n";
    print STDERR "      -java-classname=FULL-CLASS-NAME Select class name (required)\n";
    print STDERR "      -java-extends=CLASS-NAME        Select class to extend\n";
    print STDERR "    csharp:\n";
    print STDERR "      -csharp-classname=FULL-CLASS-NAME Select class name (required)\n";
    print STDERR "      -csharp-extends=CLASS-NAME        Select class to extend\n";
    print STDERR "                                      (default tinyos.message.Message)\n";
    print STDERR "    python:\n";
    print STDERR "      -python-classname=FULL-CLASS-NAME Select class name (required)\n";
    print STDERR "      -python-extends=CLASS-NAME        Select class to extend\n";
    print STDERR "                                      (default tinyos.message.Message)\n";
    print STDERR "    C:\n";
    print STDERR "      -c-prefix=PREFIX                  Select a different prefix than the\n";
    print STDERR "                                        message-type.\n";
    print STDERR "      You must use -o <somefile>.h when using the C tool. The C tool generates\n";
    print STDERR "      both <somefile>.h and <somefile>.c\n";
    exit 2;
}

sub extractarg {
    local ($i) = @_;

    if (length($ARGV[$i]) == 2) {
	$arg = $ARGV[++$i];
    }
    else {
	$arg = substr($ARGV[$i], 2);
    }
    return ($i, $arg);
}

sub poparray()
{
    my ($n) = @_;

    while ($n-- > 0) {
	pop @array_max;
	pop @array_bitsize;
	pop @array_offset;
    }
}

sub basetype()
{
    my ($type) = @_;
    my $basetype;

    $type =~ /([A-Za-z0-9_]+)$/ or die;
    $basetype = $1;

    return "PUSH" if $basetype =~ /^AN?[SU]$/;
    return "POP" if $basetype eq "AX";
    return "F" if $basetype =~ /^Nnx_float/;
    return "U" if $basetype =~ /^Nnxle_u/;
    return "I" if $basetype =~ /^Nnxle_/;
    return "BU" if $basetype =~ /^Nnx_u/;
    return "BI" if $basetype =~ /^Nnx_/;

    return $basetype;
}

sub arraymax()
{
    my ($type, $bsize) = @_;
    my @max;

    while ($type =~ /^\[([0-9]+)\](.*)/) {
	push @max, $1;
	$type = $2;
    }
    return @max;
}

sub arraybitsize()
{
    my ($bsize, @max) = @_;
    my @bitsize;

    for ($i = $#max; $i >= 0; $i--) {
	$bitsize[$i] = $bsize;
	$bsize *= $max[$i];
    }

    return @bitsize;
}

sub END() {
    unlink $ofile unless !$ofile || $completed;
}
