//******************************************************************************
//***                     COMMON DELPHI FUNCTIONS                            ***
//***                                                                        ***
//***    (c) Beppe Grimaldi, Massimo Magnano 11-11-2004.                     ***
//***                                                                        ***
//***                                                                        ***
//******************************************************************************
//  File        : MGList.pas      REV. 1.6   (13-09-2006)
//
//  Description : Implementation of an Optimazed and Polimorphic List.
//
//******************************************************************************

unit MGList;
{$mode delphi}{$H+}

interface

Type
  PDataExt = ^TDataExt;
  TDataExt = record
     Data         :Pointer;
     Prev         :PDataExt;
     Next         :PDataExt;
  end;

  //I Tag sono necessari xche' Non posso leggere le variabili che stanno nello Stack
  //quindi devo passare le variabile necessarie alle funzioni locali di compare cosė
  //EN the Tags are needed because i cannot read variables from the Stack
  //so i must pass the variables that i need in the local compare function int this way
  TLocalCompareFunction = function (Tag :Pointer; ptData1, ptData2 :Pointer) :Boolean;
  TLocalWalkFunction = procedure (Tag :Integer; ptData :Pointer);
  TObjCompareFunction = function (Tag :Pointer; ptData1, ptData2 :Pointer) :Boolean of object;
  PObjCompareFunction = ^TObjCompareFunction;
  TObjWalkFunction = procedure (Tag :Integer; ptData :Pointer) of object;


  { TMGList }

  TMGList = class
    protected
        rListInit,
        rListEnd,
        rCurrent     :PDataExt;
        rCount       :Integer;

        function Get(Index: Integer): Pointer;
        function InternalDelete(Item :PDataExt) :PDataExt; overload;
        function InternalFind(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=nil) :PDataExt; virtual;
        function PutInRightPosition(newElem :PDataExt; ATag :Pointer; CompareFunction : TLocalCompareFunction=nil) :Integer; overload; virtual;
        function PutInRightPosition(newElem :PDataExt; ATag :Pointer; CompareFunction : TObjCompareFunction) :Integer; overload; virtual;
        function allocData :Pointer; virtual;
        procedure deallocData(pData :Pointer); virtual;
        function RefreshOK(pData :Pointer) : Boolean; virtual;
    public
        constructor Create; virtual;
        destructor Destroy; override;

        function Find(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=nil): Integer; overload;
        function Find(pData :Pointer; ATag :Pointer; CompareFunction : TObjCompareFunction): Integer; overload;
        function Find(const Args: array of Variant): Pointer; overload; virtual;
        function ExtFind(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=nil): Pointer; overload;
        function ExtFind(pData :Pointer; ATag :Pointer; CompareFunction : TObjCompareFunction): Pointer; overload;
        procedure Walk(ATag :Integer; WalkFunction : TLocalWalkFunction); overload;
        procedure Walk(ATag :Integer; WalkFunction : TObjWalkFunction); overload;
        procedure WalkAndRefresh(ATag :Integer; WalkFunction : TLocalWalkFunction); overload;
        procedure WalkAndRefresh(ATag :Integer; WalkFunction : TObjWalkFunction); overload;

        function Add :Pointer; overload;
        function Insert(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=nil) :Integer; overload;
        function Insert(pData :Pointer; ATag :Pointer; CompareFunction : TObjCompareFunction) :Integer; overload;
        function DeleteFirst :Boolean;
        function DeleteLast :Boolean;
        function Delete(Index :Integer) :Boolean; overload;
        function Delete(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=Nil) :Boolean; overload;
        function Delete(pData :Pointer; ATag :Pointer; CompareFunction : TObjCompareFunction) :Boolean; overload;
        procedure Exchange(pData1, pData2 :Pointer); overload; virtual;

        procedure Clear;
        procedure Refresh;

        function FindFirst: Pointer; virtual;
        function FindNext : Pointer; virtual;
        function GetFirst : Pointer;
        function GetLast : Pointer;
        function GetCurrent : Pointer; virtual;
        function GetData(DataPointer :Pointer; DataName :String) :Variant; virtual;
        function DeleteCurrent :Boolean;
        procedure FindClose; virtual;

        property Count :Integer read rCount;
        property Items [Index :Integer] :Pointer read Get;
  end;

  TMGListClass = class of TMGList;


  TMGObjectWithCreate = class(TObject)
  public
     constructor Create(dummy :Boolean); virtual;
  end;

  TObjectWCClass = class of TMGObjectWithCreate;

  TMGObject_List = class(TMGList)
  protected
       function allocData :Pointer; override;
       procedure deallocData(pData :Pointer); override;
       function GetObjectClass :TObjectWCClass; virtual; abstract;
  end;

  TMGList_List = class(TMGList)
  protected
       function allocData :Pointer; override;
       procedure deallocData(pData :Pointer); override;
       function GetObjectClass :TMGListClass; virtual; abstract;
  end;


