{********************************************************

 SlavaNap source code.

 Copyright 2001,2002 by SlavaNap development team
 Released under GNU General Public License

 Latest version is available at
 http://www.slavanap.org

**********************************************************

 Unit: Class_Cmd2List

 TNapCmd2, TNapCmd2List declarations (VCL version)
 TSlavaCmd2, TSlavaCmd2List declarations (non-VCL version)

*********************************************************}
unit Class_Cmd2List;

interface

uses
  Windows, Classes2, SysUtils;

{$I Defines.pas}

type
  TNapCmd2Save = (cmd2SaveAll, Cmd2ID2CRC, Cmd2ID2CRCLC);
    // Cmd2SaveAll = save all data, Cmd2ID2CRC = Id2 = crc of cmd and not saved,
    // Cmd2ID2CRCLC = same as previous but crc is in lower case
  TNapCmd2 = Packed record
    Id1: Integer; // signed long
    Id2: LongInt; // unsigned long
    Cmd: string;
  end;
  PNapCmd2 = ^TNapCmd2;
  TNapCmd2List = class(TMyList)
    function Add(Value: TNapCmd2): Integer;
    function AddEx(Value: TNapCmd2): PNapCmd2;
    procedure Insert(Index: Integer; Value: TNapCmd2);
    procedure Clear; override;
    procedure Delete(Index: Integer);
    function AddCmd(Id1, Id2: Integer; Cmd: string): Integer;
    function AddCmdEx(Id1, Id2: Integer; Cmd: string): PNapCmd2;
    function Cmd(Index: Integer): TNapCmd2;
    function Id1(Index: Integer): Integer;
    function Id2(Index: Integer): Integer;
    function Str(Index: Integer): string;
    function FindByCmd(Cmd: string): Integer;
    function FindByCmdEx(Cmd: string): Pointer;
    constructor Create;
    destructor Destroy; override;
    function GetLength: Integer;
    procedure SaveToFile(FileName: string; Mode: TNapCmd2Save);
    procedure LoadFromFile(FileName: string; Mode: TNapCmd2Save);
    procedure SortByID1;
  end;
  PSlavaCmd2 = ^TSlavaCmd2;
  TSlavaCmd2 = Packed record
    Id1: Integer;
    Id2: Integer;
    Cmd: string;
    Prev,
      Next: PSlavaCmd2;
  end;
  TSlavaCmd2List = Packed record
    First: PSlavaCmd2;
    Count: Integer;
  end;
  PSlavaCmd2List = ^TSlavaCmd2List;

  // TNapCmd2List
function CreateCmd2List: TNapCmd2List;
procedure FreeCmd2List(List: TNapCmd2List);
{$IFNDEF DISABLE_MEMORY_MANAGER}
procedure ExpireCmd2Lists;
{$ENDIF}
// TSlavaCmd2List
function SlavaCmd2List_Create: PSlavaCmd2List;
procedure SlavaCmd2List_Free(List: PSlavaCmd2List);
procedure SlavaCmd2List_Clear(List: PSlavaCmd2List);
function SlavaCmd2List_AddItem(List: PSlavaCmd2List; Id1, Id2: Integer;
  Cmd: string): PSlavaCmd2;
procedure SlavaCmd2List_DeleteItem(List: PSlavaCmd2List; Item: PSlavaCmd2);

var
  Count_NapCmd2List,
    Count_NapCmd2List_Max: Integer;
  Count_NapCmd2List_Items,
    Count_NapCmd2List_Items_Max: Integer;

implementation

uses Vars, Thread, STypes, Memory_Manager;

{$IFNDEF DISABLE_MEMORY_MANAGER}
var
  Lists, Items: TMyList;
{$ENDIF}

function CreateCmd2List: TNapCmd2List;
begin
{$IFNDEF DISABLE_MEMORY_MANAGER}
  if Lists.Count > 0 then
  begin
    Result := Lists.Items[Lists.Count - 1];
    Lists.Delete(Lists.Count - 1);
  end
  else
{$ENDIF}
    Result := TNapCmd2List.Create;
end;

