#!/usr/bin/perl

# ====================================================================
# The Vovida Software License, Version 1.0 
# 
# Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. The names "VOCAL", "Vovida Open Communication Application Library",
#    and "Vovida Open Communication Application Library (VOCAL)" must
#    not be used to endorse or promote products derived from this
#    software without prior written permission. For written
#    permission, please contact vocal\@vovida.org.
# 
# 4. Products derived from this software may not be called "VOCAL", nor
#    may "VOCAL" appear in their name, without prior written
#    permission of Vovida Networks, Inc.
# 
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
# NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
# NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
# IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
# 
# ====================================================================
# 
# This software consists of voluntary contributions made by Vovida
# Networks, Inc. and many individuals on behalf of Vovida Networks,
# Inc.  For more information on Vovida Networks, Inc., please see
# <http://www.vovida.org/>.

#  $Id: cApiBuilder,v 1.11 2000/08/12 01:24:57 bogawa Exp $

use strict 'subs';
#use strict 'getopts.pl';

@functions = ( ); # main data structure that holds all info about all functions

$doCode = 1; # flag to generate CXX file, otherwise do header file 

@inclues = {}; # holds allfiles to be included in generated output 

# parse the heasder information
for $arg ( @ARGV ) {
  &debug(" arg=",$arg,"\n");
  
  if ( $arg =~ /^-c/ ) { 
    $doCode = 1; 
  }
  
  if ( $arg =~ /^-h/ ) { 
    $doCode = 0; 
  }

  if ( $arg =~ /^-d/ ) { 
    $debugCode = 1; 
  }
  
  if ( $arg =~ /^-i/ ) { 
    # an file to include 
    $arg =~ s/^-i//;
    push @includes, $arg;
  }
}

#generate the file header info 
print <<EOP;
/* 
    This file was generated using cApiBuilder.  This filemust be
    considered a derivative work of the files from which this file was
    generated.
*/
EOP

# parse the input file 
while ( <STDIN> ) {
  # still need to deal with C style comments 
  if ( s/^\#include/\#include/g > 0 ) {
    print $_ if $doCode;
  } 
  else {
    chomp;
    $ignore = 1 if(/\/\/ API:ignore begin/) ;
    $ignore = 0 if(/\/\/ API:ignore end/) ;
    next if($ignore);
    s/:/ : /g;
    s/,/ , /g;
    s/\(/ \( /g;
    s/\)/ \) /g;
    s/\{/ \{ /g;
    s/\}/ \} /g;
    s/\;/ \; /g;
    s/\/\/.*$/ /g;
    s/\#.*$/ /g;
    s/^t/ /g;
    s/const/ /g;
    s/virtual/ /g;
    
    $data .= $_ . " ";
  }
}

# remvoe C Style comments
$data =~ s/\/\*.*?\*\///gm; 

# tokenize the data
@data = split( " " , $data );

$inClass = 0;
$inPublic = 0;

