/// remote access to a mORMot server using SmartMobileStudio
// - retrieved from http://localhost:888/root/wrapper/SmartMobileStudio/mORMotClient.pas
// at 2014-12-10 21:44:23 using "SmartMobileStudio.pas.mustache" template
unit mORMotClient;

{
  WARNING:
    This unit has been generated by a mORMot 1.18.626 server.
    Any manual modification of this file may be lost after regeneration.

  Synopse mORMot framework. Copyright (C) 2014 Arnaud Bouchez
    Synopse Informatique - https://synopse.info

  This unit is released under a MPL/GPL/LGPL tri-license,
  and therefore may be freely included in any application.

  This unit would work on Smart Mobile Studio 2.1.1 and later.
}

interface

uses
  SmartCL.System,
  System.Types,
  SynCrossPlatformSpecific,
  SynCrossPlatformREST;


type // define some enumeration types, used below
  TPeopleSexe = (sFemale, sMale);
  TRecordEnum = (reOne, reTwo, reLast);

type // define some record types, used as properties below
  TTestCustomJSONArraySimpleArray = record
    F: String;
    G: array of String;
    H: record
      H1: Integer;
      H2: String;
      H3: record
        H3a: Boolean;
        H3b: TSQLRawBlob;
      end;
    end;
    I: TDateTime;
    J: array of record
      J1: Byte;
      J2: TGUID;
      J3: TRecordEnum;
    end;
  end;

  TSimpleRecord = record
    A: Integer;
    B: Integer;
    C: String;
  end;

type // define some dynamic array types, used as properties below
  TPeopleSexeDynArray = array of Byte;
  TSimpleRecordDynArray = array of TSimpleRecord;

type
  /// map "People" table
  TSQLRecordPeople = class(TSQLRecord)
  protected
    fFirstName: String; 
    fLastName: String; 
    fData: TSQLRawBlob; 
    fYearOfBirth: Integer; 
    fYearOfDeath: Word; 
    fSexe: TPeopleSexe; 
    fSimple: TTestCustomJSONArraySimpleArray; 
    // those overriden methods will emulate the needed RTTI
    class function ComputeRTTI: TRTTIPropInfos; override;
    procedure SetProperty(FieldIndex: integer; const Value: variant); override;
    function GetProperty(FieldIndex: integer): variant; override;
  public
    property FirstName: String read fFirstName write fFirstName;
    property LastName: String read fLastName write fLastName;
    property Data: TSQLRawBlob read fData write fData;
    property YearOfBirth: Integer read fYearOfBirth write fYearOfBirth;
    property YearOfDeath: Word read fYearOfDeath write fYearOfDeath;
    property Sexe: TPeopleSexe read fSexe write fSexe;
    property Simple: TTestCustomJSONArraySimpleArray read fSimple write fSimple;
  end;
  
  /// service accessible via http://localhost:888/root/Calculator
  // - this service will run in sicShared mode
  // - synchronous and asynchronous methods are available, depending on use case
  // - synchronous _*() methods will block the browser execution, so won't be
  // appropriate for long process - on error, they may raise EServiceException
  TServiceCalculator = class(TServiceClientAbstract)
  public
    /// will initialize an access to the remote service
    constructor Create(aClient: TSQLRestClientURI); override;

    procedure Add(n1: Integer; n2: Integer; 
      onSuccess: procedure(Result: Integer); onError: TSQLRestEvent);
    function _Add(const n1: Integer; const n2: Integer): Integer;

    procedure ToText(Value: Currency; Curr: String; Sexe: TPeopleSexe; Name: String; 
      onSuccess: procedure(Sexe: TPeopleSexe; Name: String); onError: TSQLRestEvent);
    procedure _ToText(const Value: Currency; const Curr: RawUTF8; var Sexe: TPeopleSexe; var Name: RawUTF8);

    procedure RecordToText(Rec: TTestCustomJSONArraySimpleArray; 
      onSuccess: procedure(Rec: TTestCustomJSONArraySimpleArray; Result: String); onError: TSQLRestEvent);
    function _RecordToText(var Rec: TTestCustomJSONArraySimpleArray): String;

    procedure GetPeople(id: TID; arr: TSimpleRecordDynArray; 
      onSuccess: procedure(People: TSQLRecordPeople; Sexes: TPeopleSexeDynArray; arr: TSimpleRecordDynArray; Result: Boolean); onError: TSQLRestEvent);
    function _GetPeople(const id: TID; var People: TSQLRecordPeople; var Sexes: TPeopleSexeDynArray; var arr: TSimpleRecordDynArray): Boolean;
  end;


