/// remote access to a mORMot server using SynCrossPlatform* units
// - retrieved from http://localhost:888/root/wrapper/CrossPlatform/mORMotClient.pas
// at 2014-12-10 21:28:46 using "CrossPlatform.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 Delphi 6 and later, under all supported platforms
  (including MacOSX, and NextGen iPhone/iPad), and the Free Pascal Compiler.
}

interface

uses
  SynCrossPlatformJSON,
  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; 
  public
    property Simple: TTestCustomJSONArraySimpleArray read fSimple write fSimple;
  published
    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;
  end;
  
  /// service implemented by TServiceCalculator
  // - you can access this service as such:
  // !var aCalculator: ICalculator;
  // !begin
  // !   aCalculator := TCalculator.Create(aClient);
  // !   // now you can use aCalculator methods
  // !...
  ICalculator = interface(IServiceAbstract)
    ['{9A60C8ED-CEB2-4E09-87D4-4A16F496E5FE}']
    function Add(const n1: Integer; const n2: Integer): Integer;
    procedure ToText(const Value: Currency; const Curr: String; var Sexe: TPeopleSexe; var Name: String);
    function RecordToText(var Rec: TTestCustomJSONArraySimpleArray): String;
    function GetPeople(const id: TID; out People: TSQLRecordPeople; out Sexes: TPeopleSexeDynArray; var arr: TSimpleRecordDynArray): Boolean;
  end;

  /// implements ICalculator from http://localhost:888/root/Calculator
  // - this service will run in sicShared mode
  TServiceCalculator = class(TServiceClientAbstract,ICalculator)
  public
    constructor Create(aClient: TSQLRestClientURI); override;
    function Add(const n1: Integer; const n2: Integer): Integer;
    procedure ToText(const Value: Currency; const Curr: String; var Sexe: TPeopleSexe; var Name: String);
    function RecordToText(var Rec: TTestCustomJSONArraySimpleArray): String;
    function GetPeople(const id: TID; out People: TSQLRecordPeople; out 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 - on connection or authentication error,
// this function will raise a corresponding exception
function GetClient(const aServerAddress, aUserName,aPassword: string;
  aServerPort: integer=SERVER_PORT): TSQLRestClientHTTP;

// 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(_variant: variant): TTestCustomJSONArraySimpleArray;
function TTestCustomJSONArraySimpleArray2Variant(const _record: TTestCustomJSONArraySimpleArray): variant;
function Variant2TSimpleRecord(_variant: variant): TSimpleRecord;
function TSimpleRecord2Variant(const _record: 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 }

function Variant2TPeopleSexe(const _variant: variant): TPeopleSexe;
begin
  result := TPeopleSexe(VariantToEnum(_variant,['sFemale','sMale']));
end;

function Variant2TRecordEnum(const _variant: variant): TRecordEnum;
begin
  result := TRecordEnum(VariantToEnum(_variant,['reOne','reTwo','reLast']));
end;


{ Some helpers for record types }

function Variant2TTestCustomJSONArraySimpleArray(_variant: variant): TTestCustomJSONArraySimpleArray;
var _a: integer;
    _arr: PJSONVariantData;
begin
  result.F := _variant.F;
  _arr := JSONVariantDataSafe(_variant.G,jvArray);
  SetLength(result.G,_arr^.Count);
  for _a := 0 to high(result.G) do
    result.G[_a] := _arr^.Values[_a];
  result.H.H1 := _variant.H.H1;
  result.H.H2 := _variant.H.H2;
  result.H.H3.H3a := _variant.H.H3.H3a;
  result.H.H3.H3b := VariantToBlob(_variant.H.H3.H3b);
  result.I := Iso8601ToDateTime(_variant.I);
  _arr := JSONVariantDataSafe(_variant.J,jvArray);
  SetLength(result.J,_arr^.Count);
  for _a := 0 to high(result.J) do
  with result.J[_a] do begin
    J1 := _arr^.Values[_a].J1;
    J2 := VariantToGUID(_arr^.Values[_a].J2);
    J3 := Variant2TRecordEnum(_arr^.Values[_a].J3);
  end;
end;

function TTestCustomJSONArraySimpleArray2Variant(const _record: TTestCustomJSONArraySimpleArray): variant;
var i: integer;
    res: TJSONVariantData;
begin
  res.Init;
  res.SetPath('F',_record.F);
  with res.EnsureData('G')^ do
    for i := 0 to high(_record.G) do
      AddValue(_record.G[i]);
  res.SetPath('H.H1',_record.H.H1);
  res.SetPath('H.H2',_record.H.H2);
  res.SetPath('H.H3.H3a',_record.H.H3.H3a);
  res.SetPath('H.H3.H3b',BlobToVariant(_record.H.H3.H3b));
  res.SetPath('I',DateTimeToIso8601(_record.I));
  with res.EnsureData('J')^ do
    for i := 0 to high(_record.J) do
    with AddItem^, _record.J[i] do begin
      AddNameValue('J1',J1);
      AddNameValue('J2',GUIDToVariant(J2));
      AddNameValue('J3',ord(J3));
    end;
  result := variant(res);
end;

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

function TSimpleRecord2Variant(const _record: TSimpleRecord): variant;
var res: TJSONVariantData;
begin
  res.Init;
  res.SetPath('A',_record.A);
  res.SetPath('B',_record.B);
  res.SetPath('C',_record.C);
  result := variant(res);
end;


{ Some helpers for dynamic array types }

function Variant2TPeopleSexeDynArray(const _variant: variant): TPeopleSexeDynArray;
var i: integer;
    arr: PJSONVariantData;
begin
  arr := JSONVariantDataSafe(_variant,jvArray);
  SetLength(result,arr^.Count);
  for i := 0 to arr^.Count-1 do
    result[i] := (arr^.Values[i]);
end;

function TPeopleSexeDynArray2Variant(const _array: TPeopleSexeDynArray): variant;
var i: integer;
    res: TJSONVariantData;
begin
  res.Init;
  for i := 0 to high(_array) do
    res.AddValue((_array[i]));
  result := variant(res);
end;

function Variant2TSimpleRecordDynArray(const _variant: variant): TSimpleRecordDynArray;
var i: integer;
    arr: PJSONVariantData;
begin
  arr := JSONVariantDataSafe(_variant,jvArray);
  SetLength(result,arr^.Count);
  for i := 0 to arr^.Count-1 do
    result[i] := Variant2TSimpleRecord(arr^.Values[i]);
end;

function TSimpleRecordDynArray2Variant(const _array: TSimpleRecordDynArray): variant;
var i: integer;
    res: TJSONVariantData;
begin
  res.Init;
  for i := 0 to high(_array) do
    res.AddValue(TSimpleRecord2Variant(_array[i]));
  result := variant(res);
end;


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

function GetClient(const aServerAddress, aUserName,aPassword: string;
  aServerPort: integer): TSQLRestClientHTTP;
begin
  result := TSQLRestClientHTTP.Create(aServerAddress,aServerPort,GetModel,true); // aOwnModel=true
  try
    if (not result.Connect) or (result.ServerTimeStamp=0) then
      raise ERestException.CreateFmt('Impossible to connect to %s:%d server',
        [aServerAddress,aServerPort]);
    if not result.SetUser(TSQLRestServerAuthenticationDefault,aUserName,aPassword) then 
      raise ERestException.CreateFmt('%s:%d server rejected "%s" credentials',
        [aServerAddress,aServerPort,aUserName]);
  except
    result.Free;
    raise;
  end;
end;


{ TServiceCalculator }

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

function TServiceCalculator.Add(const n1: Integer; const n2: Integer): Integer;
var res: TVariantDynArray;
begin
  fClient.CallRemoteService(self,'Add',1, // raise EServiceException on error
    [n1,n2],res);
  Result := res[0];
end;

procedure TServiceCalculator.ToText(const Value: Currency; const Curr: String; var Sexe: TPeopleSexe; var Name: String);
var res: TVariantDynArray;
begin
  fClient.CallRemoteService(self,'ToText',2, // raise EServiceException on error
    [Value,Curr,ord(Sexe),Name],res);
  Sexe := Variant2TPeopleSexe(res[0]);
  Name := res[1];
end;

function TServiceCalculator.RecordToText(var Rec: TTestCustomJSONArraySimpleArray): String;
var res: TVariantDynArray;
begin
  fClient.CallRemoteService(self,'RecordToText',2, // raise EServiceException on error
    [TTestCustomJSONArraySimpleArray2Variant(Rec)],res);
  Rec := Variant2TTestCustomJSONArraySimpleArray(res[0]);
  Result := res[1];
end;

function TServiceCalculator.GetPeople(const id: TID; out People: TSQLRecordPeople; out Sexes: TPeopleSexeDynArray; var arr: TSimpleRecordDynArray): Boolean;
var res: TVariantDynArray;
begin
  fClient.CallRemoteService(self,'GetPeople',4, // raise EServiceException on error
    [id,TSimpleRecordDynArray2Variant(arr)],res);
  People := TSQLRecordPeople.CreateFromVariant(res[0]);
  Sexes := Variant2TPeopleSexeDynArray(res[1]);
  arr := Variant2TSimpleRecordDynArray(res[2]);
  Result := res[3];
end;


end.