#
# Generate glue to hook C functions to tinyPy static classes
# returnvalue FUNC param
#       param [int, float, void, str, couples]
#                        str = const char *
#                        couples =  confCouple *c
#
# If we deal with instance, the method must be set through set/get as metadata
#


use strict;
my $input;
my $output;
my $headerFile;
my $line;
my $glueprefix="zzpy_";
my $functionPrefix="";
my $className;
my $cookieName;
my $cookieId;
my $staticClass=0;
#
my %cFuncs;
my %rType;
my %funcParams;
my %helpParams;
my @ctorParams;
my @ctorParamsWithNames;
#
# Variables
my %getVar;
my %setVar;
my %typeVars;

sub debug
{
        my $str=shift;
        chomp($str);
#        print OUTPUT "jsLog(\"$str\\n\");\n";

}
#
# processClass
#
sub processClass
{
        my $proto=shift;

        $proto=~s%.*\*/%%g;
        $proto=~s/ //g;
        #print "**$proto**\n";
        ($className,$cookieName,$cookieId)=split ":",$proto;
        print "Processing class $className (with cookie=$cookieName,id=$cookieId)\n";
        if($cookieName=~m/void/)
        {
                $staticClass=1;
        }
        else
        {
                $staticClass=0;
        }

}
#
# Process the constructor list
#
sub processCTOR
{
        my $proto=shift;
        my $args=$proto;
        my @params;
        my $retType=$proto;;
        my $pyfunc=$proto;;
        my $cfunc;
        my @params;
        my @paramsWithName;
        $args=~s/^.*\(//g;
        $args=~s/\).*$//g;
        @paramsWithName=split ",",$args;
        foreach (@paramsWithName)
        {
            #remove leading spaces
            $_=~ s/^\s+//;
        }
        foreach my $par (@paramsWithName)
        {
            my $par2 = $par;
            #remove all after first space
            $par2=~s/ .*//g;
            push(@params, $par2);
        }
        
        #print "args => $args\n";
        #@ctorParams=split ",",$args;
        #print " $pyfunc -> @params \n";
        
        @ctorParams=@params;
        @ctorParamsWithNames=@paramsWithName;
}
#
# Process a function declaration and write the glue code to do tinypy<->function call
#
sub processMETHOD
{
        my $proto=shift;
        my $args=$proto;
        my @params;
        my @paramsWithName;
        my $retType=$proto;;
        my $pyfunc=$proto;;
        my $cfunc;
        $args=~s/^.*\(//g;
        $args=~s/\).*$//g;
        @paramsWithName=split ",",$args;
        foreach (@paramsWithName)
        {
            #remove leading spaces
            $_=~ s/^\s+//;
        }
        foreach my $par (@paramsWithName)
        {
            my $par2 = $par;
            #remove all after first space
            $par2=~s/ .*//g;
            push(@params, $par2);
        }
        ####$args=~s/ *//g;
        #print "args => $args\n";
        ####@params=split ",",$args;
        #print "params -> @params\n";
        # Get return type...
        $retType=~s/^ *//g;
        $retType=~s/ .*$//g;
        # get functioName
        $pyfunc=~s/ *\(.*$//g;
        $pyfunc=~s/^.* //g;
        ($cfunc,$pyfunc)=split ":",$pyfunc;
        $cFuncs{$pyfunc}=$cfunc;
        $rType{$pyfunc}=$retType;

        if ($staticClass == 1 && $cfunc !~ /editor->/)
        {
            if (@params[0] =~ m/^void$/)
            {
                @params = ();
            }

            unshift @params, "IEditor";
        }

        if (@paramsWithName[0] =~ m/^void$/)
        {
            @paramsWithName = ();
        }
        push @{$funcParams{$pyfunc}}, @params;
        push @{$helpParams{$pyfunc}}, @paramsWithName;
        #print " $pyfunc -> @params \n";
}

#
# Process a  STATICVAR :/* STATICVAR */ int    audioResample:scriptSetResample,scriptGetResample
#
sub processStaticVar
{
        my $proto=shift;
        my $args=$proto;
        my $type;
        my $varname;
        my @left;
        my @funcs;


        $args=~s/^.*\*\///g; # Remove comment

        @left=split(":",$args);
        @funcs=split(",",$left[1]);
        #print " >> ".$left[0]."<<\n";
        ($type,$varname)=split(" ",$left[0]);

        $type=~s/ //g;
        $varname=~s/ //g;

        #print "<$type> * <$varname> * <@funcs> *\n";
        $typeVars{$varname}=$type;
        $setVar{$varname}=$funcs[0];
        $getVar{$varname}=$funcs[1];
        print "Processing variable $varname of type $type\n";
}


#
# parsefile
#
sub parseFile
{
my $file=shift;
open(INPUT,$file) or die("Cannot open $file");
while($line=<INPUT>)
{
        chomp($line);
        if($line=~m/^#/)
        {
        }
        else
        {
            if($line=~m/\* METHOD \*/)
            {
                my $proto=$line;
                # Remove header...
                # Remove tail
                $line=~s/^.*\*\///g;
                $line=~s/\/\/.*$//g;
                processMETHOD($line);

            } elsif($line =~m/\/* CLASS \*/)
                {
                        processClass($line);
                }
                elsif($line =~m/\/* ATTRIBUTES \*/)
                {
                        processStaticVar($line);
                }
                elsif($line=~m/\/* CTOR \*/)
                {
                        processCTOR($line);
                }
}
}
close(INPUT);
}
#
#
#
sub genReturn
{
        my $retType=shift;
        if($retType=~m/int/)
        {
                return "  return tp_number(r);";
        }
        if($retType=~m/double/)
        {
                return "  return tp_number(r);";
        }
        if($retType=~m/float/)
        {
                return "  return tp_number(r);";
        }
        if($retType=~m/void/)
        {
                return "  return tp_None;";
        }
         if($retType=~m/str/)
        {
                return "  if(!r) return tp_None;\n
  tp_obj o = tp_string_copy(tp, r, strlen(r));
  ADM_dealloc(r);
  return o;";
        }
return "???? $retType";
}
#
#
#
sub castFrom
{
        my $type=shift;
        if($type=~m/^str/)
        {
                return "pm.asString()";
        }
        if($type=~m/^int/)
        {
                return "pm.asInt()";
        }
        if($type=~m/^float/)
        {
                return "pm.asFloat()";
        }
        if($type=~m/^ptr/)
        {
                my $ptr;
                my $obj;
                ($ptr,$obj)=split "@",$type;
                return "( $obj *)pm.asObjectPointer()";
        }
        if($type=~m/^double/)
        {
                return "pm.asDouble()";
        }
        if($type=~m/^IEditor/)
        {
            return "editor";
        }
        print ">>>>>>>>>> <$type> unknown\n";
        return "????";

}
#
# Generate the locals / wrapping for the parameters
#
sub genParam
{
        my $num=shift;
        my $type=shift;
        my $r=1;
        $type=~s/ *$//g;
        $type=~s/^ *//g;
        if($type=~m/couples/)
        {
                print OUTPUT "  CONFcouple *p".$num." = NULL;\n";
                print OUTPUT "  pm.makeCouples(&p".$num.");\n";
                $r=0;
        }else
        {
                my $regular=1;
                if($type=~m/^str/)
                {
                        print OUTPUT "  const char *p".$num." = ";
                        $regular=0;
                }
                if($type=~m/^ptr/)
                {
                        $regular=0;
                        my $ptr;
                        my $obj;
                        ($ptr,$obj)=split "@",$type;

                        print OUTPUT "  $obj *p".$num." = ";

                }
                if($type =~ m/^IEditor/)
                {
                        print OUTPUT "  $type *p".$num." = ";
                        $regular=0;
                }
                if($regular==1)
                {
                        print OUTPUT "  $type p".$num." = ";
                }
                print OUTPUT castFrom($type);
                print OUTPUT ";\n";
        }
        return $r;
}
#
# Generate getter and setter glue
#
sub genGetSet
{
#
# Generate get/set
#
        my $i;
        my $f;
        my $setName=$glueprefix."_".$className."_set";
        my $getName=$glueprefix."_".$className."_get";
# Get
        print OUTPUT "tp_obj ".$getName."(tp_vm *vm)\n";
        print OUTPUT "{\n";
        debug("$getName invoked\n");
        print OUTPUT "  tp_obj self = tp_getraw(vm);\n";
        print OUTPUT "  IScriptEngine *engine = (IScriptEngine*)tp_get(vm, vm->builtins, tp_string(\"userdata\")).data.val;\n";
        print OUTPUT "  IEditor *editor = engine->editor();\n";
        print OUTPUT "  TinyParams pm(vm);\n";
        print OUTPUT "  $cookieName *me = ($cookieName *)pm.asThis(&self, $cookieId);\n";
        print OUTPUT "  char const *key = pm.asString();\n";
        my @k=sort keys %typeVars;
        my $v;
        foreach $v (@k)
        {
            print OUTPUT "  if (!strcmp(key, \"$v\"))\n";
            print OUTPUT "  {\n";

            if($staticClass==1)
            {
                if($typeVars{$v}=~m/^str/)
                {
                    print OUTPUT "     return tp_string(".$getVar{$v}."(";
                }
                else
                {
                    print OUTPUT "     return tp_number(".$getVar{$v}."(";
                }

                if ($getVar{$v} !~ m/^editor/)
                {
                    print OUTPUT "editor";
                }

                print OUTPUT "));\n";
            }
            else
            {
                print OUTPUT "     if(!me) pm.raise(\"$className:No this!\");\n";
                if($typeVars{$v}=~m/^str/)
                {
                    print OUTPUT "     return tp_string(me->".$getVar{$v}."());\n";
                }
                else
                {
                    print OUTPUT "     return tp_number(me->".$getVar{$v}."());\n";
                }
                
            }

            print OUTPUT "  }\n";
        }
        #
        # Functions are part of it...
        #
        my $cfunk;
        foreach $f( sort keys %cFuncs)
        {
                my $pyFunc=$f;
                $cfunk=$glueprefix.$f;
                print OUTPUT "  if (!strcmp(key, \"$pyFunc\"))\n";
                print OUTPUT "  {\n";
                print OUTPUT "    return tp_method(vm, self, ".$cfunk.");\n";
                print OUTPUT "  }\n";

        }
#print OUTPUT "  pm.raise(\"No such attribute %s\",key);\n";
        print OUTPUT "  return tp_get(vm, self, tp_string(key));\n";
        print OUTPUT "}\n";

# Set
        print OUTPUT "tp_obj ".$setName."(tp_vm *vm)\n";
        print OUTPUT "{\n";
        debug("$setName invoked\n");
        print OUTPUT "  tp_obj self = tp_getraw(vm);\n";
        print OUTPUT "  IScriptEngine *engine = (IScriptEngine*)tp_get(vm, vm->builtins, tp_string(\"userdata\")).data.val;\n";
        print OUTPUT "  IEditor *editor = engine->editor();\n";
        print OUTPUT "  TinyParams pm(vm);\n";
        print OUTPUT "  $cookieName *me = ($cookieName *)pm.asThis(&self, $cookieId);\n";
        print OUTPUT "  char const *key = pm.asString();\n";
        my @k=sort keys %typeVars;
        my $v;
        foreach $v (@k)
        {
          print OUTPUT "  if (!strcmp(key, \"$v\"))\n";
          print OUTPUT "  {\n";
          if($staticClass==1)
          {
                if($typeVars{$v}=~m/^str/)
                {
                    print OUTPUT "     const char * val = ".castFrom($typeVars{$v}).";\n";
                }
                else
                {
                    print OUTPUT "     ".$typeVars{$v}." val = ".castFrom($typeVars{$v}).";\n";
                }
                print OUTPUT "     ".$setVar{$v}."(";

                if ($setVar{$v} !~ m/^editor/)
                {
                    print OUTPUT "editor, ";
                }

                print OUTPUT "val);\n";
                print OUTPUT "     return tp_None;\n";
          }else
          {
                print OUTPUT "     if(!me) pm.raise(\"$className:No this!\");\n";
                if($typeVars{$v}=~m/^str/)
                {
                    print OUTPUT "     const char * val = ".castFrom($typeVars{$v}).";\n";
                }
                else
                {
                    print OUTPUT "     ".$typeVars{$v}." val = ".castFrom($typeVars{$v}).";\n";
                }
                print OUTPUT "     me->".$setVar{$v}."(val);\n";
                print OUTPUT "     return tp_None;\n";

         }
          print OUTPUT "  }\n";
        }
    #if (!strcmp(key, "prize")) return tp_number(fruit->prize);
        #print OUTPUT "  pm.raise(\"No such attribute %s\",key);\n";
        print OUTPUT "  return tp_None;\n";
        print OUTPUT "}\n";


}
#
# Gen glue
#
sub genGlue
{
#
#  Generate function call glue
#
#
#
#
        my $i;
        my $f;
        foreach $f( sort keys %cFuncs)
        {
                my @params=@{$funcParams{$f}};
                my $pyFunc=$f;
                my $cfunc=$cFuncs{$f};
                my $ret=$rType{$f};
                my $nb=scalar(@params);

                print "Generating $pyFunc -> $ret $cfunc(@params)\n";
                print OUTPUT "// $pyFunc -> $ret $cfunc(@params)\n";
                # start our function
                print OUTPUT "static tp_obj ".$glueprefix.$f."(TP)\n{\n";

                debug("$f invoked\n");
                 print OUTPUT "  tp_obj self = tp_getraw(tp);\n";  # We need a this as we only deal with instance!

                if($staticClass == 1)
                {
                    print OUTPUT "  IScriptEngine *engine = (IScriptEngine*)tp_get(tp, tp->builtins, tp_string(\"userdata\")).data.val;\n";
                    print OUTPUT "  IEditor *editor = engine->editor();\n";
                }

                 print OUTPUT "  TinyParams pm(tp);\n";
                 print OUTPUT "  $cookieName *me = ($cookieName *)pm.asThis(&self, $cookieId);\n\n";

                if($params[0]=~m/^void$/)
                {
                        $nb=0;
                }
                # unmarshall params...
                if($nb)
                {
                         for($i=0;$i<$nb;$i++)
                         {
                                 genParam($i,$params[$i]);
                         }
                }
                # call function
                if(!($ret=~m/^void$/))
                {
                        if($ret=~m/str/)
                        {
                                print OUTPUT "  char *r = ";
                        }else
                        {
                                print OUTPUT "  ".$ret." r = ";
                        }
                }else
                {
                        print OUTPUT "  ";
                }
                if($staticClass==1)
                {
                        print OUTPUT $functionPrefix.$cfunc."(";
                }else
                {
                        print OUTPUT "me->".$functionPrefix.$cfunc."(";
                }
                for($i=0;$i<$nb;$i++)
                {
                        if($i)
                        {
                                print OUTPUT ", ";
                        }
                        print OUTPUT "p".$i;
                }
                print OUTPUT ");\n";
                # return value (if any)
                print OUTPUT genReturn($ret);
                print OUTPUT "\n}\n";

        }
        genGetSet();

}
#
#
#
sub genCtorDtor
{
   my $dtor=  "myDtor".$className;
# Dtor
        print OUTPUT "// Dctor\n";
        print OUTPUT "static void ".$dtor."(tp_vm *vm,tp_obj self)\n";
        print OUTPUT "{\n";
        debug("$dtor invoked\n");
        if($staticClass==1)
        {
                # no cookie, so no need to destroy
        }else
        {
                print OUTPUT "  $cookieName *cookie = ($cookieName *)self.data.val;\n";
                print OUTPUT "  if (cookie) delete cookie;\n";
                print OUTPUT "  self.data.val = NULL;\n";
        }
        print OUTPUT "}\n";
        # end
#
# Ctor
#
        # ctor
        print OUTPUT "// Ctor (@ctorParams)\n";
        print OUTPUT "static tp_obj myCtor".$className."(tp_vm *vm)\n";
        print OUTPUT "{\n";
        debug("ctor of $className invoked\n");
        print OUTPUT "  tp_obj self = tp_getraw(vm);\n";
        print OUTPUT "  TinyParams pm(vm);\n";

        if($staticClass==1)
        {
                print OUTPUT "  void *me = NULL;\n";
        }else
        {

                my $i;
                my $nb=scalar(@ctorParams);
                for($i=0;$i<$nb;$i++)
                         {
                                 genParam($i,$ctorParams[$i]);
                         }
                print OUTPUT "  $cookieName *me = new $cookieName(";
                for($i=0;$i<$nb;$i++)
                {
                        if($i)
                        {
                                print OUTPUT ",";
                        }
                        print OUTPUT "p".$i;
                }
                print OUTPUT ");\n";
        }
        print OUTPUT "  tp_obj cdata = tp_data(vm, $cookieId, me);\n";
        print OUTPUT "  cdata.data.info->xfree = $dtor;\n";
        print OUTPUT "  tp_set(vm, self, tp_string(\"cdata\"), cdata);\n";
        print OUTPUT "  return tp_None;\n";
        print OUTPUT "}\n";
        # end
}
#
#  Generate help table
#
sub genTables
{
my $helpName=$glueprefix."_".$className."_help";
my $f;
my $cfunk;
my $pyFunc;
                print OUTPUT "static tp_obj ".$helpName."(TP)\n{\n";
                print OUTPUT "  PythonEngine *engine = (PythonEngine*)tp_get(tp, tp->builtins, tp_string(\"userdata\")).data.val;\n\n";

                print OUTPUT "  engine->callEventHandlers(IScriptEngine::Information, NULL, -1, \"constructor:\\n\");\n";
                my $classPrefix = substr($className, 0,2);
                if(!($classPrefix=~m/^py$/))
                {
                    die "Classname shoud have start with \"py\"\n";
                }
                my $clsName = substr($className, 2);
                print OUTPUT "  engine->callEventHandlers(IScriptEngine::Information, NULL, -1, \"obj\t".$clsName."(" .join(", ",@ctorParamsWithNames).")\\n\");\n";
                
                my @m=sort keys %cFuncs;
                if (@m > 0)
                {
                        print OUTPUT "  engine->callEventHandlers(IScriptEngine::Information, NULL, -1, \"methods:\\n\");\n";
                }
                foreach $f( @m)
                {
                        my @params=@{$helpParams{$f}};
                        my $ret=$rType{$f};
                        print OUTPUT "  engine->callEventHandlers(IScriptEngine::Information, NULL, -1, \"$ret\\t $f(".join(", ",@params) .")\\n\");\n";
                }
                my @v=sort keys %typeVars;
                if (@v > 0)
                {
                        print OUTPUT "  engine->callEventHandlers(IScriptEngine::Information, NULL, -1, \"variables:\\n\");\n";
                }
                foreach $f( @v)
                {
                        my $ret=$typeVars{$f};
                        print OUTPUT "  engine->callEventHandlers(IScriptEngine::Information, NULL, -1, \"$ret\\t $f\\n\");\n";
                }
                print OUTPUT "\n  return tp_None;\n";
                print OUTPUT "}\n";
#
#  Create the init function that will register our class
#
        my $setName=$glueprefix."_".$className."_set";
        my $getName=$glueprefix."_".$className."_get";

        print OUTPUT "tp_obj initClass".$className."(tp_vm *vm)\n";
        print OUTPUT "{\n";
        print OUTPUT "  tp_obj myClass = tp_class(vm);\n";
        print OUTPUT "  tp_set(vm,myClass, tp_string(\"__init__\"), tp_fnc(vm,myCtor".$className."));\n";
        print OUTPUT "  tp_set(vm,myClass, tp_string(\"__set__\"), tp_fnc(vm,".$setName."));\n";
        print OUTPUT "  tp_set(vm,myClass, tp_string(\"__get__\"), tp_fnc(vm,".$getName."));\n";
        print OUTPUT "  tp_set(vm,myClass, tp_string(\"help\"), tp_fnc(vm,$helpName));\n";
        print OUTPUT "  return myClass;\n";
        print OUTPUT "}\n";
}
##################################
#  Main
##################################
if(scalar(@ARGV)!=2)
{
        die("admPy inputfile outputfile\n");
}
$input=$ARGV[0];
$output=$ARGV[1];
##
## Main Loop
##
# 1 grab all functions
parseFile($input);
# 2 gen glue
open(OUTPUT,">$output") or die("Cannot open $output");
print OUTPUT "// Generated by admPyClass.pl from $input, DO NOT edit !\n";
genGlue();
#
#
#
genCtorDtor();
#
# 3 gen tables (ctor, help, register)
#
genTables();
close(OUTPUT);


#
print "done\n.";