procedure FreeCmd2List(List: TNapCmd2List);
begin
{$IFNDEF DISABLE_MEMORY_MANAGER}
  List.Clear;
  Lists.Add(List);
{$ELSE}
  List.Free;
{$ENDIF}
end;

function CreateItem: PNapCmd2;
var
  Data: PNapCmd2;
begin
{$IFNDEF DISABLE_MEMORY_MANAGER}
  if Items.Count > 0 then
  begin
    Data := Items.Items[Items.Count - 1];
    Items.Delete(Items.Count - 1);
  end
  else
  begin
{$ENDIF}
    Data := AllocMem(SizeOf(TNapCmd2));
    Pointer(Data^.Cmd) := nil;
    Inc(Count_NapCmd2List_Items);
    if Count_NapCmd2List_Items > Count_NapCmd2List_Items_Max then
      Count_NapCmd2List_Items_Max := Count_NapCmd2List_Items;
{$IFNDEF DISABLE_MEMORY_MANAGER}
  end;
{$ENDIF}
  Result := Data;
end;

procedure FreeItem(Item: PNapCmd2);
begin
  if Pointer(Item^.Cmd) <> nil then
    SetLength(Item^.Cmd, 0);
  Finalize(Item^);
  FreeMem(Item, SizeOf(TNapCmd2));
  Dec(Count_NapCmd2List_Items);
end;

procedure DeleteItem(Item: PNapCmd2);
begin
  if Pointer(Item^.Cmd) <> nil then
    SetLength(Item^.Cmd, 0);
{$IFDEF DISABLE_MEMORY_MANAGER}
  FreeItem(Item);
{$ELSE}
  Items.Add(Item);
{$ENDIF}
end;

{$IFNDEF DISABLE_MEMORY_MANAGER}

procedure ExpireCmd2Lists;
var
  List: TNapCmd2List;
  Item: PNapCmd2;
begin
  if Lists.Count > 1000 then
    while (Lists.Count * 3) > Count_NapCmd2List do
    try
      List := Lists.Items[Lists.Count - 1];
      Lists.Delete(Lists.Count - 1);
      List.Free;
    except
    end;
  if Items.Count > 10000 then
    while (Items.Count * 3) > Count_NapCmd2List_Items do
    try
      Item := Items.Items[Items.Count - 1];
      Items.Delete(Items.Count - 1);
      FreeItem(Item);
    except
    end;
end;
{$ENDIF}

{* * * * *  TNapCmd2List  * * * * *}

function TNapCmd2List.Add(Value: TNapCmd2): Integer;
var
  Data: PNapCmd2;
begin
  Data := CreateItem;
  with Data^ do
  begin
    Cmd := Value.Cmd;
    Id1 := Value.Id1;
    Id2 := Value.Id2;
  end;
  Result := inherited Add(Data);
end;

function TNapCmd2List.AddEx(Value: TNapCmd2): PNapCmd2;
var
  Data: PNapCmd2;
begin
  Data := CreateItem;
  with Data^ do
  begin
    Cmd := Value.Cmd;
    Id1 := Value.Id1;
    Id2 := Value.Id2;
  end;
  inherited Add(Data);
  Result := Data;
end;

function TNapCmd2List.AddCmd(Id1, Id2: Integer; Cmd: string): Integer;
var
  Data: PNapCmd2;
begin
  Data := CreateItem;
  Data^.Cmd := Cmd;
  Data^.Id1 := Id1;
  Data^.Id2 := Id2;
  Result := inherited Add(Data);
end;

function TNapCmd2List.AddCmdEx(Id1, Id2: Integer; Cmd: string): PNapCmd2;
var
  Data: PNapCmd2;
begin
  Data := CreateItem;
  Data^.Cmd := Cmd;
  Data^.Id1 := Id1;
  Data^.Id2 := Id2;
  inherited Add(Data);
  Result := Data;
end;

procedure TNapCmd2List.Insert(Index: Integer; Value: TNapCmd2);
var
  Data: PNapCmd2;
begin
  Data := CreateItem;
  with Data^ do
  begin
    Cmd := Value.Cmd;
    Id1 := Value.Id1;
    Id2 := Value.Id2;
  end;
  inherited Insert(Index, Data);
end;

