#!env perl


#
#  Projectstatus 0.5
#  -----------------
#
#   Documentation
#   =============
#
#
#  What is it?
#  projectstatus is a nice little script which searches a direcory  tree
#  consisting of projects and produces a status report in form of an
#  HTML-file of it. 
#
#  What provides it?
#  This script reads status-files which contain some informations. The main
#  part is Project, % Finnished, Status, TODO and Maintainer. It produces
#  of the Project's name a hierarchy and calculates every nodes % Finished
#  depending on the sub project's % Finished. This results in a global
#  % Finished in form of a HTML-based tree (File $DESTFILE).
#
#  How does it work?
#  This program searches the project tree for files with the name
#  $SRCFILE (s.d.). They are formatted in a certain pattern. If the 
#  script finds such a file, it reads it's content and pushes it into
#  a tree of hashes. It ddecides with the project's name which node of the
#  tree it will use. After it finishes that it starts computing the
#  % Finished fields. Every Project provides a finished field and so
#  The single nodes of the hash-tree get a computet %finished which results
#  in a global % finished.
#
#
#  How must I format the status-files?
#  The format of the status files (named as the variable $SRCFILE) is rather
#  simple. It consists of property lines. Every line starts with an identifier
#  plus a colon (:) and ends with the properties value. For example:
#  Maintainer: Some Person <new line>
#  The Property lines are delimited by new line marks <LF> or ACII 13.
#  For now it isn't possible to extend a property line to moer than
#  one file line. This is future. The following Properties are evaluated
#  and actually unterstood by the script:
#  Property          |  Content
#  Finished          | numeric integer 0..100 with or without %
#  Project           | Project name/path names delimited by . (i.e.  MainP.SubP.DetailP )
#  Maintainer        | freeform  may be   Name <email>
#  Status            | freeform  may be   (haltet|started|active...)
#  TODO              | freeform (may be changed in the future)
#
#  Example file:
#  ----------------8<-------------
#  Status: halted
#  Project: ROS.apps.gugu
#  Finished: 100%
#  TODO: there's nothing left
#  Maintainer: God 
#  -------------------------------
#
#  Variables to be used for configuration reasons:
$DESTFILE="index.html";         # Name of the files to gather the informatio from
$SRCFILE="status.txt";          # Name of the file to write the cumulated info to.
@DIRLIST= ( "." );              # List of directories to search for $DESTFILE's


use File::Find;


%projects=();

sub save_it
{
    my ($rhinfo, $ppath) = @_;
    $ppath = $rhinfo->{"Project"};
    #print "**$ppath*\n";
    #print "...$ppath..".$info{"Project"}."\n"; 
    @ppath=split(/\./,$ppath);

    
    my $c = \%projects;
    for ( @ppath[0..@ppath-2] ) 
    {
        $c->{$_} ||= {};
        $c = $c->{$_};
    }
    $c->{$ppath[-1]} = $rhinfo;
}



# reads each status-file
sub process_file 
{
    # do whatever;
    $fn = $File::Find::name;
    #print $fn."\n";
    if( $fn =~ /\/([^\/]+)$/ )
    {
	    #print $1."\n";
	    if ($1 eq $SRCFILE) 
	    {
		open(STATUSF, "<$SRCFILE") or die "Couldn't open $fn for reading: $!\n";
		$rhinfo = {".info"=>1 };
		while (<STATUSF>) 
		{
		#    print $_;
		    if( m/^\s*([^:]+):\s*(.*)$/ )
		    {
			$$rhinfo{$1}=$2;
	   #             print "$1 = $2\n";
		    }
		} 
		close(STATUSF);
		# Now that we have the files info, we can decide more about it
		if( $prj=$$rhinfo{"Project"} )
		{
		    save_it($rhinfo, $prj );  #insert %info in @projects at $prj
		}
	
	}
    }
    
    
}

