unit luaclientfunctions;

{$mode objfpc}{$H+}

interface

uses
  windows, Classes, SysUtils, syncobjs;

function CELUA_Initialize(pipename: pchar): BOOL; stdcall;
function CELUA_ExecuteFunction(script: pchar; parameters: UINT_PTR): UINT_PTR; stdcall;
function CELUA_ExecuteFunctionAsync(script: pchar; parameters: UINT_PTR): UINT_PTR; stdcall;

function CELUA_GetFunctionReferenceFromName(functionname: pchar): integer; stdcall;
function CELUA_ExecuteFunctionByReference(ref: integer; paramcount: integer; AddressOfParameters: PPointer; async: BOOLEAN): UINT_PTR; stdcall;

var CELUA_ServerName: array [0..255] of char;

implementation

var
  pipe: THandle=INVALID_HANDLE_VALUE;
  cs: TCriticalSection;



procedure CELUA_Error;
begin
  closehandle(pipe);
  pipe:=INVALID_HANDLE_VALUE;
end;

function CELUA_ExecuteFunctionByReference(ref: integer; paramcount: integer; AddressOfParameters: PPointer; async: BOOLEAN): UINT_PTR; stdcall;
type TParamType=(ptNil=0, ptBoolean=1, ptInt64=2, ptInt32=3, ptNumber=4, ptString=5, ptTable=6, ptUnknown=255);
var
  command: byte;
  bw: dword;
  a: byte;
  i: integer;
  valtype: TParamType;
  vtb: byte;
  returncount: byte;

  v: qword;
  d: double;
  stringlength: word;
  s: pchar;
begin
  if pipe=INVALID_HANDLE_VALUE then
    CELUA_Initialize(CELUA_ServerName);

  result:=0;
  command:=3;

  {$ifdef cpu32}
  valtype:=ptInt32;
  {$else}
  valtype:=ptInt64;
  {$endif}

  vtb:=byte(valtype);

  cs.enter;
  try
    if pipe<>INVALID_HANDLE_VALUE then
    begin

      if writefile(pipe, command, sizeof(command), bw, nil) then

      if async then a:=1 else a:=0;
      if writefile(pipe, a, sizeof(a), bw, nil) then
      begin
        if writefile(pipe, ref, sizeof(ref), bw, nil) then
        begin
          if writefile(pipe, paramcount, 1, bw, nil) then
          begin
            for i:=0 to paramcount-1 do
            begin
              WriteFile(pipe, vtb, sizeof(vtb),bw,nil);
              WriteFile(pipe, AddressOfParameters[i],sizeof(pointer),bw,nil);
            end;

            returncount:=1;
            if writefile(pipe, returncount, sizeof(returncount),bw,nil) then
            begin
              //the lua function is being called now
              if readfile(pipe, returncount, sizeof(returncount), bw, nil) then
              begin
                if returncount>0 then
                begin
                  for i:=0 to returncount-1 do
                  begin
                    if readfile(pipe, vtb, sizeof(vtb), bw, nil) then
                    begin
                      case TParamType(vtb) of
                        ptNil, ptUnknown: exit(0);
                        ptBoolean:
                        begin
                          readfile(pipe, a, sizeof(a), bw, nil);
                          exit(a);
                        end;

                        ptInt64:
                        begin
                          readfile(pipe, v, sizeof(v), bw, nil);
                          exit(v);
                        end;

                        ptNumber:
                        begin
                          readfile(pipe, d, sizeof(d), bw, nil);
                          exit(trunc(d));
                        end;

                        ptString:
                        begin
                          readfile(pipe, stringlength, sizeof(stringlength), bw, nil);
                          getmem(s, stringlength+1);
                          readfile(pipe, s[0], stringlength, bw, nil);
                          s[stringlength]:=#0;
                          try
                            result:=StrToInt(s);
                          except
                          end;
                        end;

                      end;
                    end else
                    begin
                      CELUA_Error;
                      exit;
                    end;

                  end;
                end;
              end;
            end;
          end;
        end;
      end;

      CELUA_Error;
    end;
  finally
    cs.leave;
  end;
end;


function CELUA_ExecuteFunction_Internal(script: pchar; parameters: UINT_PTR; async: boolean=false): UINT_PTR;
var
  command: byte;
  bw: dword;
  l: integer;
  p: qword; //biggest common denominator
  r: qword;
begin
  if pipe=INVALID_HANDLE_VALUE then
    CELUA_Initialize(CELUA_ServerName);

  bw:=0;
  r:=0;
  cs.enter;
  try

    if pipe<>INVALID_HANDLE_VALUE then
    begin
      if async then
        command:=4 //async
      else
        command:=1; //sync

      if writefile(pipe, command,sizeof(command), bw, nil) then
      begin
        l:=strlen(script);
        if writefile(pipe, l, sizeof(l), bw, nil) then
        begin
          if writefile(pipe, script^, l, bw, nil) then
          begin
            p:=parameters;
            if writefile(pipe, p, sizeof(p), bw, nil) then
            begin
              //get the result (also 8 bytes, even for a 32-bit app)
              if ReadFile(pipe, r, sizeof(r), bw, nil) then
                result:=r
              else
                CELUA_Error;
            end;
          end
          else
            CELUA_Error;
        end
        else
          CELUA_Error;

      end
      else
        CELUA_Error;
    end;

  finally
    cs.leave;
  end;
end;

function CELUA_ExecuteFunction(script: pchar; parameters: UINT_PTR): UINT_PTR; stdcall;
begin
  result:=CELUA_ExecuteFunction_Internal(script, parameters, false);
end;

function CELUA_ExecuteFunctionAsync(script: pchar; parameters: UINT_PTR): UINT_PTR; stdcall;
begin
  result:=CELUA_ExecuteFunction_Internal(script, parameters, true);
end;

function CELUA_GetFunctionReferenceFromName(functionname: pchar): integer; stdcall;
begin
  result:=CELUA_ExecuteFunction(pchar('return createRef('+functionname+')'), 0);
end;



function CELUA_Initialize(pipename: pchar): BOOL; stdcall;
begin
  if cs=nil then
    cs:=TCriticalSection.create;

  if pipename<>CELUA_ServerName then
    strcopy(CELUA_ServerName,pipename);

  cs.enter;
  try
    if pipe<>INVALID_HANDLE_VALUE then
      closehandle(pipe);

    pipe:=CreateFile(pchar('\\.\pipe\'+pipename), GENERIC_READ or GENERIC_WRITE, 0, nil,  OPEN_EXISTING, 0, 0);
    result:=pipe<>INVALID_HANDLE_VALUE;
  finally
    cs.leave;
  end;
end;

initialization
  ZeroMemory(@CELUA_ServerName,255);
  strcopy(CELUA_ServerName,pchar('CELUASERVER'));

end.

