#!perl -w

######################
#
# fdwFilter
#
# This is an input filter for doxygen that converts the darwin 
# comments to to doxygen tags.
# 
#    //--------------------------------------------------------
#    //  This is a darwin-style multi-line
#    //  comment block.
#   //--------------------------------------------------------
# to the C-style that doxygen supports:
#
#    /*!
#       This is a Qt-style multi-line
#       comment.
#     */
#
# For this to work as a doxygen input filter (INPUT_FILTER in the
# doxygen.config) file, it must read from a file named on the command
# line and write to stdout.
#
######################

use strict;
use File::Basename;

my $Cmd = basename($0);

my $usage = <<ENDUSAGE;
Usage: $Cmd inputfile > outputfile

    Input filter for doxygen 
ENDUSAGE

# Make sure there is one argument: the name of the file
if ($#ARGV != 0) {
    print $usage;
    exit;
}

my $incomment = 0;
my $supported = 0;
my @commentBlock=();
my $fileheader=0;
my $functionheader=0;
my @functionBlock=();
my @fileBlock=();
my @functionProto=();

while (<>) {
    print;      #just echo the original lines

    if ($incomment==0) {
        # Check for the start of a comment: "//----". If we get one, save it
        if (m|^\s*//----|) {
	        $incomment = 1;
                next;
        }
	if (@functionBlock) {    # do we have a functionblock recorded?
		push @functionProto, $_; # assume the function proto follows
		next unless m|;|;# collect lines until semi-colon (;)		
	}
    }  
    else { # commentblock active  $incomment==1
         unless (m|^\s*//|) {
             resetGlobals();     # reset to start looking for the next block
             next;
         }
	           
         s|//*||;      # remove //
         # check for supported header types
         $fileheader=1 if ( m|FILE NAME:|);
         $functionheader=1 if ( m|FUNCTION:|);
         
         $supported = 1 if ($fileheader==1 || $functionheader==1);
         unless ($supported) {
             resetGlobals();     # reset to start looking for the next block
             next;
         }
         push @commentBlock, $_; # record the comment lines
	 if (m|^\s*----|) {      # this should be end of block (removed //)
	        $incomment = 0;
		pop @commentBlock; # remove the -----bar
         }
         else {
                 next;           # keep on going 
         }

         # a functionheader will be followed by the prototype if this is a
         # .h file, but first pick up this comment section
         @functionBlock=getFunctionHeader(@commentBlock) if ($functionheader);
         @fileBlock=getFileHeader(@commentBlock)         if ($fileheader);
         
     }
     if (@fileBlock) {
        print "\n/\*!\n".join("",@fileBlock)."\n*/\n";
        @fileBlock=();
        resetGlobals();
        }
     if (@functionProto) {        
        print "\n/\*!\n".join("",getFunctionProto(@functionProto));         
        @functionProto=();
        print join("",@functionBlock)."\n*/\n";
        @functionBlock=();
        resetGlobals();
        }
        
}

#
# process the labels from the commentBlock list
#         get the function proto and parameter descriptions
#
# tags conversion in @commentBlock:
#           in tm*.h    doxygen tag     note
#           FUNCTION    /brief          holds function name, use for check?
#        DESCRIPTION    details         =remaining text in block
#             RETURN    /return {txt}
#             NOTES     /remarks
#
#          FILE NAME    /file
#        DESCRIPTION    /brief
#  
#  functionProto:
#             proto     /fn             without params description
#   params:
#                 I:    /param          Input:
#                 o:    /param          Output:



sub getFunctionProto {
   my $tag;
   my $proto;

   my @lines=@_;   
   my @fnlines=();
   my @params=();
   my @paramsinfo=();
   my @paramnames=();
   my @returnlines=();
   
   #print "function\n";
   #print join ("~",@lines);
   
   # get the commented parameter lines
   foreach $_  (@lines){  	# process lines from the start
	    next if m|^//|;	# skip leading comments
            s|^\s+||;   	# strip leading whitespace
	    chop;		# cut off the \n
            if (m|(.*)//(.+)|) {# look for parameter description
            $_=$1;              # remember code part       
            push (@paramsinfo, $2); # save the param
            }
            push (@fnlines,$_); # save the proto part part  
   }
    # now pick up the function prototype     
    $_  = join (" ", @fnlines); # now it's 1 line
    $_ =~ s/\s+/ /g;		# remove multiple spaces
    if ( m|(\w+)\s+(\w+)\s+\((.*)\);|) { # function: type name([params])
            push @returnlines,"\\fn $1 $2 ($3)\n"; 
	    @params = split ',',$3;	  # the args 
    } else {
    	    print STDERR "function proto missing\n";
    }
   
    # only use the parameter name (one word allowed in \param)
    foreach $_ (@params) {
    	s|\w+||; # kill type word
	push @paramnames, $_;
	}
    while ($_= shift(@paramsinfo)){  # process lines from the start
             s|^\s+||;   	# strip leading whitespace
   # and stick the params to it
   	     $tag="\\param <".shift(@paramnames).">";  # these MUST have same sequence
	     if ( m|I:\s+(.*)|) { 
                push @returnlines, "$tag Input: $1\n";
                next;
             }
             if ( m|O:\s+(.*)|) { 
                push @returnlines, "$tag Output: $1\n";
                next;
             }
             if ( m|IO:\s+(.*)|) { 
                push @returnlines, "$tag Input/Output: $1\n";
                next;
             }
   }

   return @returnlines;

}

