unit Myc.Core.Future;

interface

uses
    System.SysUtils,
    Myc.Signals,
    Myc.TaskManager,
    Myc.Futures;

type
    TMycFuture<T> = class abstract(TInterfacedObject, TFuture<T>.IFuture)
    protected
        function GetValue: T; virtual; abstract;
        function GetDone: TState; virtual; abstract;
    public
        procedure BeforeDestruction; override;
    end;

    TMycNullFuture<T> = class(TMycFuture<T>)
    private
        FValue: T;
    protected
        function GetValue: T; override;
        function GetDone: TState; override;
    public
        constructor Create(const AValue: T);
    end;

    TMycGateFuncFuture<T> = class(TMycFuture<T>)
    private
        FDone: TState;
        FResult: T;
    protected
        function GetValue: T; override;
        function GetDone: TState; override;
    public
        constructor Create(const ATaskManager: TTaskManager; const AGate: TState.IState; AProc: TFunc<T>);
        destructor Destroy; override;
    end;

    TMycFutureManaged<T> = class(TMycFuture<T>)
    private
        FFuture: TFuture<T>.IFuture;
    protected
        function GetValue: T; override;
        function GetDone: TState; override;
    public
        constructor Create(const AFuture: TFuture<T>.IFuture);
        destructor Destroy; override;
    end;

implementation

{ TMycFuture<T> }

procedure TMycFuture<T>.BeforeDestruction;
begin
    inherited;
    Assert(GetDone.IsSet, 'Trying to destroy an unfinished future');
end;

constructor TMycNullFuture<T>.Create(const AValue: T);
begin
    inherited Create;
    FValue := AValue;
end;

{ TMycNullFuture<T> }

function TMycNullFuture<T>.GetDone: TState;
begin
    Result := TState.Null;
end;

function TMycNullFuture<T>.GetValue: T;
begin
    Result := FValue;
end;

{ TMycGateFuncFuture<T> }

constructor TMycGateFuncFuture<T>.Create(const ATaskManager: TTaskManager; const AGate: TState.IState; AProc: TFunc<T>);
begin
    inherited Create;

    // Subscribe the job execution to AGate.
    // The job will run when AGate notifies the subscriber returned by Run.
    FDone :=
        ATaskManager.RunTask(
            AGate,
            function: TState
            begin
                try
                    Self.FResult := AProc();
                except
                    Self.FResult := Default(T); // Set result to Default(T) on error
                    raise; // Re-raise for TaskFactory to handle
                end;
            end
        );
end;

destructor TMycGateFuncFuture<T>.Destroy;
begin
    inherited Destroy;
end;

function TMycGateFuncFuture<T>.GetDone: TState;
begin
    Result := FDone;
end;

function TMycGateFuncFuture<T>.GetValue: T;
begin
    Assert(FDone.IsSet, 'Result is not yet available.');
    Result := FResult;
end;

{ TMycFutureManaged<T> }

constructor TMycFutureManaged<T>.Create(const AFuture: TFuture<T>.IFuture);
begin
    inherited Create;
    FFuture := AFuture;
end;

destructor TMycFutureManaged<T>.Destroy;
begin
    if GetTypeKind(T) = tkClass then
    begin
        var val := GetValue;
        var obj := TObject(PPointer(@val)^);
        if obj <> nil then
            obj.Free;
    end;
    inherited;
end;

function TMycFutureManaged<T>.GetDone: TState;
begin
    Result := FFuture.Done;
end;

function TMycFutureManaged<T>.GetValue: T;
begin
    Result := FFuture.Value;
end;

end.