for( $i=0; $i < $#data; $i++ ) {

  if ( $data[$i] eq "class" ) {
    $className = $data[$i+1];
    if ( $data[$i+2] ne ";" ) {
      while ( $data[$i] ne "{" ) { $i++; } 
      $i++;
      $inClass = 1;
      $inPublic = 0;
      &debug("# in class " , $className , "\n");
    }
  }
   
  if ( $data[$i] eq "}" ) {
    $inClass = 0;
  }

  if ( ($data[$i] eq "public") and $inClass ) {
    while ( $data[$i] ne ":" ) { $i++; } 
    $i++;
    $inPublic = 1;
    &debug("# in public of " , $className , "\n");
  }

  if ( ($data[$i] eq "private") or ($data[$i] eq "protected") ) {
    while ( $data[$i] ne ":" ) { $i++; } 
    $i++;
    $inPublic = 0;
  }
    
  if ( ($data[$i] eq "(" ) and $inClass and $inPublic ) {
    #found a function 
    $functionName = $data[$i-1];
    if ( $functionName eq $className ) {
      $returnType = $className . "*";
      $functionName = "constructor";
    } 
    elsif ( $functionName eq ( "~" . $className ) ) {
      $returnType = "void";
      $functionName = "destructor";
    } 
    else {
      $staticFunction = ( $data[$i-3] eq "static");
      $returnType = $data[$i-2];
      $returnType = "int" if ($returnType eq ";");  
      $returnType = "int" if ($returnType eq ":");  
      $returnType = "int" if ($returnType eq "{");  
    }
    $i++;

    $rec = { };
    $rec->{name} = $functionName;
    $rec->{isStatic} = $staticFunction;
    $rec->{class} = $className;
    $rec->{return} = $returnType;
    &debug("#func ",$returnType," ",$className,"::",$functionName,"\n");

    $numParam = 0;
    # get the parameters
    PARAM: while ( 1 ) {
	last PARAM if ( $data[$i] eq ")" );

	#for ( $t=$i; $t<$i+3; $t++) { print "data[",$t,"]=",$data[$t],"\n"; }
	
	$type = $data[$i];
	$name = $data[$i+1];
	
	$name =~ s/=.*//g; # kill default paramters

	&debug("#    param ",$numParam," ",$type," ",$name,"\n");
	$rec->{"paramName",$numParam} = $name;
	$rec->{"paramType",$numParam} = $type;

	$numParam++;
	$rec->{numParam} = $numParam;

	$i = $i+2;
	
	&debug("data is ",$data[$i],"\n");
	
	last PARAM if ( $data[$i] eq ")" );
	
	if ( $data[$i] eq "," ) {
	  $i++;
	  next PARAM;
	}
	
	print STDERR "VERY CONFUSED\n";

	print "VERY CONFUSED\n";
	for ( $j = $i-10; $j < $i+10; $j++ ) {
	  print $data[$j]," ";
	}
	print "\n";
	
	last PARAM;
      }

    push @functions, $rec;

    # eat rest of function
    INLINEFUNC: while ( ($data[$i] ne ";") ) {
	
	if (($data[$i] eq "{") ) {
	  #Deal with inline function
	  $i++;
	  $depth = 1;
	  while ( $depth > 0 ) {
	    $depth++ if ($data[$i] eq "{");
	    $depth-- if ($data[$i] eq "}");
	    $i++;
	  }
	  
	  last INLINEFUNC;
	}
	
	$i++;
      }

  }    
  &debug($i,": ",$data[$i] , "\n");
}

# print the functions
if (0) {
  for $func ( @functions ) {
    print "static " if $func->{isStatic};
    print $func->{return}," ", $func->{class},"::",$func->{name},"(";
    for( $t=0; $t < $func->{numParam}; $t++) {
      print "," if ( $t != 0 );
      print $func->{"paramType",$t}," ",$func->{"paramName",$t};
    }
    print ");\n";
  }
}


%definedTypes = ( "void" => 1, 
		  "bool" => 1,
		  "char" => 1,
		  "int" => 1,
		  "long" => 1,
		  "float" => 1,
		  "double" => 1,
		  "string" => 1,
		  "String" => 1,
                  "RtpSeqNumber" => 1,
                  "RtcpHeader" => 1,
                  "RtcpReport" => 1,
                  "RtcpSDESItem" => 1,
                  "RtcpSDESType" => 1,
                  "RtcpSender" => 1,
                  "RtcpType" => 1,
                  "RtpHeader" => 1,
                  "RtpPayloadType" => 1,
                  "RtpSource" => 1,
                  "RtpTime" => 1,
                  "RtpSrc" => 1,
                  "u_int32_t" => 1
		);


sub addType ( $ ) {
  my $type = shift;
  &debug("adding type ",$type," -> ",&mapTypeToC($type),"\n");
  $_ = $type;
  s/&//g;
  s/\*//g;
  $_ = "builtin" if ( $definedTypes{$_} );
  $type = $_;
  $types{$type} = $type unless ( $type eq "builtin" );
}