const
  /// the server port, corresponding to http://localhost:888
  SERVER_PORT = 888;


/// return the database Model corresponding to this server
function GetModel: TSQLModel;

/// create a TSQLRestClientHTTP instance and connect to the server
// - it will use by default port 888
// - secure connection will be established via TSQLRestServerAuthenticationDefault
// with the supplied credentials
// - request will be asynchronous, and trigger onSuccess or onError event
procedure GetClient(const aServerAddress, aUserName,aPassword: string;
  onSuccess, onError: TSQLRestEvent; aServerPort: integer=SERVER_PORT);

// publish some low-level helpers for variant conversion
// - used internally: you should not need those functions in your end-user code
function Variant2TPeopleSexe(const _variant: variant): TPeopleSexe;
function Variant2TRecordEnum(const _variant: variant): TRecordEnum;
function Variant2TTestCustomJSONArraySimpleArray(const Value: variant): TTestCustomJSONArraySimpleArray;
function TTestCustomJSONArraySimpleArray2Variant(const Value: TTestCustomJSONArraySimpleArray): variant;
function Variant2TSimpleRecord(const Value: variant): TSimpleRecord;
function TSimpleRecord2Variant(const Value: TSimpleRecord): variant;
function Variant2TPeopleSexeDynArray(const _variant: variant): TPeopleSexeDynArray;
function TPeopleSexeDynArray2Variant(const _array: TPeopleSexeDynArray): variant;
function Variant2TSimpleRecordDynArray(const _variant: variant): TSimpleRecordDynArray;
function TSimpleRecordDynArray2Variant(const _array: TSimpleRecordDynArray): variant;


implementation


{ Some helpers for enumerates types }

{$HINTS OFF} // for begin asm return ... end; end below

// those functions will use the existing generated string array constant
// defined by the SMS compiler for each enumeration

function Variant2TPeopleSexe(const _variant: variant): TPeopleSexe;
begin
  asm return @VariantToEnum(@_variant,@TPeopleSexe); end;
end;

function Variant2TRecordEnum(const _variant: variant): TRecordEnum;
begin
  asm return @VariantToEnum(@_variant,@TRecordEnum); end;
end;

{$HINTS ON}

{ Some helpers for record types:
  due to potential obfuscation of generated JavaScript, we can't assume
  that the JSON used for transmission would match record fields naming }

function Variant2TTestCustomJSONArraySimpleArray(const Value: variant): TTestCustomJSONArraySimpleArray;
begin
  result.F := Value.F;
  if VariantType(Value.G)=jvArray then
    for var i := 0 to integer(Value.G.length)-1 do
      result.G.Add(String(Value.G[i]));
  result.H.H1 := Value.H.H1;
  result.H.H2 := Value.H.H2;
  result.H.H3.H3a := Value.H.H3.H3a;
  result.H.H3.H3b := VariantToBlob(Value.H.H3.H3b);
  result.I := Iso8601ToDateTime(Value.I);
  if VariantType(Value.J)=jvArray then begin
    var tmp: TTestCustomJSONArraySimpleArray;
    tmp.J.SetLength(1);
    for var n := 0 to integer(Value.J.length)-1 do begin
      var source := Value.J[n];
      var dest := tmp.J[0];
      dest.J1 := source.J1;
      dest.J2 := VariantToGUID(source.J2);
      dest.J3 := Variant2TRecordEnum(source.J3);
      result.J.Add(dest);
    end;
  end;
end;

