use strict;
use warnings;
use Alien::TDLib;

my $header = Alien::TDLib->dist_dir . '/include/td/telegram/td_api.h';
open my $h, '<', $header or die "$header: $!";
my $src = do { local $/; <$h> };
close $h;

my %fn;
while ($src =~ /^class (\w+) final : public Function \{(.*?)^\};/msg) {
    my ($name, $body) = ($1, $2);
    my @args;
    push @args, $1 while $body =~
        /^\s{2}(?!static|explicit|void|std::int32_t ID)[\w:<>,\s\*]+?\s+(\w+)_;\s*$/mg;
    $fn{$name} = "@args";
}

# Every class, its base, and the DECLARED TYPE of each member. This is what lets
# xt/schema_slots.t reject an @type that exists but is wrong for the slot it sits
# in -- the shape of both bugs found in the 0.03 review.
my (%base, %slot);
while ($src =~ /^class (\w+) (?:final )?: public (\w+) \{(.*?)^\};/msg) {
    my ($name, $parent, $body) = ($1, $2, $3);
    $base{$name} = $parent;
    while ($body =~ /^\s{2}(?!static|explicit|void|std::int32_t ID)
                     ([\w:<>,\s\*]+?)\s+(\w+)_;\s*$/mgx) {
        my ($type, $field) = ($1, $2);
        $type =~ s/std:://g; $type =~ s/\s+//g; $type =~ s/&&$//;
        # a class body opens with 'public:' on its own line, and the type
        # pattern spans newlines, so the FIRST member of every class picks
        # it up and would otherwise be dropped from the table
        $type =~ s/\Apublic://;
        # object_ptr<X> and array<object_ptr<X>> are the only shapes that carry
        # a nested @type; everything else is a scalar we do not police
        if    ($type =~ /\Aarray<object_ptr<(\w+)>>\z/) { $slot{$name}{$field} = "array:$1" }
        elsif ($type =~ /\Aobject_ptr<(\w+)>\z/)        { $slot{$name}{$field} = $1 }
    }
}
die "only ${\ scalar keys %fn} functions parsed from $header\n" unless keys %fn > 500;

my $version = Alien::TDLib->version // 'unknown';
my $out = 'lib/EV/Telegram/TDLib/Schema.pm';
open my $o, '>', $out or die "$out: $!";
print $o <<"HEAD";
package EV::Telegram::TDLib::Schema;

use strict;
use warnings;

our \$VERSION = '0.03';

# Generated by author/gen-schema.pl from the td_api.h that Alien::TDLib
# ships. Do not edit; regenerate instead.

our \$TDLIB_VERSION = '$version';

our %FUNCTIONS = (
HEAD
printf $o "    %-44s => '%s',\n", $_, $fn{$_} for sort keys %fn;
print $o ");\n\nour %CLASS_BASE = (\n";
printf $o "    %-46s => '%s',\n", $_, $base{$_} for sort keys %base;
print $o ");\n\nour %CLASS_SLOTS = (\n";
for my $c (sort keys %slot) {
    next unless keys %{ $slot{$c} };
    printf $o "    %-46s => { %s },\n", $c,
        join ', ', map { "$_ => '$slot{$c}{$_}'" } sort keys %{ $slot{$c} };
}
print $o ");\n\n1;\n";

close $o;

printf "wrote %s: %d functions, TDLib %s\n", $out, scalar keys %fn, $version;
