#! /usr/bin/env perl
#
# (C) 2014 by Argonne National Laboratory.
#     See COPYRIGHT in top-level directory.
#

use warnings;
use strict;
use File::Basename;

my $cur_dir = "src/binding/fortran/use_mpi_f08";
my $wrappers_f_dir = "wrappers_f";
my $pmpi_dir = "wrappers_f/profiling";

# Stage 1: Translate mpi_f08.f90 into pmpi_f08.f90
# =====================================================================
my $mpi_f08_file = "mpi_f08.f90";
my $pmpi_f08_file = "pmpi_f08.f90";
my $mpi_f08_fh;
my $pmpi_f08_fh;

open($mpi_f08_fh,  "<", $mpi_f08_file)  || die "Error: Could not open $mpi_f08_file, $!";
open($pmpi_f08_fh, ">", $pmpi_f08_file) || die "Error: Could not open $pmpi_f08_file, $!";

while (<$mpi_f08_fh>) {
    if (/pmpi_f08/) {
        next; # Skip the "use :: pmpi_f08" line
    }
    # MPI_ part
    elsif (/module\s+mpi_f08/) {
        $_ =~ s/module\s+mpi_f08/module pmpi_f08/;
    } elsif (/interface\s+MPI_/) {
        $_ =~ s/interface\s+MPI_/interface PMPI_/;
    } elsif (/subroutine\s+MPI_/) {
        $_ =~ s/subroutine\s+MPI_/subroutine PMPIR_/;
    } elsif (/function\s+MPI_/) {
        $_ =~ s/function\s+MPI_/function PMPIR_/;
    }
    # MPIX_ part
    elsif (/interface\s+MPIX_/) {
        $_ =~ s/interface\s+MPIX_/interface PMPIX_/;
    } elsif (/subroutine\s+MPIX_/) {
        $_ =~ s/subroutine\s+MPIX_/subroutine PMPIXR_/;
    } elsif (/function\s+MPIX_/) {
        $_ =~ s/function\s+MPIX_/function PMPIXR_/;
    }
    print $pmpi_f08_fh $_;
}

close($mpi_f08_fh);
close($pmpi_f08_fh);

# Stage 2: Translate Fortran MPI wrapper files into PMPI wrapper files
# =====================================================================

mkdir $pmpi_dir unless -d $pmpi_dir;
foreach my $mpi_file (glob("$wrappers_f_dir/*.f90")) {
    my $basename = basename($mpi_file);
    my $pmpi_file = "$pmpi_dir/p$basename";
    my $mpi_fh;
    my $pmpi_fh;

    open($mpi_fh,  "<", $mpi_file)  or die "Error: Could not open $mpi_file, $!";
    open($pmpi_fh, ">", $pmpi_file) or die "Error: Could not open $pmpi_file, $!";

    while (<$mpi_fh>) {
        if (/subroutine\s+MPI_/) {
            $_ =~ s/subroutine\s+MPI_/subroutine PMPIR_/
        } elsif (/function\s+MPI_/) {
            $_ =~ s/function\s+MPI_/function PMPIR_/
        }
        elsif (/subroutine\s+MPIX_/) {
            $_ =~ s/subroutine\s+MPIX_/subroutine PMPIXR_/
        } elsif (/function\s+MPIX_/) {
            $_ =~ s/function\s+MPIX_/function PMPIXR_/
        }

        print $pmpi_fh $_;
    }

    close($mpi_fh);
    close($pmpi_fh);
}

# Stage 3: Generate Makefile.mk under use_mpi_f08
# =====================================================================
my $makefile = "Makefile.mk";
my $makefile_fh;
my @files;

open($makefile_fh, ">", $makefile) || die "Error: Could not open $makefile, $!";

print $makefile_fh  <<EOT;
# -*- Mode: Makefile; -*-
#
# (C) 2015 by Argonne National Laboratory.
#     See COPYRIGHT in top-level directory.
#
#  AUTOGENERATED! DO NOT EDIT!
#
#  This file is created by script $cur_dir/buildiface

# ensure that the buildiface script ends up in the release tarball
EXTRA_DIST += $cur_dir/buildiface

if BUILD_F08_BINDING

EOT

# Module dependence chain
my %f08_mods = (
    "mpi_c_interface" => "mpi_c_interface_cdesc",
    "mpi_c_interface_cdesc" => "mpi_c_interface_nobuf",
    "mpi_c_interface_nobuf" => "mpi_c_interface_glue",
    "mpi_c_interface_glue" => "mpi_f08",
    "mpi_f08" => "pmpi_f08",
    "pmpi_f08" => "mpi_f08_link_constants",
    "mpi_f08_link_constants" => "mpi_f08_callbacks",
    "mpi_f08_callbacks" => "mpi_f08_compile_constants",
    "mpi_f08_compile_constants" => "mpi_f08_types",
    "mpi_f08_types" => "mpi_c_interface_types",
    "mpi_c_interface_types" => "",
    );

print $makefile_fh  <<EOT;
AM_FCFLAGS += \@FCINCFLAG\@src/binding/fortran/use_mpi_f08