implementation

Type
    TLocalToObjData_Compare = record
        Tag  :Pointer;
        Func :TObjCompareFunction;
    end;
    PLocalToObjData_Compare = ^TLocalToObjData_Compare;

    TLocalToObjData_Walk = record
        Tag  :Integer;
        Func :TObjWalkFunction;
    end;
    PLocalToObjData_Walk = ^TLocalToObjData_Walk;


function _localToObj_Compare(xTag :Pointer; ptData1, ptData2 :Pointer) :Boolean;
begin
//   try
     Result := PLocalToObjData_Compare(xTag).Func(
                                     PLocalToObjData_Compare(xTag).Tag,
                                     ptData1, ptData2);
(*   except
     Result :=False;
   end;*)
end;

procedure _localToObj_Walk(xTag :Integer; ptData :Pointer);
begin
     PLocalToObjData_Walk(xTag).Func(PLocalToObjData_Walk(xTag).Tag, ptData);
end;

function AllocData_Compare(Tag :Pointer; Func :TObjCompareFunction) :PLocalToObjData_Compare;
begin
     GetMem(Result, sizeOf(TLocalToObjData_Compare));
     Result^.Tag :=Tag;
     Result^.Func :=Func;
end;

function AllocData_Walk(Tag :Integer; Func :TObjWalkFunction) :PLocalToObjData_Walk;
begin
     GetMem(Result, sizeOf(TLocalToObjData_Walk));
     Result^.Tag :=Tag;
     Result^.Func :=Func;
end;

function CompByData(xTag :Pointer; ptData1, ptData2 :Pointer) :Boolean;
begin
     Result := (ptData1 = ptData2);
end;



// =============================================================================

constructor TMGList.Create;
begin
   rCount := 0;
   rListInit := Nil;
   rListEnd := Nil;
   rCurrent := Nil;
end;

destructor TMGList.Destroy;
begin
   Clear;
end;

function TMGList.allocData :Pointer;
begin
     Result :=Nil;
end;

procedure TMGList.deallocData(pData :Pointer);
begin
end;

function TMGList.RefreshOK(pData :Pointer) : Boolean;
begin
     Result :=True;
end;

procedure TMGList.Clear;
var
   pIndex :PDataExt;
begin
   while (rListInit <> Nil) do
     begin
        pIndex := rListInit;
        rListInit := rListInit^.Next;
        deallocData(pIndex^.Data);
        Dispose(pIndex);
     end;
   rListInit := Nil;
   rListEnd := Nil;
   rCount := 0;
end;

procedure TMGList.Refresh;
var
   pIndex :PDataExt;
begin
   pIndex := rListInit;
   while (pIndex <> Nil) do
     begin
        if RefreshOK(pIndex^.Data)
          then pIndex := pIndex^.Next
          else begin
                  if (pIndex^.Next = Nil)  // se č l'ultimo elemento..
                    then rListEnd := pIndex^.Prev;
                  pIndex := InternalDelete(pIndex);
               end;
     end;
end;

function TMGList.FindFirst: Pointer;
begin
     if (rCurrent=Nil)
      then begin
                rCurrent :=rListInit;
                Result :=GetCurrent;
           end
      else Result :=Nil;
end;