procedure TNapCmd2List.Clear;
begin
  while Count > 0 do
    Delete(Count - 1);
  inherited Clear;
end;

procedure TNapCmd2List.Delete(Index: Integer);
begin
  if (Index < 0) or (Index >= Count) then Exit;
  if Items[Index] <> nil then
    DeleteItem(Items[Index]);
  inherited Delete(Index);
end;

function TNapCmd2List.Cmd(Index: Integer): TNapCmd2;
var
  Data: TNapCmd2;
begin
  if (Index >= 0) and (Index < Count) then
  begin
    Result := TNapCmd2(Items[Index]^);
    Exit;
  end;
  Data.Id1 := -1;
  Data.Id2 := -1;
  Data.Cmd := '';
  Result := Data;
end;

function TNapCmd2List.Id1(Index: Integer): Integer;
var
  Data: PNapCmd2;
begin
  if (Index >= 0) and (Index < Count) then
  begin
    Data := PNapCmd2(Items[Index]);
    Result := Data^.Id1;
    Exit;
  end;
  Result := -1;
end;

function TNapCmd2List.Id2(Index: Integer): Integer;
var
  Data: PNapCmd2;
begin
  if (Index >= 0) and (Index < Count) then
  begin
    Data := PNapCmd2(Items[Index]);
    Result := Data^.Id2;
    Exit;
  end;
  Result := -1;
end;

function TNapCmd2List.Str(Index: Integer): string;
var
  Data: PNapCmd2;
begin
  if (Index >= 0) and (Index < Count) then
  begin
    Data := PNapCmd2(Items[Index]);
    Result := Data^.Cmd;
    Exit;
  end;
  Result := '';
end;

function TNapCmd2List.FindByCmd(Cmd: string): Integer;
var
  I: Integer;
begin
  Cmd := AnsiLowerCase(Cmd);
  for I := 0 to Count - 1 do
    if AnsiLowerCase(PNapCmd2(Items[I]).Cmd) = Cmd then
    begin
      Result := I;
      Exit;
    end;
  Result := -1;
end;

function TNapCmd2List.FindByCmdEx(Cmd: string): Pointer;
// Warning: This function Doesn't Ignore letters case
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    if PNapCmd2(Items[I]).Cmd = Cmd then
    begin
      Result := Items[I];
      Exit;
    end;
  Result := nil;
end;

constructor TNapCmd2List.Create;
begin
  Inc(Count_NapCmd2List);
  if Count_NapCmd2List > Count_NapCmd2List_Max then
    Count_NapCmd2List_Max := Count_NapCmd2List;
  inherited Create;
end;

destructor TNapCmd2List.Destroy;
begin
  Clear;
  Dec(Count_NapCmd2List);
  inherited Destroy;
end;

function TNapCmd2List.GetLength: Integer;
var
  I, J: Integer;
begin
  J := 0;
  for I := 0 to Count - 1 do
    Inc(J, Length(PNapCmd2(Items[I])^.Cmd));
  Result := J;
end;

procedure TNapCmd2List.SaveToFile(FileName: string; Mode: TNapCmd2Save);
var
  List: TMyStringList;
  I: Integer;
  Item: PNapCmd2;
begin
  List := TMyStringList.Create;
  for I := 0 to Count - 1 do
  begin
    Item := Items[I];
    case mode of
      Cmd2SaveAll: List.Add(IntToStr(Item^.Id1) + ' ' + IntToStr(Item^.Id2) + ' '
        + Item^.Cmd);
      Cmd2ID2CRC, Cmd2ID2CRCLC: List.Add(IntToStr(Item^.Id1) + ' ' + Item^.Cmd);
    end;
    if (I mod 200) = 50 then
    begin
{$I CheckSync.pas}
    end;
  end;
  try
    List.SaveToFile(FileName);
  except
  end;
  List.Free;
end;

procedure TNapCmd2List.LoadFromFile(FileName: string; Mode: TNapCmd2Save);
var
  List: TMyStringList;
  I, J, K: Integer;
  Str: string;
