unit Myc.Data.Types.Enum;

interface

uses
    System.SysUtils,
    System.Generics.Collections,
    Myc.Data.Types;

type
    TImplDataEnumType = class(TInterfacedObject, IDataEnumType)
    private
        FName: string;
        FIdentifiers: TArray<string>;
        FIdentifierMap: TDictionary<string, Integer>;
        function GetName: String;
        function GetKind: TDataKind;
        function GetIdentifier(Idx: Integer): string;
        function GetIdentifierCount: Integer;
    public
        constructor Create(const AName: string; const AIdentifiers: array of string);
        destructor Destroy; override;
        function IndexOf(const AIdentifier: string): Integer;
        function CreateValue(const AValue: Integer): IDataEnumValue;
    end;

implementation

type
    TImplDataEnumValue = class(TInterfacedObject, IDataEnumValue)
    private
        FDataType: IDataEnumType;
        FValue: Integer;
        function GetDataType: IDataType;
        function GetAsString: string;
        function GetValue: Integer;
    public
        constructor Create(const ADataType: IDataEnumType; const AValue: Integer);
    end;

{ TImplDataEnumType }

constructor TImplDataEnumType.Create(const AName: string; const AIdentifiers: array of string);
var
    i: Integer;
begin
    inherited Create;
    FName := AName;
    FIdentifierMap := TDictionary<string, Integer>.Create;
    SetLength(FIdentifiers, Length(AIdentifiers));
    for i := 0 to High(AIdentifiers) do
    begin
        if FIdentifierMap.ContainsKey(AIdentifiers[i]) then
            raise EArgumentException.CreateFmt('Duplicate identifier: %s', [AIdentifiers[i]]);
        FIdentifiers[i] := AIdentifiers[i];
        FIdentifierMap.Add(AIdentifiers[i], i);
    end;
end;

destructor TImplDataEnumType.Destroy;
begin
    FIdentifierMap.Free;
    inherited;
end;

function TImplDataEnumType.GetName: String;
begin
    Result := FName;
end;

function TImplDataEnumType.GetKind: TDataKind;
begin
    Result := dkEnum;
end;

function TImplDataEnumType.GetIdentifier(Idx: Integer): string;
begin
    Result := FIdentifiers[Idx];
end;

function TImplDataEnumType.GetIdentifierCount: Integer;
begin
    Result := Length(FIdentifiers);
end;

function TImplDataEnumType.IndexOf(const AIdentifier: string): Integer;
begin
    if not FIdentifierMap.TryGetValue(AIdentifier, Result) then
        Result := -1;
end;

function TImplDataEnumType.CreateValue(const AValue: Integer): IDataEnumValue;
begin
    if (AValue < 0) or (AValue >= Length(FIdentifiers)) then
        raise EArgumentException.Create('Invalid enum value');
    Result := TImplDataEnumValue.Create(Self, AValue);
end;

{ TImplDataEnumValue }

constructor TImplDataEnumValue.Create(const ADataType: IDataEnumType; const AValue: Integer);
begin
    FDataType := ADataType;
    FValue := AValue;
end;

function TImplDataEnumValue.GetDataType: IDataType;
begin
    Result := FDataType;
end;

function TImplDataEnumValue.GetAsString: string;
begin
    Result := FDataType.Identifiers[FValue];
end;

function TImplDataEnumValue.GetValue: Integer;
begin
    Result := FValue;
end;

end.
