#!/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 qq[
	#ifndef WAITEVENTNAMES_H
	#define WAITEVENTNAMES_H
	#include "utils/wait_event.h"
	];

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

		];
}

print $h '#endif                          /* WAITEVENTNAMES_H */';
close $h;


printf $c $header_comment, 'c';
foreach my $cl (@classes) {
	my $wait_class = $cl->{WAIT_CLASS};
	my $lastlc = $cl->{LASTLC};
	print $c qq[
		static const char *
		pgstat_get_wait_$lastlc($wait_class w)
		{
			const char *event_name = "unknown wait event";
			switch (w)
			{
		];
	my $evs = $cl->{EVENTS};
	for my $event (@$evs) {
		my $enum_name = $event->{ENUM_NAME};
		my $wait_event_enum = $event->{WAIT_EVENT_ENUM};
		print $c qq[
			case $enum_name:
				event_name = $wait_event_enum;
				break;
			];
	}
	print $c qq[
				/* no default case, so that compiler will warn */
				}
				return event_name;
			}
			];
}

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: $!";