begin
  List := TMyStringList.Create;
  Clear;
  try
    List.LoadFromFile(FileName);
  except
    List.Free;
    Exit;
  end;
  for I := 0 to List.Count - 1 do
  begin
    Str := FirstParam(List.Strings[I]);
    if IsDigit(Str) then
    begin
      J := StrToIntDef(Str, 0);
      Str := NextParamEx(List.Strings[I]);
      if Mode = Cmd2SaveAll then
      begin
        J := StrToIntDef(FirstParam(Str), 0);
        Str := NextParamEx(Str);
      end
      else
      begin
        if Mode = Cmd2ID2CRC then
          K := StringCRC(Str, False)
        else
          K := StringCRC(Str, True);
      end;
      AddCmd(J, K, Str);
    end;
  end;
  List.Free;
end;

procedure TNapCmd2List.SortByID1;
var
  I, J: Integer;
  T: Pointer;
begin
  if Count < 2 then Exit;
  for I := 0 to Count - 2 do
    for J := I + 1 to Count - 1 do
    begin
      if PNapCmd2(Items[I])^.Id1 > PNapCmd2(Items[J])^.Id1 then
      begin
        T := Items[I];
        Items[I] := Items[J];
        Items[J] := T;
      end;
    end
end;

{$IFNDEF DISABLE_MEMORY_MANAGER}

procedure ClearCmd2Lists;
var
  I: Integer;
  P: PNapCmd2;
begin
  for I := 0 to Lists.Count - 1 do
    TNapCmd2List(Lists.Items[I]).Free;
  Lists.Clear;
  for I := 0 to Items.Count - 1 do
  begin
    P := Items.Items[I];
    FreeItem(P);
  end;
  Items.Clear;
end;
{$ENDIF}

function SlavaCmd2List_Create: PSlavaCmd2List;
begin
  Result := AllocMem(SizeOf(TSlavaCmd2List));
  Result^.First := nil;
  Result^.Count := 0;
end;

procedure SlavaCmd2List_Free(List: PSlavaCmd2List);
begin
  SlavaCmd2List_Clear(List);
  FreeMem(List, SizeOf(TSlavaCmd2List));
end;

procedure SlavaCmd2List_Clear(List: PSlavaCmd2List);
var
  P, N: PSlavaCmd2;
begin
  P := List^.First;
  while P <> nil do
  begin
    N := P^.Next;
    P^.Cmd := '';
    FreeMem(P, SizeOf(TSlavaCmd2));
    P := N;
  end;
  List^.Count := 0;
  List^.First := nil;
end;

function SlavaCmd2List_AddItem(List: PSlavaCmd2List; Id1, Id2: Integer;
  Cmd: string): PSlavaCmd2;
begin
  Result := AllocMem(SizeOf(TSlavaCmd2));
  Result^.Next := List^.First;
  Pointer(Result^.Cmd) := nil;
  Result^.Id1 := Id1;
  Result^.Id2 := Id2;
  Result^.Cmd := Cmd;
  if List^.First <> nil then
    List^.First^.Prev := Result;
  List^.First := Result;
  Inc(List^.Count);
end;

procedure SlavaCmd2List_DeleteItem(List: PSlavaCmd2List; Item: PSlavaCmd2);
var
  Prev, Next: PSlavaCmd2;
begin
  Prev := Item^.Prev;
  Next := Item^.Next;
  Item^.Cmd := '';
  FreeMem(Item, SizeOf(TSlavaCmd2));
  if Prev = nil then
  begin
    List^.First := Next;
    Next^.Prev := nil;
  end
  else
  begin
    Prev^.Next := Next;
    if Next <> nil then
      Next^.Prev := Prev;
  end;
  Dec(List^.Count);
end;

initialization
  begin
    Count_NapCmd2List := 0;
    Count_NapCmd2List_Max := 0;
    Count_NapCmd2List_Items := 0;
    Count_NapCmd2List_Items_Max := 0;
{$IFNDEF DISABLE_MEMORY_MANAGER}
    Lists := TMyList.Create;
    Items := TMyList.Create;
{$ENDIF}
  end;

{$IFNDEF DISABLE_MEMORY_MANAGER}
finalization
  begin
    ClearCmd2Lists;
    Lists.Free;
    Items.Free;
  end;
{$ENDIF}

end.