sub mapTypeToC ( $ ) {
  $_ = shift;
  s/string&/char\*/g;
  s/string/char\*/g;
  s/&/\*/g;
  s/bool/int/g;
  my $type = $_;

  # try to fix template classes

  s/\<(\w*)\>/_Tmpl_$1_/;
  $type =~ s/\<(\w*)\>/_Tmpl_$1_/;

  s/Ptr$/\*/;
  s/\**//g;

  &debug("check defined for " , $_ , "\n");

  if ( $definedTypes{$_} ) {
    &debug("is defined\n");
    #$type .= "_isCDefined";
  }
  else {
   &debug("is not defined\n");
   $type =~ s/\*/Ptr/; # needs work for builtin types
#   $type .= "Ptr"; # needs work for builtin types

  }

  # try to fix template classes

  s/\<(\w*)\>/_Tmpl_$1_/;

#  s/^intPtr/int*/;
#  s/^longPtr/long*/;
#  s/^charPtr/char*/;
#  s/^shortPtr/short*/;
#  s/^floatPtr/float*/;
#  s/^doublePtr/double*/;
#  s/^voidPtr/void*/;
 
 $mapTypeToC = $type;
}


# compute all the type lookups 
for $func ( @functions ) {
  &addType( $func->{class} );
  &addType( $func->{return} );
  for( $t=0; $t < $func->{numParam}; $t++) {
    &addType( $func->{"paramType",$t} );
  }
}


