#!/usr/bin/perl

use strict;
use warnings;

open E, "< enums.decl" or die "unable to open enums.h";
open O, "> enums.h" or die "unable to open enums.c";

my %enum;
my %tag;
my %ver;
my @s;
my $c;
my $last = 0;
while(<E>) {
    s|/\*.*/||;
    s|^\s\* .*||;
    s|^.*\*/||;
    if (my ($name, $tag, $ver) = /^enum\s*(\w+)\s*(?:=>\s*(\w+))?(?:\s+if\s+(\d+\.(?:\d+(?:\.\d+)?)?))?/) {
        $last = 0;
	# $name =~ s/^ldap_//;
	$c = $enum{$name} = [];
        $tag{$name} = $tag;
        if (defined $ver) {
            $ver =~ /^(\d+)(?:\.(\d+)(?:\.(\d+))?)?$/ or die "bad version number $ver";
            $ver{$name} = sprintf("#if PG_VERSION_NUM >= %d%02d%02d\n", $1, $2 || 0, $3 || 0)
        }
    }
    elsif (/^\s*((?:P[QG]|CONN)\w+)\s*=\s*(\d+),/) {
        $c->[$2] = $1;

        $last = $2 + 1;
    }
    elsif (/^\s*((?:P[QG]|CONN)\w+)\s*,/) {
        $c->[$last++] = $1;
    }
    elsif (m|^\s*s/(\w+)/(\w*)/|) {
        push @s, [$1, $2];
    }
}

@s = sort { length($b->[0]) <=> length($a->[0]) } @s;
# use Data::Dumper; print Dumper \@s;

print O <<HEAD;
/*
 *
 * This file is generated by gen_constants.pl
 * Do not edit by hand!
 *
 */

HEAD

for my $enum (sort keys %enum) {
    my $ver = $ver{$enum};
    defined $ver and print O $ver;
    my $len = @{$enum{$enum}};
    print O "SV *enum2sv_${enum}[$len];\n";
    defined $ver and print O "#endif\n";
}

# print O <<DECL for @c;
# SV *${_}_sv;
# DECL

print O <<INIT;

static void
init_constants(void) {
INIT

for my $enum (sort keys %enum) {
    my $c = $enum{$enum};
    my $tag = $tag{$enum};
    my $ver = $ver{$enum};

    defined $ver and print O $ver;

    if (defined $tag and length $tag) {
        $tag = qq("$tag");
    }
    else {
        $tag = 'NULL';
    }


    for my $ix (0..$#$c) {
	my $name = $c->[$ix];
	my $value;
	if (defined $name) {
	    $value = $name;
            unless (defined $c->[0]) {
                $c->[0] = "$value - $ix"
            }
	}
	else {
	    $name = uc "${enum}_$ix";
	    $value = $ix;
	}

        for my $s (@s) {
            my ($f, $t) = @$s;
            $name =~ s/^$f/$t/;
        }

	printf O <<C, $ix, $name, length($name), $value, $tag;
    enum2sv_${enum}[%d] = make_constant("%s", %d, %s, %s);
C

    }
    defined $ver and print O "#endif\n";

    print O "\n";
}

print O <<END;
}
END

for my $enum (sort keys %enum) {
    my $ver = $ver{$enum};
    defined $ver and print O $ver;

    my $len = @{$enum{$enum}};
    my $start = $enum{$enum}[0] || 0;
    print O <<ETS;
static SV *
${enum}2sv(I32 ix) {
    SV *sv;
    ix -= $start;
    if ((ix < 0) || (ix >= $len)) {
        return newSViv(ix);
    }
    sv = newSVsv(enum2sv_${enum}[ix]);
    return sv;
}
ETS
    defined $ver and print O "#endif\n";
    print O "\n";
}
