#!/usr/bin/perl -W


# number of elements in each row/column is read from tables.txt table data
# only njkrl tables are allowed to have rows bigger than 8 elements
# make sure the table size in tables.txt matches your contoller table size!


if (@ARGV != 2) {
    print "usage:\n";
    print "\t make_table.pl in-data out-commands\n\n";
    exit;
}

open(GLOBAL, "<$ARGV[0]") or die "can't open $ARGV[0]";
open(OF, ">$ARGV[1]");

print OF "Mantt\n";

$numberformat=16;	# hex by default
while($l = <GLOBAL>) {
    chomp $l;

	if ($l =~ /^\s*($|\/\/|\#|;)/) {  # skip lines consisting of comments only
		next;
	}
    
	# numberformat=10 for decimal
	if ($l =~ /^\s*numberformat\s*\=\s*([\w\d]+)/) {
		$numberformat = $1;
		next;
    	}

	maybe_table_line( $l );
}

close GLOBAL;

print OF "mcs\n";  # config save everything
print OF "bye\n";  # goodbye
close OF;


# split as a preparation: config and table processing will be unified
# returns undef if it does not look like a table line
# returns 1 if it looks like a table line (even if warning was printed due to unknown table)
sub maybe_table_line
{
	my $l = shift;
	if ($l !~ /^\s*(\w)\[(\w)\]=([^\#]+)/) {  # search for j[0]=
	    	# not our business
	    	return undef;
	}

	@values = split /\s+/, $3;
	$table_width=@values;
	$array_name = $1;
	$index = $2;

	if( $array_name !~ /[njkrlhtby]/) {
	    print STDERR "unrecognised array \"$array_name\", ignoring\n";
	    # it looked like a table, but unknown name:
	    # we don't want the config processing to chew on it:
	    return 1;
	}

	check_table_width( $array_name, $table_width);

	my @hexvals = numbervalue($array_name, \@values);
	for ($i=0; $i<$table_width; $i++) {
		printf(OF "g%02X%s%02X\n", 16 * hex($index) + $i, $array_name, $hexvals[$i]);
    	}

	return 1;
}

# basic check of table width
# TODO: check jnlb against the known size of r
# unfortunately this makes little sense, since we require users to 
# fill all 8 or 16 r (and k) values to allow firmware do binary search
# even if they only fill 4..5 values in l and ve
sub check_table_width
{
	my ($array_name, $table_width) = @_ ;
	my $minlengths={ j=>5,n=>5,k=>8,r=>8,l=>5,h=>8,t=>8,b=>8,y=>8 };
	my $maxlengths={ j=>16,n=>16,k=>15,r=>16,l=>16,h=>8,t=>8,b=>16,y=>16 };

	my $min = $minlengths->{$array_name};
	my $max = $maxlengths->{$array_name};
	if( $table_width < $min 
	 || $table_width > $max ){
		print "Warning: number of elements in $array_name\[\] should be $min .. $max (inclusive)\n";
	}
}

# TODO: support other numbering than 16 (hex) and 10 (decimal)
# TODO: check limits
# what conversions do we want?
# >2 digit hexa values are truncated (only first 2 character used)
sub numbervalue
{
	my $arr_name = shift;
	my $aref = shift;

	my @ret;

	my $el;
	foreach $el (@$aref){
		my $val;
		if($numberformat eq '16'){
			if( length($el) > 2 ){
				# only higher byte is used
				$val = hex( substr($el,0,2) );
			} else {
				$val = hex( $el );
			}
		} else {
			$val = $el;
		}
	
		if($numberformat eq 'real'){
			$val = realworld($arr_name, $val);
		} 
		push @ret, trimtrim($val);
	} # foreach
	return @ret;
}

# conversion from realworld (what is that ? :-) units 
#
sub realworld
{
	my $arr_name = shift;
	my $realworld = shift;

	my $internal = $realworld;
	
	if($arr_name eq 'n'){	# ignition advance table
		$internal = $realworld * 4;	# unit is [0.25 degrees]
	}
	
	return $internal;
}

# rounds and railing (checks limits)
sub trimtrim
{
	my $in = shift;
	my $ok=1;
	
	$in = int($in+0.5);

	if($in < -0.5){
		$ok = undef;
		$in = 0;
	} elsif ($in >255.5){
		$ok = undef;
		$in = 255;
	}

	if(! $ok){
		print STDERR "Warning: out of bounds: $in";
	}
	return $in;
}

