#!/usr/bin/perl
use File::Basename;
use Cwd;
my $cwd = getcwd;

use strict;
use integer;
sub devsort;

my $input = shift;
my $output = shift;
my $base = "/tmp/" . basename($input, '.in') . '.' . $$;
my $c = $base . '.c';
my $shilka = $base . '.shilka';

open(INPUT, $input) or die "$0: couldn't open '$input' - $!\n";

my @lines = ();
my $storage_ix = -1;
my @storage = ();
my %pointers = ();
my @patterns = ();
my $patterns_ix = -1;
while (<INPUT>) {
    if (/%storage_here/) {
	$storage_ix = @lines;
    } elsif (/^"([^"]+)",\s*(.*)$/o) {
    	push(@patterns, [$1, $2]);
	next;
    }
    if (@patterns) {
	for my $f (sort devsort @patterns) {
	    my $x = $f->[0];
	    my $rest = $f->[1];
	    my ($dev, $devrest) = ($x =~ /([^%]+)(%.*)?$/o);
	    push(@lines, generate($dev, $devrest, $rest, []));
	}
	@patterns = ();
    }
    push(@lines, $_);
}

close INPUT;
# @storage = sort devsort @storage;
chop $storage[$#storage];
chop $storage[$#storage];
$storage[$#storage] .= "\n";
splice(@lines, $storage_ix, 1,
       "static const device dev_storage[] =\n", "{\n",
       @storage, "};\n\n",
       sort {$a cmp $b} values %pointers);
open(SHILKA, '>', $shilka);
print SHILKA @lines;
close SHILKA;

chdir '/tmp';
system qw'shilka -length -strip -no-definitions', $shilka;
if ($? == -1) {
    die "$0: shilka command missing? - $!\n";
} else {
    exit $? if $?;
}
chdir $cwd;
unlink $shilka;
open(C, $c) or die "$0: couldn't open $c - $!\n";
@lines = <C>;
close C;
unlink $c;
splice(@lines, 0, 3);
my $ign_until_brace = 0;
for (my $i = 0; $i < @lines; $i++) {
    $_ = $lines[$i];
    $ign_until_brace = 1 if /(?:KR_reset|KR_output_statistics).*\)\s*$/o;
    if ($ign_until_brace || /(?:#\s*line|(?:KR_reset|KR_output_statistics).*;)/)  {
	$ign_until_brace = 0 if $ign_until_brace && /}/o;
	splice(@lines, $i, 1);
	redo;
    };
}
open(OUTPUT, '>', $output) or do {{
    if (chmod(0664, $output)) {
	open(OUTPUT, '>', $output);
	last;
    }
    die "$0: couldn't open $output - $!\n";
}};
print OUTPUT @lines;
close OUTPUT;

sub generate {
    my $dev = shift;
    my $devrest = shift;
    my $rest = shift;
    my $vars = shift;
    my $res;
    my @lines = ();
    if ($devrest) {
	my ($a, $low, $high, $fmt, $b) = ($devrest =~ /%([\({])([^-]+)-([^\)}]+)[\)}](.)(.*)/o);
	my ($middle, $devrest0) = ($b =~ /^([^%]*)(%.*)?$/);
	$fmt = "%$fmt";
	my $vars_ix = @{$vars};
	for my $f ($low .. $high) {
	    $vars->[$vars_ix] = $f;
	    $#{$vars} = $vars_ix;
	    my $dev0 = $dev . sprintf($fmt, $f) . $middle;
	    push(@lines, generate($dev0, $devrest0, $rest, $vars));
	}
      } else {
	my $fh = $dev;
	$fh =~ s%/%_%og;
	my $shilka_id = $fh;
	my $storage_str = $fh . '_storage';
	$fh =~ s/^_dev_/FH_/o;
	$fh = uc $fh;
	$shilka_id =~ s/^_dev_//o;
	$storage_str =~ s/^_dev/dev/o;
	my $storage_loc = "dev_storage + " . @storage;
	@lines = ('"' . $dev . '"' . " = $shilka_id {return $storage_loc;}\n");
	$rest = "$fh, $rest" if $rest =~ /^"/o;
	$rest = fixup($rest, $vars);
	if ($rest =~ /^(.*), ([a-z_]*_dev)/) {
	    $pointers{$2} ||= "const device *$2 = $storage_loc;\n";
	    $rest = $1;
	}
	push(@storage, "  {\"$dev\", " . $rest . "},\n");
    }
    return @lines;
}

sub fixup {
    my $rest = shift;
    my $vars = shift;
    0 while $rest =~ s/{([^}]*)}/evalit($1, $vars)/eg;
    return $rest;
}

sub evalit {
    my $what = shift;
    my $vars = shift;
    $what =~ s/\$(\d+)/'$vars->[$1-1]'/g;
    my $res = eval $what;
    return $res;
}

sub devsort {
    my $a0 = $a->[0];
    my $b0 = $b->[0];
    $a0 =~ s/(\D)(\d+)/"$1" . sprintf "%05d", $2/e;
    $b0 =~ s/(\D)(\d+)/"$1" . sprintf "%05d", $2/e;
    return $a0 cmp $b0;
}