#computes the average finished fields
# These will be named ~Finished:
# returns the average finished
sub compute # takes ref to a hash-tree
{
    my ($rh)=@_;
    
    # is it an info hash or a directory hash?
    if( $$rh{".info"} ) #a info hash
    {
        #print "Fin: .".$$rh{"Finished"}.".\n";
        $$rh{"Finished"} =~ s/%//;
        $x = $$rh{"Finished"};
        return (1, $x );
    }
    else #dir hash
    {
        my $sum=0, my $num=0;
        foreach $i (keys( %$rh ))
        {
            if( ref($$rh{$i}) )
            {    
                my ($rnum, $rsum) = compute( $$rh{$i} );
                $num += $rnum;
                $sum += $rsum;
            }
        }
        #if( $num>0 )
        {
            $$rh{".Finished"}=$sum/$num;        
            $$rh{".num"}=$num;        
            #$$rh{".sum"}=$sum;        
        }
        return ($num, $sum);
    }
    return -1;
}


sub gibaus
{
    my ($rh,$sp)=@_;
    # rwahat is a %
    foreach $i (keys( %$rh ))
    {
        print "  " x $sp;
        print "$i = $$rh{$i}\n";
        if( ref($$rh{$i}) )
        {    gibaus( $$rh{$i}, $sp+1 );
        }
    }
}

#generates the status bar
sub genstatus
{
    my ($percent)=@_;
    #$percent = 0+$percent;
    $red = sprintf "%02x", (1+($percent*2.55));
    $green = sprintf "%02x", (256-($percent*2.55));
    print $red." : $green\n";
    $styles='"border-width:2px;border-style:outset;margin:1px;'.
    'width:'.($percent*3).'px;background-color:#'.$red.$green.'99"';
    $ret="<div style=$styles>$percent%</div>";
    return $ret;
}

# produces an ourput file
sub produce_file
{
    open($DEST,">$DESTFILE") ||  die "Sorry, I couldn't create $DESTFILE.\n";
    #$FILE=STDOUT;
    $FILE = $DEST;
    print $FILE '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
    print $FILE '<html>  <head>    <title>Status of the Megaproject</title></head><body>';
    
    produce_file_rec( \%projects, 0, $FILE );
    
    print $FILE "</body></html>\n";
    close($DEST);
}

#recoursive part
sub produce_file_rec
{
    my ($rh,$sp,$FILE)=@_;
    my $i; my $rinfo;
    print $FILE "<ul>\n";
    foreach $i (keys( %$rh ))
    {
        if( ref($$rh{$i}) ) # do not try that with scalars
        {   
	    print $FILE "<li>";
	    $rinfo = $$rh{$i};
	    if( $rinfo->{".info"} ) 
	    {  # info hash
		print $FILE "<table border=\"1\">\n";
		print $FILE "<tr><td><strong>Project:</strong></td><td>".$rinfo->{"Project"}."</td></tr>\n"; 
		print $FILE "<tr><td><strong>Finished:</strong></td><td>".genstatus($rinfo->{"Finished"})."</td></tr>\n"; 
		print $FILE "<tr><td><strong>Status:</strong></td><td>".$rinfo->{"Status"}."</td></tr>\n"; 
		print $FILE "<tr><td><strong>Maintainer:</strong></td><td>".$rinfo->{"Maintainer"}."</td></tr>\n"; 
		print $FILE "<tr><td><strong>TODO:</strong></td><td>".$rinfo->{"TODO"}."</td></tr>\n"; 
	    }
	    else # dir hash
	    {	    
		print $FILE "<table border=\"1\">";
                print $FILE "<tr><td><strong>Projectpath:</strong></td><td>$i</td></tr>";
                print $FILE "<tr><td><strong>Finished:</strong></td><td>".genstatus($rinfo->{".Finished"})."</td></tr>";
                print $FILE "<tr><td><strong>Subprojects:</strong></td><td>".$rinfo->{".num"}."</td></tr>";
                print $FILE "</table>";
		produce_file_rec( $rinfo, $sp+1, $FILE );
	    }	
  	    print $FILE "</li>\n";
        }
    }
    print $FILE "</ul>\n";
}


# finds all files under a given dir.
sub start
{
    find(\&process_file, @DIRLIST);
}


start;
#gibaus  \%projects;
compute \%projects;
#gibaus  \%projects;
produce_file \%projects;




