#!/usr/bin/perl

require "reascript_helper.pl";

$arg=shift @ARGV;
$arg eq "-x64" and $isx64=1;


($funclist, $rtype, $ptypes)=ReadAPIFuncs();


print <<EOF;
import re
from ctypes import *

# lowercase rpr_ functions are for internal use

_ft={}

def rpr_initft(ft):
  if (len(_ft) == 0):
    _ft.update(ft)

def rpr_getfp(fn):
  return _ft[fn]

EOF

if ($isx64)
{
  print <<EOF;
def rpr_packp(t,v):
  m=re.match('^\\((\\w+\\*)\\)0x([0-9A-F]+)\$', str(v))
  if (m != None):
    (_t,_v)=m.groups()
    if (_t == t or t == 'void*'):
      a=int(_v[:8],16)
      b=int(_v[8:],16);
      p=c_uint64((a<<32)|b).value
      if (RPR_ValidatePtr(p,t)):
        return p
  return 0

def rpr_unpackp(t,v):
  if (v == None):
    v=0
  a=int(v>>32)
  b=int(v&0xFFFFFFFF)
  return '(%s)0x%08X%08X' % (t,a,b)

def RPR_ValidatePtr(p,t):
  a=_ft['ValidatePtr']
  f=CFUNCTYPE(c_byte,c_uint64,c_char_p)(a)
  return f(c_uint64(p),rpr_packsc(t))

EOF
}
else
{
  print <<EOF;
def rpr_packp(t,v):
  m=re.match('^\\((\\w+\\*)\\)0x([0-9A-F]+)\$', str(v))
  if (m != None):
    (_t,_v)=m.groups()
    if (_t == t or t == 'void*'):
      p=c_uint(int(_v,16)).value
      if (RPR_ValidatePtr(p,t)):
        return p
  return 0

def rpr_unpackp(t,v):
  if (v == None):
    v=0
  return '(%s)0x%08X' % (t,v)

def RPR_ValidatePtr(p,t):
  a=_ft['ValidatePtr']
  f=CFUNCTYPE(c_byte,c_uint,c_char_p)(a)
  return f(c_uint(p),rpr_packsc(t))

EOF
}

print <<EOF;
def rpr_packsc(v):
  return c_char_p(str(v).encode())

def rpr_packs(v):
  MAX_STRBUF=262144
  return create_string_buffer(str(v).encode(),MAX_STRBUF)

def rpr_unpacks(v):
  return str(v.value.decode())

EOF

sub PyType
{
  my ($t)=@_;
  $t eq "void" and return "None";
  ($t eq "bool" || $t eq "char") and return "c_byte";
  IsInt($t) and return "c_int";
  $t eq "float" and return "c_float";
  $t eq "double" and return "c_double";
  IsString($t) and return "c_char_p";
  IsPlainDataPtr($t) and return "c_void_p";
  IsOpaquePtr($t) and return ($isx64 ? "c_uint64" : "c_uint"); # includes void*
  die "unknown type $t";
}

sub PyPack
{
  my ($t, $v)=@_;
  IsPlainData($t) and return sprintf "%s(%s)", PyType($t), $v; 
  $t eq "const char*" and return sprintf "rpr_packsc(%s)", $v;
  $t eq "char*" and return sprintf "rpr_packs(%s)", $v;
  # todo handle char**
  IsPlainDataPtr($t) and return sprintf "%s(%s)", PyType(UnderlyingType($t)), $v;
  IsOpaquePtr($t) and return sprintf "rpr_packp('%s',%s)", $t, $v;	
  die "unknown type $t";
}

sub PyUnpack
{
  my ($t, $ov, $v)=@_;
  (IsPlainData($t) || IsOpaquePtr($t) || $t eq "const char*") and return $ov;
  $t eq "char*" and return sprintf "rpr_unpacks(%s)", $v;
  ($t eq "bool*" || $t eq "char*" || IsInt(UnderlyingType($t))) and
    return sprintf "int(%s.value)", $v;
  ($t eq "float*" || $t eq "double*") and return sprintf "float(%s.value)", $v;
  # todo handle char**
  IsPlainDataPtr($t) and return PyUnpack(UnderlyingType($t), $v);
  die "unknown type $t";
}

sub PyRetUnpack
{
  my ($t, $v)=@_;
  $t eq "const char*" and return sprintf "str(%s.decode())", $v;
  IsOpaquePtr($t) and return sprintf "rpr_unpackp('%s',%s)", $t, $v;
  return $v;
}


ReadAPIFuncs();

foreach $fname (@$funclist)
{
  $fname eq "ValidatePtr" and next;

  @inparms=();
  @proto=();
  @pack=();
  @cparms=();
  @unpack=();

  push @proto, PyType($rtype->{$fname});

  $byref=0;
  $i=0;
  foreach $t (split(",", $ptypes->{$fname}))
  { 
    $isptr=IsPlainDataPtr($t);

    push @inparms, "p$i";
    push @proto, PyType($t);
    push @pack, PyPack($t, "p$i");
	push @cparms, (($isptr && $t ne "char*") ? "byref(t[$i])" : "t[$i]");
    push @unpack, PyUnpack($t, "p$i", "t[$i]");
    $isptr and $byref=1;
    ++$i;
  }

  $isvoid=($rtype->{$fname} eq "void");

  printf "def RPR_$fname(%s):\n", join(",", @inparms);
  printf "  a=_ft['$fname']\n"; # errors out if key does not exist
  printf "  f=CFUNCTYPE(%s)(a)\n", join(",", @proto);
  @pack and printf "  t=(%s%s)\n", join(",", @pack), (@pack == 1 ? ",": "");
  printf "  %sf(%s)\n", ($isvoid ? "" : "r="), join(",", @cparms);
  if ($byref)
  {
    $isvoid or unshift @unpack, PyRetUnpack($rtype->{$fname}, "r");
	printf "  return (%s)\n", join(",", @unpack);
  }
  elsif (!$isvoid)
  {
    printf "  return %s\n", PyRetUnpack($rtype->{$fname}, "r");
  }
  print "\n";
}


warn "Parsed " . @$funclist . " functions\n";