function TMGList.FindNext : Pointer;
begin
     if (rCurrent<>Nil)
      then begin
                rCurrent :=rCurrent^.Next;
                Result :=GetCurrent;
           end
      else Result :=Nil;
end;

function TMGList.GetFirst: Pointer;
begin
     Result :=rListInit^.Data;
end;

function TMGList.GetLast: Pointer;
begin
     Result :=rListEnd^.Data;
end;

function TMGList.GetCurrent : Pointer;
begin
     if (rCurrent=Nil)
     then Result :=Nil
     else Result :=rCurrent^.Data;
end;

function TMGList.GetData(DataPointer :Pointer; DataName :String) :Variant;
begin
     Result :=Variant(Integer(DataPointer));
end;

function TMGList.DeleteCurrent :Boolean;
begin
   Result := False;
   if (rCurrent <> Nil) then
     begin
        rCurrent := InternalDelete(rCurrent);
        Result := True;
     end;
end;

procedure TMGList.FindClose;
begin
     rCurrent :=Nil;
end;

function TMGList.Get(Index: Integer): Pointer;
var
   I :Integer;
   pIndex :PDataExt;

begin
   Result := Nil;
   if ((Index >= 0) and (Index < rCount)) then
     begin
        pIndex := rListInit;
        for i:=0 to Index-1 do
          pIndex := pIndex^.Next;
        Result := pIndex^.Data;
     end;
end;

function TMGList.Find(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=nil): Integer;
var
   i :Integer;
   Found :Boolean;
   pIndex :PDataExt;

begin
   if not(Assigned(CompareFunction))
   then CompareFunction :=CompByData;

   Result := -1;
   i := 0;
   Found := False;
   pIndex := rListInit;
   while ((i < rCount) and not Found) do
     if CompareFunction(ATag, pData, pIndex^.Data)
       then begin
               Result := i;
               Found := True;
            end
       else begin
               Inc(i);
               pIndex := pIndex^.Next;
            end;
end;

function TMGList.Find(pData :Pointer; ATag :Pointer; CompareFunction : TObjCompareFunction): Integer;
Var
  auxPointer :PLocalToObjData_Compare;

begin
     auxPointer :=AllocData_Compare(ATag, CompareFunction);
     Result := Find(pData, auxPointer, _LocalToObj_Compare);
     FreeMem(auxPointer);
end;

function TMGList.Find(const Args: array of Variant): Pointer;
begin
     Result :=Nil;
end;

function TMGList.ExtFind(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=nil): Pointer;
var
   Found  :Boolean;
   pIndex :PDataExt;

begin
   if not(Assigned(CompareFunction))
   then CompareFunction :=CompByData;

   Result := Nil;
   Found := False;
   pIndex := rListInit;
   while ((pIndex <> Nil) and not Found) do
     if CompareFunction(ATag, pData, pIndex^.Data)
       then begin
               Result := pIndex^.Data;
               Found := True;
            end
       else pIndex := pIndex^.Next;
end;


function TMGList.ExtFind(pData :Pointer; ATag :Pointer; CompareFunction : TObjCompareFunction): Pointer;
Var
  auxPointer :PLocalToObjData_Compare;

begin
     auxPointer :=AllocData_Compare(ATag, CompareFunction);
     Result := ExtFind(pData, auxPointer, _LocalToObj_Compare);
     FreeMem(auxPointer);
end;

procedure TMGList.Walk(ATag :Integer; WalkFunction : TLocalWalkFunction);
var
   pIndex :PDataExt;

begin
     pIndex := rListInit;
     while (pIndex <> Nil) do
     begin
          WalkFunction(ATag, pIndex^.Data);
          pIndex := pIndex^.Next;
     end;
end;

procedure TMGList.Walk(ATag :Integer; WalkFunction : TObjWalkFunction);
Var
  auxPointer :PLocalToObjData_Walk;

begin
     auxPointer :=AllocData_Walk(ATag, WalkFunction);
     Walk(Integer(auxPointer), _LocalToObj_Walk);
     FreeMem(auxPointer);
end;

procedure TMGList.WalkAndRefresh(ATag :Integer; WalkFunction : TLocalWalkFunction);
var
   pIndex :PDataExt;

