#!/usr/bin/perl

# Read and parse commands.x to generate commands.c
open(I,"<commands.x") or exit(1); binmode(I);
open(O,">commands.c") or exit(1); binmode(O);

print O "/* THIS FILE IS AUTO-GENERATED, DO NOT EDIT */\n\n";

my $cmd = "";
my $linenumber = 0;
my $CurrentBlock = "global";

my $Doc = {};

while(my $line = <I>) {
   chomp $line;
   ++$linenumber;

   next if ! $line;
   if ($line =~ /^#define/) { print O "$line\n"; next; }
   if ($line =~ /^\/\//) { print O "$line\n"; next; }

   if ($line eq '#') {
	AddDoc("");
	next;
	}

   if ($line =~ /^#\s+(.+)/) {
	AddDoc($1);
	next;
	}

   if ($line =~ /^global (.+)$/) {
	AddGlobal($1);
	next;
	}

   if ($line =~ /^\s+/) {
	AddOutput($line);
	next;
	}

   StartBlock(lc $line);
   }

close(I);

print O "\n#include \"ninox.h\"\n\n";

foreach my $c (sort { $a cmp $b } keys %$Block) {
   print O "int $c(char *arg, char *val, int bval)\n\t{\n";
   foreach my $l (@{$Block->{$c}}) {
	print O "$l\n";
	}
   print O "\treturn(1);\n\t}\n\n";

   if (exists $Doc->{$c}) {
	my $C = $c; $C =~ s/^_CMD_//;
	print O "int _HLP_$c(char *arg, char *val, int bval)\n\t{\n";
	print O "\tPrint(\"\\nHelp for '%s'\\n\\n\",\"$C\");\n";
   	foreach my $l (@{$Doc->{$c}}) {
	   $l =~ s/(['"])/\\$1/g;
	   print O "\tPrint(\"\t$l\\n\");\n";
	   }
	print O "\texit(0);\n\t}\n\n";
	}
   }

print O "\n\n";
print O "struct cfunc Names[] = {\n";
foreach my $n (sort { $a cmp $b } keys %Names) {
	my $val = $Names{$n};
	print O "\t{\"$n\" , $val },\n";
	print O "\t{\"help-$n\" , _HLP_$val },\n" if exists $Doc->{$val};
	}
print O "\t{ \"\", NULL }};\n\n";

# Generate a function to display all global variables
print O "int ShowGlobals()\n\t{\n";
print O "\tPrint(\"[ninox]\\n\");\n";


open(G,">globals.c") or die; binmode(G);
open(H,">globals.h") or die; binmode(H);
print H "/* THIS FILE IS AUTO-GENERATED, DO NOT EDIT */\n\n";

print G "/* THIS FILE IS AUTO-GENERATED, DO NOT EDIT */\n\n";
print G "#include \"ninox.h\"\n\n";

my @g = sort { 	my $A=lc $a; my $B=lc $b; 
		$A =~ s/^\w+\s+\W*//; $B =~ s/^\w+\s+\W*//; 
		$A cmp $B} keys %GLOBALS;
foreach my $g (@g) {
   $g =~ s/\s+/\t/;
   print G "$g\n";
   $g =~ s/\s*=.+$//;
   print H "extern $g;\n";

   my ($type,$var) = $g =~ /^(\w+\W+)(\w+)/g;

   $type = '%d' if $type =~ /^int\s+/;
   $type = '%lf' if $type =~ /^double\s+/;
   $type = '%s' if $type =~ /^char\s+\*/;

   print O "\tPrint(\"$var=$type\\n\",$var);\n";
   }

print O "\t}\n";

print H "\nextern struct cfunc Names[];\n";

close(O);
close(G);
close(H);

exit(0);

#===============================================

sub AddOutput
	{
	push @{$Block->{$CurrentBlock}}, @_;
	}

sub AddDoc
	{
	push @{$Doc->{$CurrentBlock}}, @_;
	}

sub AddName
	{
	my $n = shift;
	my $pri = shift;

	die "duplicate name '$n' on line $linenumber" if exists $Names{$n};
	$Names{$n} = $pri;
	}

sub StartBlock
	{
	my $line = shift;
	my @names = split(/\s+/, $line);

	my $primary = shift @names;

	my $func = "_CMD_$primary";
	$func =~ s/\-/_/g;

	AddName($primary,$func);

	# Collect aliases and add them too
	foreach my $n (@names) {
	   AddName($n,$func);
	   }

	$CurrentBlock = $func;
	}

sub AddGlobal
	{
	my $var = shift;

	die "Duplicate global '$var' (line $linenumber)" if exists $GLOBALS{$var};
	$GLOBALS{$var} = 1;
	}
