#! /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) 2014 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

print $makefile_fh "AM_FCFLAGS += \@FCINCFLAG\@src/binding/fortran/use_mpi\n\n";

print $makefile_fh  <<EOT;

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 \\
	src/binding/fortran/use_mpi/pmpi_f08.\$(MOD) \\
	src/binding/fortran/use_mpi/mpi_f08.\$(MOD) \\
	src/binding/fortran/use_mpi/mpi_f08_callbacks.\$(MOD) \\
	src/binding/fortran/use_mpi/mpi_f08_compile_constants.\$(MOD) \\
	src/binding/fortran/use_mpi/mpi_f08_link_constants.\$(MOD) \\
	src/binding/fortran/use_mpi/mpi_f08_types.\$(MOD)

EOT

print $makefile_fh "mpi_fc_modules += src/binding/fortran/use_mpi/mpi_f08.\$(MOD)\n\n";

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 "BUILT_SOURCES += ";
foreach my $x (keys %f08_mods) {
    print $makefile_fh "src/binding/fortran/use_mpi/$x.\$(MOD) ";
}
print $makefile_fh "\n\n";

print $makefile_fh "CLEANFILES += ";
foreach my $x (keys %f08_mods) {
    print $makefile_fh "src/binding/fortran/use_mpi/$x.\$(MOD) ";
}
print $makefile_fh "\n\n";

foreach my $x (keys %f08_mods) {
    print $makefile_fh "src/binding/fortran/use_mpi/$x.\$(MOD) src/binding/fortran/use_mpi_f08/$x.lo:";
    if ($f08_mods{$x}) {print $makefile_fh " src/binding/fortran/use_mpi/$f08_mods{$x}.\$(MOD)";}
    print $makefile_fh "\n";
    print $makefile_fh "\t\$(mod_verbose)\$(FC_COMPILE_MODS) -c src/binding/fortran/use_mpi_f08/`echo \$\@ | cut -f1 -d '.' | sed -e 's+.*/++g'`.F90 -o src/binding/fortran/use_mpi_f08/`echo \$\@ | cut -f1 -d '.' | sed -e 's+.*/++g'`.lo\n\n";
}

print $makefile_fh "mpi_fc_sources += \\\n";
@files = glob("$wrappers_f_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"; }
}

for (my $i=0; $i<=$#files; $i++) {
    my $fname = $files[$i];
    $fname =~ s/\.F90/.lo/g;
    print $makefile_fh "$cur_dir/$fname: src/binding/fortran/use_mpi/mpi_f08.\$(MOD) src/binding/fortran/use_mpi/mpi_c_interface.\$(MOD)\n\n";
}

print $makefile_fh "mpi_fc_sources += \\\n";
@files = glob("$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"; }
}

for (my $i=0; $i<=$#files; $i++) {
    my $fname = $files[$i];
    $fname =~ s/\.F90/.lo/g;
    print $makefile_fh "$cur_dir/$fname: src/binding/fortran/use_mpi/mpi_f08.\$(MOD) src/binding/fortran/use_mpi/mpi_c_interface.\$(MOD)\n\n";
}

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);

# Stage 4: Call script in ./wrappers_c to generate C wrappers for subarrays
# =====================================================================

