#!/usr/local/bin/perl5
# paramGen: Ballista script to generate .param files from .tpl files
# Copyright (C) 1998-2001  Carnegie Mellon University
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

########################
# Usage:        paramGen <.tpl filename> ...
#
########################
#
# Entry Conditions:
#               .param files are already generated for each parent of
#                the .tpl files that appear on the command line
#
########################

# must use 5.004 so that m//gc does not reset pos()
use 5.004;
use strict;

# complain if no arguments are given
unless (@ARGV) {
    die "usage: paramGen <.tpl filename> ...\n";
}

# process each .tpl file on the command line
foreach my $infile (@ARGV) {

    my $outfile;          # output filename
    my $datatype;         # name of the datatype being processed
    my $input;            # contents of the input file
    my $parent;           # parent of the datatype
    my $dials_section;    # the dials section from the template
    my @dials;            # the individual dials and their values (see below)
    my @output;           # lines to be output
    my $dial_count;       # number of dials of the datatype

    # The structure of @dials:
    #  For the dials section
    #   enum_dial DIAL_ONE : FOO, BAR, BAZ;
    #   int_dial  DIAL_TWO;
    #   enum_dial DIAL_THREE : ZAPF, DING, DONG;
    #   
    #  The @dials array would be structured as:
    #   @dials = [
    #              ['FOO', 'BAR', 'BAZ],
    #              ['NODIAL_TWO'],
    #              ['ZAPF', 'DING', 'DONG']
    #            ]

    # open the .tpl file for input
    open(IN, "<$infile") || die "Can't open $infile: $!";

    # open the .param file for output
    $outfile = $infile;
    $outfile =~ s/\.tpl/.param/ || die "Bad input filename $infile\n";
    open(OUT, ">$outfile") || die "Can't open $outfile: $!";

    # determine the name of the datatype
    $datatype = $infile;
    $datatype =~ s|.*/||;
    $datatype =~ s/\.tpl$//;

    # read the entire file into $input
    undef $/;
    $input = <IN>;
    $/ = 1;

    # find the parent line
    if ($input =~ m/^parent (\w+)/ms) {
	$parent = $1;
    } else {
	die "File $infile does not contain a parent line";
    }
    

    # copy the parent .param file into the beginning of this .param
    #  file by matching everything before and after the current
    #  datatype name
    $outfile =~ m/(.*)$datatype(.*)/;
    $parent = "$1$parent$2";
    open(PARENT, "<$parent") || die "Can't open $parent for $infile: $!";
    print OUT <PARENT>;
    close PARENT;
    

    # find the dials section for further processing
    ($dials_section) = $input =~ m/^dials\s\[(.*?)\]/sm;
    unless (length $dials_section) {
	die "File $infile does not contain a dials section";
    }

    # find each individual dial
    while ($dials_section =~ m/^\s*(\w+)_dial\s*(\w+)/smg) {
	
	my $dial_type = $1;
	my $dial_name = $2;

	# complain about any type of dial we can't handle
	if ($dial_type !~ m/^(enum|int)$/) {
	    die "Unknown dial type in $infile: $dial_type\n";
	}

	# find the settings for the current dial
	my ($settings) = $dials_section =~ m/\G\s*:(.*?);/sgc;
	
	# get rid of any optional whitespace seperating the settings
	$settings =~ s/\s//gs;

	# smash case on the setting name, to match the convention
	#  used by the datatype compiler
	$settings = uc $settings;

	# an int_dial does not have any settings, so we force a single
	#  setting into its list, so that we have something to enumerate
	#  over later
	if ($dial_type eq 'int') {
	    $settings = "NO${dial_name}";
	}

	unless (length $settings) {
	    die "Dial $dial_name does not contain any settings in $infile";
	}
	
	# push the settings onto the dials list
	push @dials, [split m/,/, $settings];
	
	# prepend the datatype name to the name of each setting
	foreach (@{$dials[-1]}) {
	    $_ = "${datatype}_$_";
	}

	# an int_dial needs INTDIAL_ prepended, for sanity's sake
	if ($dial_type eq 'int') {
	    $dials[-1]->[0] = "INTDIAL_" . $dials[-1]->[0];
	}
    }

    # the output format should be:
    #     <datatype>  <num>  <setting> ...
    #  where num is the number of settings
    #
    # we take the *first* set of settings, and prepend the datatype name
    #  and setting count, then take successive cross products to build up
    #  the total dials settings
    
    $dial_count = @dials;
    
    @output = @{shift @dials} if @dials;

    foreach (@output) {
	$_ = join("\t", $datatype, $dial_count, $_);
    }

    while (@dials) {
	@output = crossProduct(shift @dials, \@output);
    }
    
    # now that the cross product is completed, simply dump it all to output
    foreach (@output) {
	print OUT "$_\t\n";
    }
    
}

# calculate the cross product of two arrays
sub crossProduct {
    my $a1 = shift;
    my $a2 = shift;

    my @b;

    foreach my $i1 (@$a1) {
	foreach my $i2 (@$a2) {
	    push @b, "$i2\t$i1";
	}
    }

    return @b;
}