sub getFunctionHeader {
   my $lines=join("",@_);
   my ($function,$description,$return,$notes);
   my @retval=();
   my @returnlist=();
   my $val;
   
   # replace empty lines, =multiple eof's
   $lines =~ s|\n+|\n|g;
   # replace empty whitespace, tabs etc by single space
   $lines =~ s| +| |g;
   # remove comment line bars ---------
   $lines =~ s|^--+\n||g;
   # replace the end-of-lines by \n, (? can't get multiline matches to work?)
   $lines =~ s|\n|\\n|g;
   if ( $lines =~ m|FUNCTION:(.*)DESCRIPTION:(.*)RETURN:(.*)NOTES:(.*)|) {
     $function = $1;
     $description = $2;
     @retval = fixRetval ($3);
     $return = shift @retval;	# this is the return descr. line
     $notes = $4;
     # process the return list
     push @returnlist, "\\brief $function\n";
     push @returnlist, "$description\n\n";  # brief stops at empty line
      push @returnlist, "\\return $return\n";
     foreach $val (@retval) {
          push @returnlist, "\\retval $val\n";
     }
     push @returnlist, "\\remark $notes\n";
   }
    return @returnlist;
}

# a function description could look like this
#
#tmErrorCode_t: Status of operation
# and some more
#TM_OK - succesful
#TMHW_ERR_UART_BAD_UNIT_NUM - invalid device
#   maybe this line too
#TMHW_ERR_UART_BAD_PARAMETER - bad parameter
#TMHW_ERR_UART_NOT_SUPPORTED - not supported
sub fixRetval {
    my $in = shift @_;
    my @retval=();
    my @lines=();
    my $line;
    my $prevline;
    
    $in =~ s/\\n/\n/g;		# make it real eol again
    @lines = split ('\n',$in);	# list of retval lines + return: line
    
    foreach $line (@lines) {    
    	if ( $line =~ m/\w+:/)	{	# 1st is return type description
		push @retval, $line; 
		next;
	}
	if ( $line =~ m/[A-Z0-9_] - /) { # return value description
		push @retval, $line; 
		next;
	}
	# anything else is a continuation of the previous item so
	# just stick it to the end of the previous one
	$prevline = pop @retval;
	push @retval, $prevline.$line;
    }
    return @retval;

}
sub getFunctionHeaderw {
   my @lines=@_;
   my @detaillines=();
   my @textlines=();
   my $tag="none";
   
   #print "file\n";
   #print join ("+",@lines);

   while ($_= shift(@lines)){  	# process lines from the start
             next if (m|---------|);	# ignore ----- lines
             s|^\s+||;   	# strip leading whitespace
             if ( m|FUNCTION:\s+(.*)|) {
                $tag="\\brief";
                push @textlines, "$tag $1\n";
                next;
             }
             if (m|DESCRIPTION:\s+(.*)|) {
                $tag="details";
                push @detaillines, "$1\n";
                next;
             }
             if ($tag =~ m|details|) {
                push @detaillines, $_ if $_;
                next;
             }
             if (m|NOTES:\s+(.*)|) { 
                $tag="\\remarks";
                push @textlines, "$tag $1\n";
                next;
             }
             if (m|RETURN:\s+(.*)|) { 
                $tag="\\return";
                push @textlines, "$tag $1\n";
                next;
             }
 	     
   }
   push @textlines, @detaillines;
   return @textlines;
}

sub getFileHeader {
   my @lines=@_;
   my @detaillines=();
   my @textlines=();
   my $tag;
   
   #print "file\n";
   #print join ("+",@lines);

   while ($_= shift(@lines)){  	# process lines from the start
             s|^\s+||;   	# strip leading whitespace
             if ( m|FILE NAME:\s+(.*)|) {
                $tag="\\file";
                push @textlines, "$tag $1\n";
                next;
             }
             if (m|DESCRIPTION:\s+(.*)|) {
                $tag="details";
                push @detaillines, $1;
                next;
             }
             if (m|NOTES:\s+(.*)|) { 
                $tag="\\remarks";
                push @textlines, "$tag $1\n";
                next;
             }
             if (m|DOCUMENT REF:\s+(.*)|) { 
                next;
             }
	     next if (m|---------|);	# ignore ----- lines
 	 #    if (m|(.*)|){ 		# untagged line	
	#	push @detaillines, $1 if $tag =~ "details";
	#	push @textlines, $1 if $tag =~ "/remarks";
	 #    }
    }

   push  @textlines, @detaillines;
   return @textlines;
}
#
# reset all the variables 
#       
sub resetGlobals {
             $incomment=0;
             @commentBlock=();
             $fileheader=0;
             $functionheader=0;
}
        

