#!/usr/bin/perl
#----------------------------------------------------------------------
#
# Generate wait events support files from waiteventnames.txt:
# - waiteventnames.h
# - waiteventnames.c
# - waiteventnames.sgml
#
# Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
# src/backend/utils/activity/generate-waiteventnames.pl
#
#----------------------------------------------------------------------

use strict;
use warnings;
use Getopt::Long;

my $output_path = '.';

my %hashwe;
my $waitclass;
my @wait_classes = ("PG_WAIT_ACTIVITY", "PG_WAIT_CLIENT", "PG_WAIT_IPC", "PG_WAIT_TIMEOUT", "PG_WAIT_IO");


GetOptions(
	'outdir:s'       => \$output_path);

open my $waiteventnames, '<', $ARGV[0] or die;

# Read the input file and populate the hash table
while (<$waiteventnames>)
{
	chomp;

	# Skip comments
	next if /^#/;
	next if /^\s*$/;

	die "unable to parse waiteventnames.txt"
	  unless /^(\w+)\t+(\w+)\t+("\w+")\t+("\w.*\.")$/;

	(my $waitclassname, my $waiteventenumname, my $waiteventdescription, my $waitevendocsentence) = ($1, $2, $3, $4);

	my @waiteventlist = [$waiteventenumname, $waiteventdescription, $waitevendocsentence];
	my $trimmedwaiteventname = $waiteventenumname;
	$trimmedwaiteventname =~ s/^WAIT_EVENT_//;
	die "wait event names must start with 'WAIT_EVENT_'" if $trimmedwaiteventname eq $waiteventenumname;
	push(@{ $hashwe{$waitclassname} }, @waiteventlist);
}
close $waiteventnames;

# Generate the output data
my @classes;
foreach $waitclass (sort keys %hashwe) {
	my $last = $waitclass;
	$last =~ s/^WaitEvent//;
	my $lastuc = uc $last;
	my $lastlc = lc $last;
	my $pg_wait_class = "PG_WAIT_".$lastuc;
	die "waitclass $pg_wait_class does not exist" unless grep( /^$pg_wait_class$/, @wait_classes );

	my @events;
	foreach my $wev (@{$hashwe{$waitclass}}) {
		push @events, {
			PG_WAIT_CLASS => $pg_wait_class,
			ENUM_NAME => $wev->[0],
			WAIT_EVENT_ENUM => $wev->[1],
			WAIT_EVENT_NAME => substr($wev->[1], 1, -1),
			WAIT_EVENT_DOC =>  substr($wev->[2], 1, -1)
			};
	}
	push @classes, {
		WAIT_CLASS => $waitclass,
		DISPLAY_NAME => ucfirst($lastlc),
		LASTLC => $lastlc,
		EVENTS => \@events
		};
}

my $header_comment =
  '/*-------------------------------------------------------------------------
 *
 * waiteventnames.%s
 *    Generated wait events infrastructure code
 *
 * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * NOTES
 *  ******************************
 *  *** DO NOT EDIT THIS FILE! ***
 *  ******************************
 *
 *  It has been GENERATED by src/backend/utils/activity/generate-waiteventnames.pl
 *
 *-------------------------------------------------------------------------
 */
';



# Include PID in suffix in case parallel make runs this multiple times.
my $htmp = "$output_path/waiteventnames.h.tmp$$";
open my $h, '>', $htmp or die "Could not open $htmp: $!";
my $ctmp = "$output_path/waiteventnames.c.tmp$$";
open my $c, '>', $ctmp or die "Could not open $ctmp: $!";
my $stmp = "$output_path/waiteventnames.s.tmp$$";
open my $s, '>', $stmp or die "Could not open $stmp: $!";

printf $h $header_comment, 'h';
print $h <<'H1';
#ifndef WAITEVENTNAMES_H
#define WAITEVENTNAMES_H

#include "utils/wait_event.h"

H1

foreach my $cl (@classes) {
	my $wait_class = $cl->{WAIT_CLASS};
	print $h <<'HC1';
typedef enum
{
HC1
	my $evs = $cl->{EVENTS};
	my $last_event = scalar(@$evs) - 1;
	my $ectr = 0;
	for my $event (@$evs) {
		printf $h "\t%s%s%s\n",
			$event->{ENUM_NAME},
			($ectr == 0) ? " = $event->{PG_WAIT_CLASS}" : "",
			($ectr == $last_event) ? "" : ",";
		$ectr++;
	}
	print $h <<"HC2";
} $wait_class;

HC2
}

print $h <<'H2';
#endif                          /* WAITEVENTNAMES_H */
H2

close $h;


printf $c $header_comment, 'c';
foreach my $cl (@classes) {
	my $wait_class = $cl->{WAIT_CLASS};
	my $lastlc = $cl->{LASTLC};
	print $c <<"CC1";
static const char *
pgstat_get_wait_$lastlc($wait_class w)
{
	const char *event_name = "unknown wait event";

	switch (w)
	{
CC1
	my $evs = $cl->{EVENTS};
	for my $event (@$evs) {
		my $enum_name = $event->{ENUM_NAME};
		my $wait_event_enum = $event->{WAIT_EVENT_ENUM};
		print $c <<"CCEV"
		 case $enum_name:
			 event_name = $wait_event_enum;
			 break;
CCEV
	}
	print $c <<"CC2";
			 /* no default case, so that compiler will warn */
	}

	return event_name;

}
CC2
}

close $c;

foreach my $cl (@classes) {
	my $display_name = $cl->{DISPLAY_NAME};
	my $lastlc = $cl->{LASTLC};
	print $s <<"SC1";
  <table id="wait-event-$lastlc-table">
   <title>Wait Events of Type <literal>$display_name</literal></title>
   <tgroup cols="2">
    <thead>
     <row>
      <entry><literal>Activity</literal> Wait Event</entry>
      <entry>Description</entry>
     </row>
    </thead>

    <tbody>
SC1
	my $evs = $cl->{EVENTS};
	for my $event (@$evs) {
		my $wait_event_name = $event->{WAIT_EVENT_NAME};
		my $wait_event_doc = $event->{WAIT_EVENT_DOC};
		print $s <<"SCEV";
     <row>
      <entry><literal>$wait_event_name</literal></entry>
      <entry>$wait_event_doc</entry>
     </row>
SCEV
	}
	print $s <<"SC2";
    </tbody>
   </tgroup>
  </table>
SC2
}

close $s;

rename($htmp, "$output_path/waiteventnames.h") || die "rename: $htmp to $output_path/waiteventnames.h: $!";
rename($ctmp, "$output_path/waiteventnames.c") || die "rename: $ctmp: $!";
rename($stmp, "$output_path/waiteventnames.sgml") || die "rename: $ctmp: $!";

