#!/usr/bin/env perl
# Developed by Daan van Vugt, donated to TUBS under the zlib license
# Simple script to look for use dependencies in files given on stdin and print them
# use as:
# makedepend file_to_check.f90 DIR1 DIR2 DIR3 ...
# Add -t flag after makedepend to print it out in TUBS format
use strict;
use warnings;
use Getopt::Std;

our($opt_t);
if (!getopts('t')) {
    warn("Flags not read correctly");
}

my $filename = shift(@ARGV);
$filename =~ /([^\/]*)[.][f90|f]/;
my $basename = $1;
$filename =~ /(.*)\/[^\/]*[.][f90|f]/;
my $dirname = $1;
my $skip_modules = "hdf5 mpi mpi_f08 omp_lib iso_c_binding iso_fortran_env h5lt ieee_exceptions ieee_arithmetic";
my $include_headers = "version.h"; # only include this header file if found

sub uniq {
    my %seen;
    return grep { !$seen{$_}++ } @_;
}
# Print the dependencies of this file alone
sub print_dependencies {
    my $this_path = shift;
    open(my $fh, $this_path) or return;

    my $found_modules = "";
    my $to_check = "";
    my $target = "";
    if (defined $opt_t) {
        $target = $target . "$basename.o";
    } else {
        $target = $target . ".obj/$basename.o";
    }

    while (<$fh>) {

    # Test if this is a program
    if (/^\s*program\s*[a-z][a-z0-9_]*/i) {
        print "PROGRAM_SOURCES += $basename\n";
    }

    # modules defined in the current file (checked against later)
    if (/^\s*module\s*([a-z][a-z0-9_]*)/i) {
        $found_modules .= "$1 ";
        # If the module keyword is found add it as a target
        if (!defined $opt_t) {
            $target = $target . " .mod/$basename.mod";
        }
    # C preprocessor style includes (header files)
    } elsif (/^#\s*include\s*["<]([^">]*.h)[">]/) {
        my $header_name = $1;
        if ($include_headers =~ /\b$header_name\b/i) {
        print "$target: .mod/$header_name\n";
        }
    # C preprocessor style includes
    } elsif (/^#\s*include\s*["<]([^">]*)[">]/) {
        if ( -f "$dirname/$1" ) {
        print_dependencies("$dirname/$1");
        }
    # Fortran 90 "use"
    } elsif (/^\s*use\s+(\w+)/i) {
        unless ($skip_modules =~ /\b$1\b/i or $found_modules =~ /\b$1\b/i) {
            if (defined $opt_t) {
                print "$target: $1.mod\n"
            } else {
                print "$target: .mod/$1.mod\n";
            }
        }
    # Subroutine calls
    } elsif (/call\s*([a-z][a-z0-9_]*)/i) {
        unless ($to_check =~ /\b$1\b/i) {
        $to_check .= "$1 ";
        }
    # bind(C) function declarations
    } elsif (/^\s*(function|subroutine)\s*([a-z][a-z0-9_]*)[(]?.*[)]?\s*bind[(]C/i) {
        unless ($to_check =~ /\b$2\b/i) {
        $to_check .= "$2 ";
        }
    } else {
        # function calls
        while (/^[^!']*external\s*:?:?\s*([a-z][a-z0-9_]*,?\s*)+/gi) {
        unless ($to_check =~ /\b$1\b/i) {
            $to_check .= "$1 ";
        }
        }
    }
    }

    # Perform some known translations
    $to_check =~ s/\blplot6\b/ppplib/gi;
    $to_check =~ s/\blplot\b/ppplib/gi;
    $to_check =~ s/\bpplot\b/ppplib/gi;
    $to_check =~ s/\bbegplt\b/ppplib/gi;
    $to_check =~ s/\bdrive_dgmres\b/dPackgmres/gi;
    $to_check =~ s/\bdgmres\b/dPackgmres/gi;
    $to_check =~ s/\binit_dgmres\b/dPackgmres/gi;
    $to_check =~ s/\bpcg32_srandom_r\b/pcg_basic/gi;
    $to_check =~ s/\bpcg32_random_double_r\b/pcg_basic/gi;

    # Do not check omp_ or mpi_ functions
    $to_check =~ s/\bomp_[^ ]*\b//gi;
    $to_check =~ s/\bmpi_[^ ]*\b//gi;

    # Create an array with all names to search
    my @names = split ' ', $to_check;

    # Check for files with the same name as any subroutines
    my $cmd = "find @ARGV -maxdepth 1 ";
    for (uniq @names) {
    $cmd .= "-iname '$_.f90' -or -iname '$_.f' -or -iname '$_.c' -or -iname '$_.cpp' -or ";
    }
    $cmd .= '-name "nomatchlistend"';
    my $targets=`$cmd`;
    for (split "\n", $targets) {
    $_ =~ /([^\/]*)[.](f90|f|c)?/i;
    unless ($basename eq $1) {
        # Add the dependency on the output file, not the object file
        # This will be picked up by obj_deps.sh and be used correctly while avoiding
        # problems with dependency loops.
        print "$basename: .obj/$1.o\n";
    }
    }

    if (defined $opt_t) {
        if ($this_path =~ /([^\/]*)[.][f90|f]/) {
          print "FORT_MOD_DEPEND_$this_path\n";
      }
    }

}

# Read from filename, or stdin if - is the second argument
if(defined $ARGV[0] and $ARGV[0] eq "-"){
    shift(@ARGV);
    print_dependencies("/dev/stdin");
} else {
    print_dependencies($filename);
}
