#!/usr/bin/perl -w

use strict;

{
    my $maxdepth = 2; # default

    if ($#ARGV > -1 && $ARGV[0] eq "-h") {
       print STDERR "usage: $0 [-d maxdepth] [target subroutine(s)]\n";
       print STDERR "       builds the dependency tree of f90 files - run from the QE root directory\n";
       print STDERR "       defaults: maxdepth=2, targets=all f90 files under the current directory\n";
       exit 0;
    }

    if ($#ARGV > -1 && $ARGV[0] eq "-d")
    {
        if ($#ARGV < 1)
	{
	    print STDERR "error: maxdepth missing\n";
	    print STDERR "usage: $0 [-d maxdepth] [targets]\n";
	    exit 1;
	}
	$maxdepth = $ARGV[1];
	if ($maxdepth !~ /^\d+$/)
	{
	    print STDERR "error: non-numeric maxdepth\n";
	    print STDERR "usage: $0 [-d maxdepth] [targets]\n";
	    exit 1;
	}
	shift @ARGV;
	shift @ARGV;
    }

    my $basedir = $0;
    # Orig: the following is the directory where this script is
    # $basedir =~ s/(.*)\/.*/$1/;
    # the following works only if we run the script from QE root
    $basedir = "./";
    my @sources = split(/ /, `echo $basedir/*/*.f90 $basedir/*/*/*.f90`);

    # grab program, function and subroutine declarations
    my (%place, %fname, %pname);
    foreach my $file (@sources)
    {
	open(IN, "$file");
	while (<IN>)
	{
	    $_ = "\L$_"; # cast everything to lowercase
	    if (/^[^!'""']*\bfunction\s+(\w+)/o && ! /^\s*end\s+function\b/o)
	    {
		$fname{$1} = 1;
		push_place(\%place, $1, $file);
	    }
	    elsif (/^\s*program\s+(\w+)/o)
	    {
		$pname{$1} = 1;
		push_place(\%place, $1, $file);
	    }
	    elsif (/^\s*(?:(?:pure|recursive)\s+)?subroutine\s+(\w+)/o)
	    {
		push_place(\%place, $1, $file);
	    }
	}
	close(IN);
    }
    my @names = sort keys %place;
    my @functions = sort keys %fname;

    # if no arguments are specified, stat all programs
    my @targets = @ARGV;
    if ($#targets < 0)
    {
	@targets = sort keys %pname;
    }

    my %cache;
    foreach my $name (@targets)
    {
	stat_name($name, \%place, \@functions, \%cache, 0, "", $maxdepth);
    }
}

sub push_place
{
    my ($place, $name, $file) = @_;
    if (defined $$place{$name})
    {
	$$place{$name} = "$$place{$name} $file";
    }
    else
    {
	$$place{$name} = "$file";
    }
}

sub stat_name
{
    my ($name, $place, $functions, $cache, $depth, $indent, $maxdepth) = @_;
    print "$indent$name\n";

    if ($depth >= $maxdepth || ! defined $$place{$name})
    {
	return;
    }

    if (! defined $$cache{$name})
    {
	my %cname;
	my @files = split(/ /, $$place{$name});
	foreach my $file (@files)
	{
	    my $current = "";
	    open(IN, $file);
	    while (<IN>)
	    {
		$_ = "\L$_";
		if (/^\s*program\s+(\w+)/o)
		{
		    $current = "$1";
		}
		elsif (/^\s*(?:(?:pure|recursive)\s+)?subroutine\s+(\w+)/o)
		{
		    $current = "$1";
		}
		elsif (/^[^!'""']*\bfunction\s+(\w+)/o)
		{
		    $current = "$1";
		}
		# here we are inside the relevant program/subroutine/function
		elsif ($current eq $name)
		{
		    # subroutine calls
		    if (/^\s*call\s+(\w+)/o)
		    {
			$cname{$1} = 1;
		    }
		    # function calls
		    foreach my $fun (@$functions)
		    {
			if (/^[^!'""']*\b$fun\b/)
			{
			    $cname{$fun} = 1;
			}
		    }
		}
	    }
	    close(IN);
	}
	my @calls = sort keys %cname;
	$$cache{$name} = \@calls;
    }

    foreach my $call (@{$$cache{$name}})
    {
	if ($call ne $name)
	{
	    stat_name($call, $place, $functions, $cache,
		      $depth+1, "  $indent", $maxdepth);
	}
    }
}