function TTestCustomJSONArraySimpleArray2Variant(const Value: TTestCustomJSONArraySimpleArray): variant;
begin
  result := new JObject;
  result.F := Value.F;
  result.G := variant(Value.G);
  result.H := new JObject;
  result.H.H1 := Value.H.H1;
  result.H.H2 := Value.H.H2;
  result.H.H3 := new JObject;
  result.H.H3.H3a := Value.H.H3.H3a;
  result.H.H3.H3b := BlobToVariant(Value.H.H3.H3b);
  result.I := DateTimeToIso8601(Value.I);
  result.J := TVariant.CreateArray;
  for var source in Value.J do begin
    var dest: variant := new JObject;
    dest.J1 := source.J1;
    dest.J2 := GUIDToVariant(source.J2);
    dest.J3 := ord(source.J3);
    result.J.push(dest);
  end;
end;

function Variant2TSimpleRecord(const Value: variant): TSimpleRecord;
begin
  result.A := Value.A;
  result.B := Value.B;
  result.C := Value.C;
end;

function TSimpleRecord2Variant(const Value: TSimpleRecord): variant;
begin
  result := new JObject;
  result.A := Value.A;
  result.B := Value.B;
  result.C := Value.C;
end;


{ Some helpers for dynamic array types }

function Variant2TPeopleSexeDynArray(const _variant: variant): TPeopleSexeDynArray;
var tmp: Byte;
begin
  if VariantType(_variant)=jvArray then
    for var i := 0 to integer(_variant.Length)-1 do begin
      tmp := (_variant[i]);
      result.Add(tmp);
    end;
end;

function TPeopleSexeDynArray2Variant(const _array: TPeopleSexeDynArray): variant;
var i: integer;
begin
  result := TVariant.CreateArray;
  for i := 0 to high(_array) do
    result.push((_array[i]));
end;

function Variant2TSimpleRecordDynArray(const _variant: variant): TSimpleRecordDynArray;
var tmp: TSimpleRecord;
begin
  if VariantType(_variant)=jvArray then
    for var i := 0 to integer(_variant.Length)-1 do begin
      tmp := Variant2TSimpleRecord(_variant[i]);
      result.Add(tmp);
    end;
end;

function TSimpleRecordDynArray2Variant(const _array: TSimpleRecordDynArray): variant;
var i: integer;
begin
  result := TVariant.CreateArray;
  for i := 0 to high(_array) do
    result.push(TSimpleRecord2Variant(_array[i]));
end;



{ TSQLRecordPeople }

class function TSQLRecordPeople.ComputeRTTI: TRTTIPropInfos;
begin
  result := TRTTIPropInfos.Create(
    ['FirstName','LastName','Data','YearOfBirth','YearOfDeath','Sexe','Simple'],
    [sftUnspecified,sftUnspecified,sftBlob,sftUnspecified,sftUnspecified,sftUnspecified,sftRecord]);
end;

procedure TSQLRecordPeople.SetProperty(FieldIndex: integer; const Value: variant);
begin
  case FieldIndex of
  0: fID := Value;
  1: fFirstName := Value;
  2: fLastName := Value;
  3: fData := VariantToBlob(Value);
  4: fYearOfBirth := Value;
  5: fYearOfDeath := Value;
  6: fSexe := Variant2TPeopleSexe(Value);
  7: fSimple := Variant2TTestCustomJSONArraySimpleArray(Value);
  end;
end;

function TSQLRecordPeople.GetProperty(FieldIndex: integer): variant;
begin
  case FieldIndex of
  0: result := fID;
  1: result := fFirstName;
  2: result := fLastName;
  3: result := BlobToVariant(fData);
  4: result := fYearOfBirth;
  5: result := fYearOfDeath;
  6: result := ord(fSexe);
  7: result := TTestCustomJSONArraySimpleArray2Variant(fSimple);
  end;
end;


function GetModel: TSQLModel;
begin
  result := TSQLModel.Create([TSQLAuthUser,TSQLAuthGroup,TSQLRecordPeople],'root');
end;

procedure GetClient(const aServerAddress, aUserName,aPassword: string;
  onSuccess, onError: TSQLRestEvent; aServerPort: integer);
begin
  var client := TSQLRestClientHTTP.Create(aServerAddress,aServerPort,GetModel,true);
  client.Connect(
  lambda
    try
      if client.ServerTimeStamp=0 then begin
        if Assigned(onError) then
          onError(client);
        exit;
      end;
      if not client.SetUser(TSQLRestServerAuthenticationDefault,aUserName,aPassword) then begin
        if Assigned(onError) then
          onError(client);
        exit;
      end;
      if Assigned(onSuccess) then
        onSuccess(client);
    except
      if Assigned(onError) then
        onError(client);
    end;
  end,
  onError);
