unit Myc.Data.Records.Types;

interface

uses
    System.TypInfo,
    System.Generics.Collections;

type
    TDataType = class abstract
    strict private
        class var
            FRegistry: TObjectDictionary<String, TDataType>;
        class constructor CreateClass;
        class destructor DestroyClass;
    private
        FTypeName: string;
        class function GetItems(const Name: String): TDataType; static;
        class procedure SetItems(const Name: String; const Value: TDataType); static;
    protected
        function GetHandle: PTypeInfo; virtual; abstract;
        function GetSize: Integer; virtual; abstract;
    public
        constructor Create(const ATypeName: string);
        procedure Init(P: Pointer); virtual;
        procedure Assign(P, Q: Pointer); virtual; abstract;
        procedure Finalize(P: Pointer); virtual;

        class procedure RegisterType(DataType: TDataType);
        class property Items[const Name: String]: TDataType read GetItems write SetItems; default;

        property TypeName: string read FTypeName;

        property Handle: PTypeInfo read GetHandle;
        property Size: Integer read GetSize;
    end;

    TFloatType = class(TDataType)
        type
            TFltType = (fSingle, fDouble);
    private
        FHandle: PTypeInfo;
        FSize: Integer;
        FFloatType: TFltType;
    protected
        function GetHandle: PTypeInfo; override;
        function GetSize: Integer; override;
    public
        constructor Create(const ATypeName: string);
        procedure Init(P: Pointer); override;
        procedure Assign(P, Q: Pointer); override;
        procedure Finalize(P: Pointer); override;
    end;

    TIntegerType = class(TDataType)
    private
        FKind: TTypeKind;
    public
        constructor Create(const ATypeName: string; AHandle: PTypeInfo; ASize: Integer);
        procedure Init(P: Pointer); override;
        procedure Assign(P, Q: Pointer); override;
        procedure Finalize(P: Pointer); override;
    end;

    TStringType = class(TDataType)
    public
        constructor Create(const ATypeName: string; AHandle: PTypeInfo; ASize: Integer);
        procedure Init(P: Pointer); override;
        procedure Assign(P, Q: Pointer); override;
        procedure Finalize(P: Pointer); override;
    end;

implementation

{ TDataType }

constructor TDataType.Create(const ATypeName: string);
begin
    inherited Create;
    FTypeName := ATypeName;
end;

class constructor TDataType.CreateClass;
begin
    FRegistry := TObjectDictionary<String, TDataType>.Create([doOwnsValues]);
end;

class destructor TDataType.DestroyClass;
begin
    FRegistry.Free;
end;

procedure TDataType.Finalize(P: Pointer);
begin

end;

class function TDataType.GetItems(const Name: String): TDataType;
begin
    if not FRegistry.TryGetValue(Name, Result) then
        Result := nil;
end;

procedure TDataType.Init(P: Pointer);
begin

end;

class procedure TDataType.RegisterType(DataType: TDataType);
begin
    FRegistry.Add(DataType.TypeName, DataType);
end;

class procedure TDataType.SetItems(const Name: String; const Value: TDataType);
begin
    // TODO -cMM: TDataType.SetItems default body inserted
end;

{ TFloatType }

constructor TFloatType.Create(const ATypeName: string);
begin
    inherited;
    FFloatType := GetTypeData(AHandle).FloatType;
    Assert(FFloatType in [ftSingle, ftDouble]);
end;

procedure TFloatType.Assign(P, Q: Pointer);
begin
    case FFloatType of
        ftSingle: PSingle(Q)^ := PSingle(P)^;
        ftDouble: PDouble(Q)^ := PDouble(P)^;
    end;
end;

procedure TFloatType.Finalize(P: Pointer);
begin

end;

function TFloatType.GetHandle: PTypeInfo;
begin
    Result := FHandle;
end;

function TFloatType.GetSize: Integer;
begin
    Result := FSize;
end;

procedure TFloatType.Init(P: Pointer);
begin
end;

{ TIntegerType }

constructor TIntegerType.Create(const ATypeName: string; AHandle: PTypeInfo; ASize: Integer);
begin
    inherited;
    FKind := AHandle.Kind;
    Assert(FKind in [tkInteger, tkInt64]);
end;

procedure TIntegerType.Assign(P, Q: Pointer);
begin
    case FKind of
        tkInteger: PInteger(Q)^ := PInteger(P)^;
        tkInt64: PInt64(Q)^ := PInt64(P)^;
    end;
end;

procedure TIntegerType.Finalize(P: Pointer);
begin

end;

procedure TIntegerType.Init(P: Pointer);
begin

end;

{ TStringType }

constructor TStringType.Create(const ATypeName: string; AHandle: PTypeInfo; ASize: Integer);
begin
    inherited;
    Assert(AHandle.Kind in [tkString, tkUString]);
end;

procedure TStringType.Assign(P, Q: Pointer);
begin
    PString(Q)^ := PString(P)^;
end;

procedure TStringType.Finalize(P: Pointer);
begin
    System.Finalize(PString(P)^);
end;

procedure TStringType.Init(P: Pointer);
begin
    System.Initialize(PString(P)^);
end;

initialization
    TDataType.RegisterType(TFloatType.Create('Float'))

end.