# generate the file 
if (1) {

#  print "\n#include <cassert>\n\n" if $doCode;
  print "\n#include <assert.h>\n\n" if $doCode;

  for $include ( @includes ) {
    print "#include \"$include\" \n";
  }

  print "\n#ifdef __cplusplus\nextern \"C\" \n{\n#endif\n";

  print "\n\n";

  print " \/\* This file uses the types : \n";
  # build the type declarations 
  for $type ( sort keys %types ) {
    $type =~ s/\<(\w*)\>/_Tmpl_$1_/;
    print "typedef void* ",$type,"Ptr;\n";
  }
  print " */ \n ";
  print "\n\n";

  print "/* these function return a string describibg any */ \n";
  print "/* errors or NULL if there was no error */ \n\n";

  # build the prototypes 
  for $func ( @functions ) {
	
    if ( $func->{class} ne $prevClass ) {
      print "\n" ;
      $constructorCount=0;
    }
    $prevClass = $func->{class};
    
    if ( $func->{name} eq  "constructor" ) {
      if (1) {
	$constructorCount++;

	if ( $doCode ) {
	  print "\n/// See ",$func->{class},"::",$func->{class},"\n";
	}
	print "int ";
	print "\n" if $doCode;
	print $func->{class},"_create",$constructorCount;
	print "( ",&mapTypeToC($func->{class} . "*"),"* objPtr";
	for( $t=0; $t < $func->{numParam}; $t++) {
	  print ", ",&mapTypeToC($func->{"paramType",$t});
	  print " ",$func->{"paramName",$t};
	}
	print ")";
	print ";" unless $doCode;
	print "\n"; 
	if ( $doCode ) {
	  print "{\n";
	  print "   int retValue=0;\n";
	  print "   ",$func->{class},"* obj = NULL; \n";
	  print "   try\n";
	  print "   {\n";
	  print "       ";
	  print "obj = new ",$func->{class},"(";
	  for( $t=0; $t < $func->{numParam}; $t++) {
	    print "," if ( $t != 0 );
	    $type = $func->{"paramType",$t};
	    
	    if ( $type eq "string" ) {
	      print "string";
	      print "(",$func->{"paramName",$t},")";
	    }
	    elsif ( $type eq "string&" ) {
	      print "string";
	      print "(",$func->{"paramName",$t},")";
	    }
	    elsif ( $type =~ /.*&$/ ) {
	      # deal with a feverence type 
	      $type =~ s/&$//;
	      print "*static_cast<",$type,"*>";
	      print "(",$func->{"paramName",$t},")";
	    }
	    else {
	      print "static_cast<",$type,">";
	      print "(",$func->{"paramName",$t},")";
	    }
	  }
	  print ")";
	  print ";\n";
	  print "       assert(objPtr); \n";
	  print "       *objPtr = static_cast<",&mapTypeToC($func->{class} . "*"),">(obj); \n";
	  print "   }\n";
	  print "   catch (...)\n";
	  print "   {\n";
	  print "       retValue=-1; \n";
	  print "   }\n";
	  print "   \n";
	  print "   return retValue;\n";
	  print "}\n\n";
	}
      }
    }
    elsif( $func->{name} eq "destructor" ) {
      if (1) {
	if ( $doCode ) {
	  print "\n/// See ",$func->{class},"::~",$func->{class},"\n";
	}
	print "void ";
	print "\n" if $doCode;
	print $func->{class},"_free";
	print "( ",&mapTypeToC($func->{class} . "*")," objPtr";
	print ")";
	if ( $doCode ) {
	  print "\n{\n";
	  print "   assert(objPtr);\n";
	  print "   ",$func->{class},"* obj = ";
	  print "static_cast<",$func->{class},"*>(objPtr);\n";
	  print "   delete obj;\n";
	  print "}\n";
	}
	else {
	  print ";\n";
	}
      }
    }
    else { 
      if (1) {
	if ( $doCode ) {
	  print "\n/// See ",$func->{class},"::",$func->{name},"\n";
	}
	print "int ";
	print "\n" if $doCode;

	# deal with overloaded function names 
	$cName = $func->{class} . "_" . $func->{name};
	$count = 1;
	while ( $usedName{$cName} ) {
	  $count++;
	  $cName =  $func->{class} . "_" . $func->{name} . $count;
	}
	$usedName{$cName} = 1;

	print $cName;
	print "( ";

	# do the this pointer part 
	print &mapTypeToC($func->{class} . "*")," objPtr" unless $func->{isStatic};
	print ", " unless $func->{isStatic} or ( ($func->{numParam}==0) and (($func->{return} eq "void")) );

	# do the parametes 
	for( $t=0; $t < $func->{numParam}; $t++) {
	  print ", " unless ( $t == 0 );
	  print &mapTypeToC($func->{"paramType",$t});
	  print " ",$func->{"paramName",$t};
	}
	print ", " unless ($func->{return} eq "void")or($func->{numParam}==0);

	# do return value part 
	if ( $func->{return} ne "void" ) {
	  print &mapTypeToC($func->{return} . "*")," returnValue";
	} 

	# end the first line 
	print ")";
	print ";" unless $doCode;
	print "\n"; 
	if ( $doCode ) {
	  print "{\n";
	  print "   int retValue=0;\n";
	   if ( !($func->{isStatic}) ) {
	     print "   ",$func->{class},"* obj = ";
	     print "static_cast<",$func->{class},"*>(objPtr);\n";
	  }
	  print "   try\n";
	  print "   {\n";
	  print "       ";

	  # deal with the return type here 
	  if (  $func->{return} ne "void" ) {
	    print "*reinterpret_cast<",$func->{return},"*>(returnValue) = ";
	  }
	    
	  if ( $func->{isStatic} ) {
	    print "$func->{class}::";
	  }
	  else {
	    print "obj->";
	  }
	  print $func->{name},"(";
	  for( $t=0; $t < $func->{numParam}; $t++) {
	    print "," if ( $t != 0 );
	    $type = $func->{"paramType",$t};
	    
	    if ( $type eq "string" ) {
	      print "string";
	      print "(",$func->{"paramName",$t},")";
	    }
	    elsif ( $type eq "string&" ) {
	      print "string";
	      print "(",$func->{"paramName",$t},")";
	    }
	    elsif ( $type =~ /.*&$/ ) {
	      # deal with a reference type 
	      $type =~ s/&$//;
	      print "*static_cast<",$type,"*>";
	      print "(",$func->{"paramName",$t},")";
	    }
	    else {
	      print "static_cast<",$type,">";
	      print "(",$func->{"paramName",$t},")";
	    }
	  }
	  print ")";
	  print ";\n";
	  print "   }\n";
	  print "   catch (...)\n";
	  print "   {\n";
	  print "       retValue=-1; \n";
	  print "   }\n";
	  print "   \n";
	  print "   return retValue;\n";
	  print "}\n\n";
	}
      }
    }
  }

  print "\n#ifdef __cplusplus\n} /*close extern C*/ \n#endif\n";
}


sub debug {
    if($debugCode) {
	print STDERR @_;
    }
}


syntax highlighted by Code2HTML, v. 0.9.1