unit Myc.Data.Types.RTTI;

interface

uses
    System.SysUtils,
    System.RTTI,
    Myc.Data.Types;

function AsTValue(const Value: TDataType.TValue): TValue;

function RttiTypeToDataType(rttiType: TRttiType): TDataType;

implementation

uses
    System.TypInfo,
    System.Math;

// Recursively converts a TRttiType to an IDataType.
function RttiTypeToDataType(rttiType: TRttiType): TDataType;
var
    i: Integer;
begin
    // Convert an RTTI type representation to its corresponding IDataType.
    case rttiType.TypeKind of
        tkInteger, tkInt64, tkChar, tkWChar: Result := TDataType.Ordinal;
        tkFloat:
            if (rttiType.Handle = TypeInfo(TDateTime)) then
                Result := TDataType.Timestamp
            else
                Result := TDataType.Float;
        tkString, tkWideString, tkUString: Result := TDataType.Text;
        tkEnumeration:
        begin
            var enumType := rttiType as TRttiEnumerationType;
            var names := enumType.GetNames;
            var identifiers: TArray<string>;
            SetLength(identifiers, Length(names));
            for i := 0 to High(names) do
                identifiers[i] := names[i];
            Result := TDataType.EnumOf(enumType.Name, identifiers);
        end;
        tkDynArray:
        begin
            var arrayType := rttiType as TRttiDynamicArrayType;
            Result := TDataType.ArrayOf(RttiTypeToDataType(arrayType.ElementType));
        end;
        tkRecord:
        begin
            var recordType := rttiType as TRttiRecordType;
            var fields := recordType.GetFields;
            var recordFields: TArray<TDataRecordField>;
            SetLength(recordFields, Length(fields));
            for i := 0 to High(fields) do
                recordFields[i] := TDataRecordField.Create(fields[i].Name, RttiTypeToDataType(fields[i].FieldType));
            Result := TDataType.RecordOf(recordFields);
        end;
    else
        // Raise an exception for unsupported types.
        raise ENotSupportedException.CreateFmt(
            'Type "%s" (Kind: %s) is not supported for RTTI-based record creation.',
            [rttiType.Name, GetEnumName(TypeInfo(TTypeKind), Ord(rttiType.TypeKind))]);
    end;
end;

{ TValueHelper }

function AsTValue(const Value: TDataType.TValue): TValue;
begin
    case Value.DataType.Kind of
        dkVoid: Result := TValue.Empty;
        dkOrdinal: Result := TValue.From<Int64>(Value.AsOrdinal.Value);
        dkFloat: Result := TValue.From<Double>(Value.AsFloat.Value);
        dkText: Result := TValue.From<string>(Value.AsText.Value);
        dkTimestamp: Result := TValue.From<TDateTime>(Value.AsTimestamp.Value);
        dkEnum: Result := TValue.From<Integer>(Value.AsEnum.Value);
        dkMethod: Result := TValue.From<TDataMethodProc>(Value.AsMethod.Value);
    else
        raise EInvalidOpException.Create('Unsupported data type');
    end;
end;

end.