end;


{ TServiceCalculator }

constructor TServiceCalculator.Create(aClient: TSQLRestClientURI);
begin
  fServiceName := 'Calculator';
  fServiceURI := 'Calculator';
  fInstanceImplementation := sicShared;
  fContractExpected := '814F1362B19B2F4D';
  inherited Create(aClient);
end;


procedure TServiceCalculator.Add(n1: Integer; n2: Integer; 
      onSuccess: procedure(Result: Integer); onError: TSQLRestEvent);
begin 
  fClient.CallRemoteServiceAsynch(self,'Add',1,
    [n1,n2],
    lambda (res: array of Variant)
      onSuccess(res[0]);
    end, onError); 
end;

function TServiceCalculator._Add(const n1: Integer; const n2: Integer): Integer;
begin
  var res := fClient.CallRemoteServiceSynch(self,'Add',1,
    [n1,n2]);
  Result := res[0];
end;


procedure TServiceCalculator.ToText(Value: Currency; Curr: String; Sexe: TPeopleSexe; Name: String; 
      onSuccess: procedure(Sexe: TPeopleSexe; Name: String); onError: TSQLRestEvent);
begin 
  fClient.CallRemoteServiceAsynch(self,'ToText',2,
    [Value,Curr,ord(Sexe),Name],
    lambda (res: array of Variant)
      onSuccess(Variant2TPeopleSexe(res[0]),res[1]);
    end, onError); 
end;

procedure TServiceCalculator._ToText(const Value: Currency; const Curr: RawUTF8; var Sexe: TPeopleSexe; var Name: RawUTF8);
begin
  var res := fClient.CallRemoteServiceSynch(self,'ToText',2,
    [Value,Curr,ord(Sexe),Name]);
  Sexe := Variant2TPeopleSexe(res[0]);
  Name := res[1];
end;


procedure TServiceCalculator.RecordToText(Rec: TTestCustomJSONArraySimpleArray; 
      onSuccess: procedure(Rec: TTestCustomJSONArraySimpleArray; Result: String); onError: TSQLRestEvent);
begin 
  fClient.CallRemoteServiceAsynch(self,'RecordToText',2,
    [TTestCustomJSONArraySimpleArray2Variant(Rec)],
    lambda (res: array of Variant)
      onSuccess(Variant2TTestCustomJSONArraySimpleArray(res[0]),res[1]);
    end, onError); 
end;

function TServiceCalculator._RecordToText(var Rec: TTestCustomJSONArraySimpleArray): String;
begin
  var res := fClient.CallRemoteServiceSynch(self,'RecordToText',2,
    [TTestCustomJSONArraySimpleArray2Variant(Rec)]);
  Rec := Variant2TTestCustomJSONArraySimpleArray(res[0]);
  Result := res[1];
end;


procedure TServiceCalculator.GetPeople(id: TID; arr: TSimpleRecordDynArray; 
      onSuccess: procedure(People: TSQLRecordPeople; Sexes: TPeopleSexeDynArray; arr: TSimpleRecordDynArray; Result: Boolean); onError: TSQLRestEvent);
begin 
  fClient.CallRemoteServiceAsynch(self,'GetPeople',4,
    [id,TSimpleRecordDynArray2Variant(arr)],
    lambda (res: array of Variant)
      onSuccess(TSQLRecordPeople.CreateFromVariant(res[0]),Variant2TPeopleSexeDynArray(res[1]),Variant2TSimpleRecordDynArray(res[2]),res[3]);
    end, onError); 
end;

function TServiceCalculator._GetPeople(const id: TID; var People: TSQLRecordPeople; var Sexes: TPeopleSexeDynArray; var arr: TSimpleRecordDynArray): Boolean;
begin
  var res := fClient.CallRemoteServiceSynch(self,'GetPeople',4,
    [id,TSimpleRecordDynArray2Variant(arr)]);
  People := TSQLRecordPeople.CreateFromVariant(res[0]);
  Sexes := Variant2TPeopleSexeDynArray(res[1]);
  arr := Variant2TSimpleRecordDynArray(res[2]);
  Result := res[3];
end;



end.