#!/usr/bin/perl -w
# collate-test: Collate testcases from several source files.
#
# Copyright (C) 2007,2008,2011,2015 Olly Betts
# Copyright (C) 2008 Lemur Consulting Ltd
#
# 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, see
# <https://www.gnu.org/licenses/>.

use strict;

my $generated_warning =
"/* Warning: This file is generated by $0 - do not modify directly! */\n";

my %by_cond = ();
my $srcdir = shift @ARGV;
my $collated_hdr = shift @ARGV;
my $allfile = shift @ARGV;

open COLLATED_HDR, ">$collated_hdr.tmp" or die $!;
print COLLATED_HDR "$generated_warning\n";

open ALL, ">$allfile.tmp" or die $!;
print ALL $generated_warning;

for my $cc_source (@ARGV) {
    (my $generated_h = $cc_source) =~ s/\.\w+$/\.h/;
    print ALL "#include \"$generated_h\"\n";

    -f $cc_source or $cc_source = "$srcdir/$cc_source";

    open HDR, ">$generated_h.tmp" or die $!;
    print HDR $generated_warning;
    open SRC, "<$cc_source" or die $!;
    my $ignore = 0;
    my @if_stack;
    while (<SRC>) {
        if (!$ignore && /^DEFINE_TESTCASE\((.*?),\s*(.*)\)/) {
            my ($test_func, $cond) = ($1, $2);
            $cond =~ s/\s+//g;
            push @{$by_cond{$cond}}, $test_func;
            print HDR "extern void test_$test_func();\n";
        }
        if (s/^#\s*//) {
            s!/\*.*?\*/! !g;
            s!//.*!!;
            if (/^if\s*0\s*$/) {
                push @if_stack, $ignore;
                $ignore = 1;
            } elsif (/^if/) {
                push @if_stack, $ignore;
            } elsif (/^endif/) {
                $ignore = pop @if_stack;
            }
        }
    }
    close SRC;
    close HDR or die $!;
    update_if_required($generated_h);
}

foreach my $cond (sort keys %by_cond) {
#        my $name = $cond;
#        $name =~ s/\&\&/_and_/g;
#        $name =~ s/\|\|/_or_/g;
#        $name =~ s/\!/not_/g;
#        $name =~ s/\(/bra_/g;
#        $name =~ s/\)/_ket/g;
    my $c_cond = $cond;
    $c_cond =~ s/\b([a-z]+)\b/(properties&\U$1\E)/g;
    print COLLATED_HDR <<END;
    if ($c_cond) {
        static const test_desc tests[] = {
END
    for (@{$by_cond{$cond}}) {
        print COLLATED_HDR <<END;
            { \"$_\", test_$_ },
END
    }
    print COLLATED_HDR <<END;
            { 0, 0 }
        };
        result = max(result, test_driver::run(tests));
    }
END
}

close ALL or die $!;
update_if_required($allfile);

close COLLATED_HDR or die $!;
update_if_required($collated_hdr);

# If $file.tmp is different to $file, rename it over the top.  Otherwise
# just delete it to avoid needlessly updating $file's timestamp.
sub update_if_required {
    my $file = shift;
    my $current_size = -s $file;
    if (defined $current_size && $current_size == -s "$file.tmp") {
        open OLD, "<$file" or die $!;
        open NEW, "<$file.tmp" or die $!;
        my $different = 0;
        while (<OLD>) {
            my $new = <NEW>;
            if ($_ ne $new) {
                $different = 1;
                last;
            }
        }
        close OLD;
        close NEW;
        if (! $different) {
            unlink "$file.tmp";
            return;
        }
    }
different:
    unlink $file if $^O eq 'MSWin32' && defined $current_size;
    rename "$file.tmp", $file or dir $!;
}