begin
     pIndex := rListInit;
     while (pIndex <> Nil) do
     begin
          if RefreshOk(pIndex^.Data)
          then begin
                    WalkFunction(ATag, pIndex^.Data);
                    pIndex := pIndex^.Next;
               end
          else begin
                  if (pIndex^.Next = Nil)  // se č l'ultimo elemento..
                    then rListEnd := pIndex^.Prev;
                  pIndex := InternalDelete(pIndex);
               end;
     end;
end;

procedure TMGList.WalkAndRefresh(ATag :Integer; WalkFunction : TObjWalkFunction);
Var
  auxPointer :PLocalToObjData_Walk;

begin
     auxPointer :=AllocData_Walk(ATag, WalkFunction);
     WalkAndRefresh(Integer(auxPointer), _LocalToObj_Walk);
     FreeMem(auxPointer);
end;


function TMGList.Add :Pointer;
var
   newElem :PDataExt;

begin
   new(newElem);
   fillchar(newElem^, sizeof(TDataExt), 0);
   newElem^.Data := allocData;

   if (rListEnd = Nil)
     then begin
             rListInit := newElem;
             rListEnd := newElem;
          end
     else begin
             rListEnd^.Next := newElem;
             newElem^.Prev := rListEnd;
             rListEnd := newElem;
          end;
   Inc(rCount);
   Result := newElem^.Data;
end;

function TMGList.PutInRightPosition(newElem :PDataExt; ATag :Pointer; CompareFunction : TLocalCompareFunction) :Integer;
var
   Found   :Boolean;
   pIndex  :PDataExt;

begin
   if not(Assigned(CompareFunction))
   then CompareFunction :=CompByData;

   Result := 0;
   if (rListInit = Nil)
     then begin
             rListInit := newElem;
             rListEnd := newElem;
          end
     else begin
             Found := False;
             pIndex := rListInit;
             repeat
               if CompareFunction(ATag, newElem^.Data, pIndex^.Data)
                 then begin
                         // uso 'newElem^.Prev' per conservare il puntatore al record precedente..
                         newElem^.Prev := pIndex;
                         pIndex := pIndex^.Next;
                      end
                 else Found := True;
               Inc(Result);
             until ((pIndex = Nil) or Found);

             if (newElem^.Prev = Nil)  // inserisco in prima posizione..
               then rListInit := newElem
               else newElem^.Prev^.Next := newElem;
             newElem^.Next := pIndex;
             if (pIndex <> Nil)
               then pIndex^.Prev := newElem
               else rListEnd := newElem;  // inserisco in ultima posizione..
          end;
end;

function TMGList.PutInRightPosition(newElem :PDataExt; ATag :Pointer; CompareFunction : TObjCompareFunction) :Integer;
Var
  auxPointer :PLocalToObjData_Compare;

begin
     auxPointer :=AllocData_Compare(ATag, CompareFunction);
     Result := PutInRightPosition(newElem, auxPointer, _LocalToObj_Compare);
     FreeMem(auxPointer);
end;

function TMGList.Insert(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=Nil) :Integer;
var
   newElem :PDataExt;
   
begin
   if not(Assigned(CompareFunction))
   then CompareFunction :=CompByData;

   new(newElem);
   fillchar(newElem^, sizeof(TDataExt), 0);
   newElem^.Data :=pData;

   Result := PutInRightPosition(pData, ATag, CompareFunction);
   Inc(rCount);
end;

function TMGList.Insert(pData :Pointer; ATag :Pointer; CompareFunction : TObjCompareFunction) :Integer;
Var
  auxPointer :PLocalToObjData_Compare;

begin
     auxPointer :=AllocData_Compare(ATag, CompareFunction);
     Result := Insert(pData, auxPointer, _LocalToObj_Compare);
     FreeMem(auxPointer);
end;

function TMGList.DeleteFirst: Boolean;
begin
     try
        InternalDelete(rListInit);
        Result :=True;
     except
       Result :=False;
     end;
end;

function TMGList.DeleteLast: Boolean;
begin
     try
        InternalDelete(rListEnd);
        Result :=True;
     except
       Result :=False;
     end;
