#! /usr/bin/perl
#
# Read files, looking for MPIU_STATE_DECL(a), #define FUNCNAME name, 
# and MPI[DUI]*_STATE_DECL(a), and char FCNAME[] = "name".  
# Use these to create a file that provides a mapping from the id values
# to the routine names.  Note that since the state names are often
# defined by an enum, these values must be set at runtime.
# 
require "maint/parse.sub";

$debug = 0;
$showfiles = 0;
$depth = 1;
$curdir = ".";
$array_name = "MPIU_States";   # Default array name
$field_name = "";              # Use ".foo" for an array of structures
$outfile = "";
$namefile = "";
# Check for special args
@files = ();
foreach $arg (@ARGV) {
    if ($arg =~ /^-showfiles/) { $showfiles = 1; }
    elsif( $arg =~ /-debug/) { $debug = 1; }
    elsif( $arg =~ /-arrayname=(.*)/) {
	$array_name = $1;
    }
    elsif ($arg =~ /-outfile=(.*)/) {
	$outfile = $1;
    }
    elsif ($arg =~ /-namefile=(.*)/) {
	$namefile = $1;
    }
    else {
	print "Adding $arg to files\n" if $debug;
	$files[$#files+1] = $arg;
    }
}
# Process the definitions

foreach $file (@files) {
    print "$file\n" if $showfiles;
    if (-d $file) {
	if ($file =~ /\.\./ || $file =~ /^\.$/) { next; }
	&ProcessDir( $file );
    }
    else {
	&ProcessFile( $file );
    }
}

&OutputStateFile ( $outfile );
&OutputNameFile( $namefile );

# ---------------------------------------------------------------------------

sub ProcessFile { 
    my $filename = $_[0];
    open (FD, "<$filename" ) || die "Could not open $filename\n";

    #
    # Clear all names
    my $state_name = "";
    my $fcname = "";

    while (<FD>) {
	# Look for the definitions
	if (/MPI[DUI]*_STATE_DECL\((.*)\)\s*;/) {
	    $state_name = $1;
	    # At this point, we should have the name of the function
	    if ($fcname eq "") {
		# Use the filename if we don't have the function name;
		# also extract the name from the state
		$funcname = $state_name;
		$funcname =~ s/MPI.*_STATE_//g;
		if (defined($statenames{$state_name})) {
		    # Override the name with 
		    $statenames{$state_name} = "many:$funcname";
		}
		else {
		    $statenames{$state_name} = "$filename:$funcname";
		}
	    }
	    else {
		$statenames{$state_name} = $fcname;
	    }
	}
	elsif (/MPI[DUI]*_MPI_STATE_DECL\((.*)\)\s*;/) {
	    $state_name = $1;
	    # At this point, we should have the name of the function
	    $statenames{$state_name} = $fcname;
	}
	elsif (/char\s+FCNAME\[\]\s*=\s*\"(.*)\"/) {
	    $fcname = $1;
	}	
	elsif (/\#define\s+FUNCNAME\s+([A-Za-z0-9_]*)/) {
	    $fcname = $1;
	}
    }		
    close FD;
}

sub OutputRegistered  {
    my $filename = $_[0];
    my $OUTFD = OUTFD;
    if ($filename ne "") {
	open( $OUTFD, ">$filename" ) || die "Could not open $filename\n";
    }
    else {
	$OUTFD = STDOUT;
    }
    foreach my $state_name (sort keys(%statenames)) {
	my $fcname = $statenames{$state_name};
	print $OUTFD "$array_name\[$state_name\]$field_name = \"$fcname\";\n";
    }
}

# ---------------------------------------------------------------------------
# This is a routine that outputs a compilable file that implements the
# routine MPIU_Describe_states, including the various header text
sub OutputStateFile {
    my $filename = $_[0];
    my $OUTFD = OUTFD;
    if ($filename ne "") {
	open( $OUTFD, ">$filename" ) || die "Could not open $filename\n";
    }
    else {
	$OUTFD = STDOUT;
    }

    # Output the header
    print $OUTFD "/* -*- Mode: C; c-basic-offset:4 ; -*- */\
/*  \
 *  (C) 2003 by Argonne National Laboratory.\
 *      See COPYRIGHT in top-level directory.\
 *\
 * This file is automatically generated by buildiface $arg_string\
 * DO NOT EDIT\
 */\
/* Generic definitions of states derived by mpich2/maint/getstates  */\
\
#include \"mpiimpl.h\"\
extern char *($array_name\[\]);\
void MPIU_Describe_states( void )\
{\n";

    # Output the assignments
    foreach my $state_name (sort keys(%statenames)) {
	my $fcname = $statenames{$state_name};
	print $OUTFD "    $array_name\[$state_name\]$field_name = \"$fcname\";\n";
    }

    # Output the end of the file
    print $OUTFD "}\n";

    if ($filename ne "") {
	close $OUTFD;
    }
    
}
# This is a routine that outputs a compilable file that defines all of
# the states
sub OutputNameFile {
    my $filename = $_[0];
    my $OUTFD = OUTFD;
    if ($filename ne "") {
	open( $OUTFD, ">$filename" ) || die "Could not open $filename\n";
    }
    else {
	$OUTFD = STDOUT;
    }

    # Output the header
    print $OUTFD "/* -*- Mode: C; c-basic-offset:4 ; -*- */\
/*  \
 *  (C) 2003 by Argonne National Laboratory.\
 *      See COPYRIGHT in top-level directory.\
 *\
 * This file is automatically generated by buildiface $arg_string\
 * DO NOT EDIT\
 */\
/* Generic definitions of states derived by mpich2/maint/getstates  */\
\
#ifndef LWLOG_STATES_INCLUDED\
#define LWLOG_STATES_INCLUDED\n";

    # Output the assignments.  Use #define for now
    my $i = 1;
    foreach my $state_name (sort keys(%statenames)) {
	print $OUTFD "#define $state_name $i\n";
	$i++;
    }

    print $OUTFD "\n#endif\n";

    if ($filename ne "") {
	close $OUTFD;
    }
}
# ---------------------------------------------------------------------------
sub ProcessDir {
    my $DIR = "DIR$depth"; $depth++;
    my $dir = $_[0]; $dir =~ s/\/$//;
    my $savedir = $curdir;
    $curdir =~ s/\/$//;
    $curdir = "$curdir/$dir";
    print "Processing directory $curdir\n" if ($verbose);
    opendir( $DIR, "$curdir" ) || die "Cannot open directory $curdir\n";
    my @filelist;
    while ($file = readdir( $DIR) ) {
	print "File $file\n" if ($verbose);
	$filelist[$#filelist+1] = $file;
    }
    closedir( $DIR );
    foreach $file (@filelist) {
	print "processing $file\n" if ($verbose);
	if (-d "$curdir/$file") {
	    if (! ($file =~ /^\./) && ! ($file =~ /CVS/) ) { 
		&SetCurrentDirectory( "$curdir/$file" );
		&ProcessDir( $file );
	    }
	}
	elsif ($file =~ /\.[ch]$/) {
	    print "File $file\n" if ($verbose);
	    &ProcessFile( "$curdir/$file" );
	}
    }
    closedir( $DIR );
    $curdir = $savedir;
}

# --------------------------------------------------------------------------
# These routines ensure that the current directory is printed before any
# warning messages.  This is used to reduce the amount of noise that
# comes out of the codingcheck
# --------------------------------------------------------------------------
$currentDirectory = "";
$directoryPrinted = 0;
sub SetCurrentDirectory {
    $currentDirectory =  $_[0];
    $directoryPrinted = 0;
}
sub PrintCurrentDirectory {
    if (!$directoryPrinted) {
	$directoryPrinted = 1;
	print "Directory $currentDirectory\n";
    }
}