mpi_fc_sources += \\
	src/binding/fortran/use_mpi_f08/pmpi_f08.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_f08.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_f08_callbacks.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_f08_compile_constants.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_f08_link_constants.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_f08_types.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_c_interface.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_c_interface_cdesc.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_c_interface_glue.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_c_interface_nobuf.f90 \\
	src/binding/fortran/use_mpi_f08/mpi_c_interface_types.f90

mpi_fc_modules += \\
  src/binding/fortran/use_mpi_f08/\$(PMPI_F08_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_F08_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_F08_CALLBACKS_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_F08_COMPILE_CONSTANTS_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_F08_LINK_CONSTANTS_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_F08_TYPES_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_C_INTERFACE_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_C_INTERFACE_CDESC_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_C_INTERFACE_GLUE_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_C_INTERFACE_NOBUF_NAME).\$(MOD) \\
  src/binding/fortran/use_mpi_f08/\$(MPI_C_INTERFACE_TYPES_NAME).\$(MOD)

EOT

print $makefile_fh "f08_module_files =";
foreach my $x (keys %f08_mods) {
    my $DOLLARX = "\$(".uc($x)."_NAME)"; # Convert to $(X_NAME) for later substitution by configure
    print $makefile_fh " \\\n    src/binding/fortran/use_mpi_f08/$DOLLARX.\$(MOD)";
}
print $makefile_fh "\n\n";
print $makefile_fh "BUILT_SOURCES += \$(f08_module_files)\n";
print $makefile_fh "CLEANFILES += \$(f08_module_files)\n\n";

print $makefile_fh "mpi_fc_sources += \\\n";
@files = glob("$wrappers_f_dir/*.f90 $pmpi_dir/*.f90");
for (my $i=0; $i<=$#files; $i++) {
    print $makefile_fh "\t$cur_dir/$files[$i]";
    if ($i != $#files) { print $makefile_fh " \\\n"; } else { print $makefile_fh "\n\n"; }
}

# Don't reuse FC_COMPILE_MODS since it has its own FCMODOUTFLAG directory
print $makefile_fh "F08_COMPILE_MODS = \$(LTFCCOMPILE)\n";
print $makefile_fh "F08_COMPILE_MODS += \$(FCMODOUTFLAG)src/binding/fortran/use_mpi_f08\n\n";

# Generate module files using multi-ouput automake rules.
# We need to compile modules according to dependency given in %f08_mods
foreach my $x (keys %f08_mods) {
    my $DOLLARX = "\$(".uc($x)."_NAME)";
    print $makefile_fh "src/binding/fortran/use_mpi_f08/$x.stamp: src/binding/fortran/use_mpi_f08/$x.f90";
    if ($f08_mods{$x}) { print $makefile_fh " src/binding/fortran/use_mpi_f08/$f08_mods{$x}.stamp"; }
    print $makefile_fh "\n";

    print $makefile_fh <<EOT;
\t\@rm -f src/binding/fortran/use_mpi_f08/$x.tmp
\t\@touch src/binding/fortran/use_mpi_f08/$x.tmp
\t\$(mod_verbose)\$(F08_COMPILE_MODS) -c `test -f 'src/binding/fortran/use_mpi_f08/$x.f90' || echo '\$(srcdir)/'`src/binding/fortran/use_mpi_f08/$x.f90 -o src/binding/fortran/use_mpi_f08/$x.lo
\t\@mv src/binding/fortran/use_mpi_f08/$x.tmp src/binding/fortran/use_mpi_f08/$x.stamp

src/binding/fortran/use_mpi_f08/$DOLLARX.\$(MOD) src/binding/fortran/use_mpi_f08/$x.lo : src/binding/fortran/use_mpi_f08/$x.stamp
\t\@if test -f \$@; then :; else \\
\t  trap 'rm -rf src/binding/fortran/use_mpi_f08/$x-lock src/binding/fortran/use_mpi_f08/$x.stamp' 1 2 13 15; \\
\t  if mkdir src/binding/fortran/use_mpi_f08/$x-lock 2>/dev/null; then \\
\t    rm -f src/binding/fortran/use_mpi_f08/$x.stamp; \\
\t    \$(MAKE) \$(AM_MAKEFLAGS) src/binding/fortran/use_mpi_f08/$x.stamp; \\
\t    rmdir src/binding/fortran/use_mpi_f08/$x-lock; \\
\t  else \\
\t    while test -d src/binding/fortran/use_mpi_f08/$x-lock; do sleep 1; done; \\
\t    test -f src/binding/fortran/use_mpi_f08/$x.stamp; exit \$\$?; \\
\t   fi; \\
\tfi

CLEANFILES += src/binding/fortran/use_mpi_f08/$DOLLARX.\$(MOD) \\
    src/binding/fortran/use_mpi_f08/$x.lo \\
    src/binding/fortran/use_mpi_f08/$x.stamp \\
    src/binding/fortran/use_mpi_f08/$x.tmp

EOT
}

print $makefile_fh "include \$(top_srcdir)/src/binding/fortran/use_mpi_f08/wrappers_c/Makefile.mk\n\n";
print $makefile_fh "endif BUILD_F08_BINDING\n";
close($makefile_fh);
