unit uControllerBase;

interface

uses
  Classes, uDADataTable, Forms;

type
  ISujeto = interface;

  IObservador = interface (IInterface)
  ['{679D5CF2-D5DC-4A52-9FF3-04AD91402483}']
    procedure AddSujeto(Sujeto: ISujeto);
    procedure DeleteSujeto(Sujeto: ISujeto);
    procedure RecibirAviso(ASujeto: ISujeto); overload;
    procedure RecibirAviso(ASujeto: ISujeto; ADataTable: IDAStronglyTypedDataTable); overload;
  end;

  ISujeto = interface (IInterface)
  ['{CDB691CD-D1D6-4F2E-AA34-93B1CD0E6030}']
    procedure AddObservador(Observador: IObservador);
    procedure DeleteObservador(Observador: IObservador);
  end;

  { ******************* PARA PRUEBAS ******************************************}
  TMiInterfacedObject = class(TObject, IInterface)
  protected
    FRefCount: Integer;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; virtual; stdcall;
    function GetRefCount : Integer;
  public
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    class function NewInstance: TObject; override;
    property RefCount: Integer read FRefCount;
  end;


  { ***************************************************************************}


  TObservador = class(TObject, IObservador)
  private
    fSujetos: IInterfaceList;
  protected
    FRefCount: Integer;
    procedure RecibirAviso(ASujeto: ISujeto); overload; virtual;
    procedure RecibirAviso(ASujeto: ISujeto; ADataTable: IDAStronglyTypedDataTable); overload; virtual; abstract;
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    function GetRefCount : Integer;
  public
    constructor Create; virtual;
    procedure AddSujeto(Sujeto: ISujeto);
    procedure DeleteSujeto(Sujeto: ISujeto);
    destructor Destroy; override;

    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
    class function NewInstance: TObject; override;
    property RefCount: Integer read FRefCount;
  end;

  TSujeto = class(TInterfacedObject, ISujeto)
  private
    fObservadores: IInterfaceList;
  protected
    procedure AvisarObservadores; overload;
    procedure AvisarObservadores(ADataTable: IDAStronglyTypedDataTable); overload;
  public
    constructor Create; virtual;
    procedure AddObservador(Observador: IObservador);
    procedure DeleteObservador(Observador: IObservador);
    destructor Destroy; override;
  end;

  IControllerBase = IObservador;
  TControllerBase = TObservador;

implementation

uses
  Dialogs, SysUtils;


function InterlockedIncrement(var I: Integer): Integer;
asm
      MOV   EDX,1
      XCHG  EAX,EDX
 LOCK XADD  [EDX],EAX
      INC   EAX
end;

function InterlockedDecrement(var I: Integer): Integer;
asm
      MOV   EDX,-1
      XCHG  EAX,EDX
 LOCK XADD  [EDX],EAX
      DEC   EAX
end;

{ TMiInterfacedObject }

procedure TMiInterfacedObject.AfterConstruction;
begin
// Release the constructor's implicit refcount
  InterlockedDecrement(FRefCount);
end;

procedure TMiInterfacedObject.BeforeDestruction;
begin
//  if RefCount <> 0 then
//    Error(reInvalidPtr);
end;

function TMiInterfacedObject.GetRefCount: Integer;
begin
  Result := FRefCount;
end;

// Set an implicit refcount so that refcounting
// during construction won't destroy the object.
class function TMiInterfacedObject.NewInstance: TObject;
begin
  Result := inherited NewInstance;
  TMiInterfacedObject(Result).FRefCount := 1;
end;

function TMiInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

function TMiInterfacedObject._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
//  ShowMessage('_AddRef: ' + ClassName + ' - RefCount: ' + IntToStr(FRefCount));
end;

function TMiInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
//  ShowMessage('_Release: ' + ClassName + ' - RefCount: ' + IntToStr(FRefCount));
  if Result = 0 then
    Destroy;
end;



{ TSujeto }

procedure TSujeto.addObservador(Observador: IObservador);
begin
  FObservadores.Add(Observador);
  Observador.AddSujeto(Self);
end;

procedure TSujeto.AvisarObservadores;
var
  i: Integer;
  AObs : IObservador;
begin
  for i := 0 to Pred(FObservadores.Count) do
  begin
     if Supports(FObservadores[i], IObservador, AObs) then
        AObs.RecibirAviso(Self);
  end;
end;

procedure TSujeto.AvisarObservadores(ADataTable: IDAStronglyTypedDataTable);
var
  i: Integer;
  AObs : IObservador;
begin
  for i := 0 to Pred(FObservadores.Count) do
  begin
     if Supports(FObservadores[i], IObservador, AObs) then
        AObs.RecibirAviso(Self, ADataTable);
  end;
end;

constructor TSujeto.Create;
begin
  inherited;
  FObservadores := TInterfaceList.Create;
end;

procedure TSujeto.DeleteObservador(Observador: IObservador);
begin
  FObservadores.Remove(Observador);
end;

destructor TSujeto.Destroy;
begin
  FObservadores := NIL;
  inherited;
end;

{ TObservador }

procedure TObservador.RecibirAviso(ASujeto: ISujeto);
begin
  //
end;

function TObservador._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
//  ShowMessage('_AddRef: ' + ClassName + ' - RefCount: ' + IntToStr(FRefCount));
end;

function TObservador._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
//  ShowMessage('_Release: ' + ClassName + ' - RefCount: ' + IntToStr(FRefCount));
//  if (Result = 0)
  if (Assigned(fSujetos) and (fSujetos.Count = FRefCount)) then
     Destroy;
end;

procedure TObservador.AddSujeto(Sujeto: ISujeto);
begin
  FSujetos.Add(Sujeto);
end;

procedure TObservador.AfterConstruction;
begin
// Release the constructor's implicit refcount
  InterlockedDecrement(FRefCount);
end;

procedure TObservador.BeforeDestruction;
begin
//  if RefCount <> 0 then
//    Error(reInvalidPtr);
end;

constructor TObservador.Create;
begin
  inherited;
  FSujetos := TInterfaceList.Create;
end;

procedure TObservador.DeleteSujeto(Sujeto: ISujeto);
begin
  FSujetos.Remove(Sujeto);
end;

destructor TObservador.Destroy;
begin
  FSujetos := NIL;
  inherited;
end;

function TObservador.GetRefCount: Integer;
begin
  Result := FRefCount;
end;

class function TObservador.NewInstance: TObject;
begin
  Result := inherited NewInstance;
  TObservador(Result).FRefCount := 1;
end;

function TObservador.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

end.
