unit Bcl.Rtti.Common;

{$I bcl.inc}

interface

uses
  SysUtils,
  {$IFDEF PAS2JS}
  JS;
  {$ELSE}
  Rtti,
  Bcl.TypInfo.Common;
  {$ENDIF}

type
  {$IFDEF ISDELPHI}
  TValue = Rtti.TValue;
  {$ELSE}
    {$IFDEF PAS2JS}
    TValue = JSValue;
    {$ELSE}
    TValue = Pointer;
    {$ENDIF}
  {$ENDIF}

  {$IFDEF PAS2JS}
  EInvalidJSCast = class(Exception)
  public
    constructor Create(Value: TValue; const TypeName: string); reintroduce;
  end;
  {$ENDIF}

function TValue_IsArray(const Value: TValue): Boolean; {$IFDEF ISDELPHI}inline;{$ENDIF}
function TValue_GetArrayLength(const Value: TValue): Integer; {$IFDEF ISDELPHI}inline;{$ENDIF}
function TValue_GetArrayElement(const Value: TValue; Index: Integer): TValue; {$IFDEF ISDELPHI}inline;{$ENDIF}
function TValue_AsString(const Value: TValue): String;
function TValue_AsInteger(const Value: TValue): Integer;
function TValue_AsOrdinal(const Value: TValue): {$IFDEF PAS2JS}Integer{$ELSE}Int64{$ENDIF};
function TValue_AsBoolean(const Value: TValue): Boolean;
function TValue_AsExtended(const Value: TValue): Extended;
function TValue_AsDouble(const Value: TValue): Double;
function TValue_AsBytes(const Value: TValue): TBytes;

function TValue_FromDateTime(const Value: TDateTime): TValue;
function TValue_FromDate(const Value: TDate): TValue;
function TValue_FromTime(const Value: TTime): TValue;
function TValue_FromBytes(const Value: TBytes): TValue;

function TValue_ToString(const Value: TValue): string;


implementation

{ EInvalidJSCast }

{$IFDEF PAS2JS}
constructor EInvalidJSCast.Create(Value: TValue; const TypeName: string);
begin
  inherited CreateFmt('Cannot cast value %s to type %s',
    [TValue_ToString(Value), TypeName]);
end;
{$ENDIF}

{$IFDEF PAS2JS}
function TValue_ToString(const Value: TValue): string;
asm
  Result = "" + Value;
end;
{$ELSE}
function TValue_ToString(const Value: TValue): string;
begin
  Result := Value.ToString;
end;
{$ENDIF}

function TValue_IsArray(const Value: TValue): Boolean;
begin
  {$IFDEF PAS2JS}
  Result := JS.IsArray(Value);
  {$ELSE}
  Result := Value.IsArray;
  {$ENDIF}
end;

function TValue_GetArrayLength(const Value: TValue): Integer;
begin
  {$IFDEF PAS2JS}
  Result := TJSArray(Value).Length;
  {$ELSE}
  Result := Value.GetArrayLength;
  {$ENDIF}
end;

function TValue_GetArrayElement(const Value: TValue; Index: Integer): TValue;
begin
  {$IFDEF PAS2JS}
  Result := TJSArray(Value)[Index];
  {$ELSE}
  Result := Value.GetArrayElement(Index);
  {$ENDIF}
end;

function TValue_AsString(const Value: TValue): string;
begin
  {$IFDEF PAS2JS}
  if not JS.isString(Value) then
    raise EInvalidJSCast.Create(Value, 'String');
  Result := string(Value);
  {$ELSE}
  Result := Value.AsString;
  {$ENDIF}
end;

function TValue_AsInteger(const Value: TValue): Integer;
begin
  {$IFDEF PAS2JS}
  Result := 0; // Avoid hint
  asm
    Result = Math.floor(Value);
  end;
  if (Result <> Value) or (jsTypeOf(Value) <> 'number') then
    raise EInvalidJSCast.Create(Value, 'Integer');
  {$ELSE}
  Result := Value.AsInteger;
  {$ENDIF}
end;

{$IFDEF PAS2JS}
function TValue_AsOrdinal(const Value: TValue): Integer;
begin
  Result := TValue_AsInteger(Value);
end;
{$ELSE}
function TValue_AsOrdinal(const Value: TValue): Int64;
begin
  Result := Value.AsOrdinal;
end;
{$ENDIF}

function TValue_AsBoolean(const Value: TValue): Boolean;
begin
  {$IFDEF PAS2JS}
  Result := False; // Avoid hint
  if not JS.isBoolean(Value) then
    raise EInvalidJSCast.Create(Value, 'Boolean');
  asm
    Result = !(Value == false);
  end;
  {$ELSE}
  Result := Value.AsBoolean;
  {$ENDIF}
end;

function TValue_AsExtended(const Value: TValue): Extended;
begin
  {$IFDEF PAS2JS}
  Result := 0.0; // Avoid hint
  if not JS.isNumber(Value) then
    raise EInvalidJSCast.Create(Value, 'Number');
  asm
    Result = rtl.getNumber(Value);
  end;
  {$ELSE}
  Result := Value.AsExtended;
  {$ENDIF}
end;

function TValue_FromDateTime(const Value: TDateTime): TValue;
begin
  {$IFDEF PAS2JS}
  Result := Value;
  {$ELSE}
  Result := TValue.From<TDateTime>(Value);
  {$ENDIF}
end;

function TValue_FromDate(const Value: TDate): TValue;
begin
  {$IFDEF PAS2JS}
  Result := Value;
  {$ELSE}
  Result := TValue.From<TDate>(Value);
  {$ENDIF}
end;

function TValue_FromTime(const Value: TTime): TValue;
begin
  {$IFDEF PAS2JS}
  Result := Value;
  {$ELSE}
  Result := TValue.From<TTime>(Value);
  {$ENDIF}
end;

function TValue_AsDouble(const Value: TValue): Double;
begin
  {$IFDEF PAS2JS}
  Result := TValue_AsExtended(Value);
  {$ELSE}
  Result := Value.AsType<double>;
  {$ENDIF}
end;

function TValue_AsBytes(const Value: TValue): TBytes;
begin
  {$IFDEF PAS2JS}
  Result := TBytes(Value);
  {$ELSE}
  Result := Value.AsType<TBytes>;
  {$ENDIF}
end;

function TValue_FromBytes(const Value: TBytes): TValue;
begin
  {$IFDEF PAS2JS}
  Result := Value;
  {$ELSE}
  Result := TValue.From<TBytes>(Value);
  {$ENDIF}
end;

end.
