unit Myc.Mutable;

interface

uses
    System.SysUtils,
    Myc.Signals;

type
    TMutable<T> = record
        type
            IMutable = interface
                {$REGION 'property access'}
                function GetChanged: TSignal;
                function GetValue: T;
                {$ENDREGION}
                property Changed: TSignal read GetChanged;
                property Value: T read GetValue;
            end;

    {$REGION 'private'}
    strict private
        class var
            FNull: IMutable;
        class constructor CreateClass;

    private
        FMutable: IMutable;
        function GetChanged: TSignal; inline;
        function GetValue: T; inline;
    {$ENDREGION}
    public
        constructor Create(const AMutable: IMutable);
        class operator Initialize(out Dest: TMutable<T>);
        class operator Implicit(const A: IMutable): TMutable<T>; overload;
        class operator Implicit(const A: TMutable<T>): IMutable; overload;

        class property Null: IMutable read FNull;

        class function Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>; overload; static;
        class function Constant(const Value: T): TMutable<T>; overload; static;

        property Value: T read GetValue;
        property Changed: TSignal read GetChanged;
    end;

    TWriteable<T> = record
        type
            IWriteable = interface(TMutable<T>.IMutable)
                procedure SetValue(const Value: T);
            end;

    {$REGION 'private'}
    private
        FWriteable: IWriteable;
        function GetChanged: TSignal; inline;
        function GetValue: T; inline;
        procedure SetValue(const Value: T);
    {$ENDREGION}
    public
        constructor Create(const AWriteable: IWriteable);
        class operator Implicit(const A: IWriteable): TWriteable<T>; overload;
        class operator Implicit(const A: TWriteable<T>): IWriteable; overload;

        class function CreateWriteable: TWriteable<T>; overload; static;
        class function CreateWriteable(const Init: T): TWriteable<T>; overload; static;

        function Protect: TWriteable<T>;
        function AsMutable: TMutable<T>;

        property Value: T read GetValue write SetValue;
        property Changed: TSignal read GetChanged;
    end;

    TLazy<T> = record
        type
            ILazy = interface
                {$REGION 'property access'}
                function GetChanged: TState;
                {$ENDREGION}
                function Update(var Value: T): Boolean;
                property Changed: TState read GetChanged;
            end;

    {$REGION 'private'}
    strict private
        class var
            FNull: ILazy;
        class constructor CreateClass;

    private
        FLazy: ILazy;
        function GetChanged: TState; inline;
    {$ENDREGION}
    public
        constructor Create(const ALazy: ILazy);
        class operator Initialize(out Dest: TLazy<T>);
        class operator Implicit(const A: ILazy): TLazy<T>; overload;
        class operator Implicit(const A: TLazy<T>): ILazy; overload;

        class property Null: ILazy read FNull;

        class function Constant(const Value: T): TLazy<T>; overload; static;

        function Update(var Value: T): Boolean; inline;

        property Changed: TState read GetChanged;
    end;

implementation

uses
    Myc.Core.Mutable;

{ TMutable<T> }

constructor TMutable<T>.Create(const AMutable: IMutable);
begin
    if Assigned(AMutable) then
        FMutable := AMutable;
end;

class constructor TMutable<T>.CreateClass;
begin
    FNull := TMycConstMutable<T>.Create(Default(T));
end;

class function TMutable<T>.Constant(const Value: T): TMutable<T>;
begin
    Result := TMycConstMutable<T>.Create(Value);
end;

class function TMutable<T>.Construct(const Changing: TSignal; const Proc: TFunc<T>): TMutable<T>;
begin
    Result := TMycFuncMutable<T>.Create(Changing, Proc);
end;

function TMutable<T>.GetChanged: TSignal;
begin
    Result := FMutable.Changed;
end;

function TMutable<T>.GetValue: T;
begin
    Result := FMutable.Value;
end;

class operator TMutable<T>.Implicit(const A: TMutable<T>): IMutable;
begin
    Result := A.FMutable;
end;

class operator TMutable<T>.Implicit(const A: IMutable): TMutable<T>;
begin
    Result.Create(A);
end;

class operator TMutable<T>.Initialize(out Dest: TMutable<T>);
begin
    Dest.FMutable := FNull;
end;

{ TWriteable<T> }

constructor TWriteable<T>.Create(const AWriteable: IWriteable);
begin
    FWriteable := AWriteable;
    Assert(Assigned(FWriteable));
end;

function TWriteable<T>.AsMutable: TMutable<T>;
begin
    Result.Create(FWriteable);
end;

class function TWriteable<T>.CreateWriteable: TWriteable<T>;
begin
    Result := CreateWriteable(Default(T));
end;

class function TWriteable<T>.CreateWriteable(const Init: T): TWriteable<T>;
begin
    Result := TMycWriteableMutable<T>.Create(Init);
end;

function TWriteable<T>.GetChanged: TSignal;
begin
    Result := FWriteable.Changed;
end;

function TWriteable<T>.GetValue: T;
begin
    Result := FWriteable.Value;
end;

function TWriteable<T>.Protect: TWriteable<T>;
begin
    if not (FWriteable is TMycProtectedMutable<T>) then
        Result := TMycProtectedMutable<T>.Create(FWriteable)
    else
        Result := FWriteable;
end;

procedure TWriteable<T>.SetValue(const Value: T);
begin
    FWriteable.SetValue(Value);
end;

class operator TWriteable<T>.Implicit(const A: IWriteable): TWriteable<T>;
begin
    Result.Create(A);
end;

class operator TWriteable<T>.Implicit(const A: TWriteable<T>): IWriteable;
begin
    Result := A.FWriteable;
end;

{ TLazy<T> }

constructor TLazy<T>.Create(const ALazy: ILazy);
begin
    FLazy := ALazy;
end;

class constructor TLazy<T>.CreateClass;
begin
    FNull := TMycConstLazy<T>.Create(Default(T));
end;

class function TLazy<T>.Constant(const Value: T): TLazy<T>;
begin
    Result := TMycConstLazy<T>.Create(Value);
end;

function TLazy<T>.GetChanged: TState;
begin
    Result := FLazy.Changed;
end;

function TLazy<T>.Update(var Value: T): Boolean;
begin
    Result := FLazy.Update(Value);
end;

class operator TLazy<T>.Implicit(const A: TLazy<T>): ILazy;
begin
    Result := A.FLazy;
end;

class operator TLazy<T>.Implicit(const A: ILazy): TLazy<T>;
begin
    Result.FLazy := A;
end;

class operator TLazy<T>.Initialize(out Dest: TLazy<T>);
begin
    Dest.FLazy := FNull;
end;

end.