end;


function TMGList.Delete(Index :Integer) :Boolean;
var
   i      :Integer;
   pIndex :PDataExt;

begin
   Result := False;
   if ((Index >= 0) and (Index < rCount)) then
     begin
        pIndex := rListInit;
        for i:=0 to Index-1 do
          pIndex := pIndex^.Next;

        if (pIndex = Nil)
          then InternalDelete(rListEnd)
          else InternalDelete(pIndex);

        Result := True;
     end;
end;

function TMGList.Delete(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=Nil) :Boolean;
Var
   toDel :PDataExt;

begin
     if not(Assigned(CompareFunction))
     then CompareFunction :=CompByData;

     toDel := InternalFind(pData, ATag, CompareFunction);
     Result := (toDel<>Nil);
     if Result
     then InternalDelete(toDel);
end;

function TMGList.Delete(pData :Pointer; ATag :Pointer; CompareFunction : TObjCompareFunction) :Boolean;
Var
  auxPointer :PLocalToObjData_Compare;

begin
     auxPointer :=AllocData_Compare(ATag, CompareFunction);
     Result := Delete(pData, auxPointer, _LocalToObj_Compare);
     FreeMem(auxPointer);
end;

procedure TMGList.Exchange(pData1, pData2 :Pointer);
var
   pIndex,
   pIndexData1,
   pIndexData2  :PDataExt;
   xData        :Pointer;


begin
   pIndex := rListInit;
   pIndexData1 :=Nil;
   pIndexData2 :=Nil;
   while ((pIndex <> Nil) and ((pIndexData1=Nil) or (pIndexData2=Nil))) do
   begin
        if (pIndex^.Data=pData1)
        then pIndexData1 :=pIndex
        else if (pIndex^.Data=pData2)
             then pIndexData2 :=pIndex;

        pIndex := pIndex^.Next;
   end;

   if ((pIndexData1<>Nil) and (pIndexData2<>Nil)) then
   begin
        xData := pIndexData1^.Data;
        pIndexData1^.Data := pIndexData2^.Data;
        pIndexData2^.Data := xData;
   end;
end;


function TMGList.InternalDelete(Item :PDataExt) :PDataExt;
var
   P :PDataExt;
begin
   Result := Nil;
   P := PDataExt(Item);
   if (P <> Nil) then
     begin
        if (P^.Prev <> Nil)
          then P^.Prev^.Next := P^.Next
          else rListInit := P^.Next;
        if (P^.Next <> Nil)
          then P^.Next^.Prev := P^.Prev
          else rListEnd := P^.Prev;  // sto cancellando l'ultimo elemento..

        Result := P^.Prev;
        deallocData(P^.Data);
        Dispose(P);
        Dec(rCount);
     end;
end;


function TMGList.InternalFind(pData :Pointer; ATag :Pointer; CompareFunction : TLocalCompareFunction=nil) :PDataExt;
var
   Found  :Boolean;
   pIndex :PDataExt;

begin
   if not(Assigned(CompareFunction))
   then CompareFunction :=CompByData;

   Result := Nil;
   Found := False;
   pIndex := rListInit;
   while ((pIndex <> Nil) and not Found) do
     if CompareFunction(ATag, pData, pIndex^.Data)
       then begin
               Result := pIndex;
               Found := True;
            end
       else pIndex := pIndex^.Next;
end;

//==============================================================================
//  TMGObject_List = class(TMGList)

constructor TMGObjectWithCreate.Create(dummy :Boolean);
begin
     inherited Create;
end;

function TMGObject_List.allocData :Pointer;
begin
     Result :=GetObjectClass.Create(true); //Why Tobject.Create is not virtual???
end;

procedure TMGObject_List.deallocData(pData :Pointer);
begin
     TObject(pData).Free;
end;

//==============================================================================
//  TMGList_List = class(TMGList)

function TMGList_List.allocData :Pointer;
begin
     Result :=GetObjectClass.Create;
end;

procedure TMGList_List.deallocData(pData :Pointer);
begin
     TMGList(pData).Free;
end;


end.
