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

 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_Cmdlist

 TNapCmd and TNapCmdList declarations

*********************************************************}
unit Class_Cmdlist;

interface

uses
  Windows, Classes2, SysUtils;

{$I Defines.pas}

type
  TNapCmd = record
    Id: Integer;
    Cmd: string;
  end;
  PNapCmd = ^TNapCmd;
  TNapCmdList = class(TMyList)
    function Add(Value: TNapCmd): Integer;
    procedure Insert(Index: Integer; Value: TNapCmd);
    procedure Clear; override;
    procedure Delete(Index: Integer);
    function AddCmd(Id: Integer; Cmd: string): Integer;
    function Cmd(Index: Integer): TNapCmd;
    function Id(Index: Integer): Integer;
    function Str(Index: Integer): string;
    function FindByCmd(Cmd: string; Ignore_case: Boolean): Integer;
    function FindById(Id: Integer): Integer;
    function FindItem(Id: Integer; Cmd: string): Integer;
    procedure SaveToFile(FileName: string);
    procedure LoadFromFile(FileName: string);
    procedure Sort;
    constructor Create;
    destructor Destroy; override;
    function GetLength: Integer;
  end;

function CreateCmdList: TNapCmdList;
procedure FreeCmdList(List: TNapCmdList);
{$IFNDEF DISABLE_MEMORY_MANAGER}
procedure ExpireCmdLists;
{$ENDIF}

var
  Count_Napcmdlist,
    Count_Napcmdlist_Max: Integer;
  Count_Napcmdlist_Items,
    Count_Napcmdlist_Items_Max: Integer;

implementation

uses Vars, Thread, STypes, Memory_Manager;

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

function CreateCmdList: TNapCmdList;
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 := TNapCmdList.Create;
end;

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

function CreateItem: PNapCmd;
var
  Data: PNapCmd;
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(TNapCmd));
    Pointer(Data^.Cmd) := nil;
    Inc(Count_Napcmdlist_Items);
    if Count_Napcmdlist_Items > Count_Napcmdlist_Items_Max then
      Count_Napcmdlist_Items_Max := Count_Napcmdlist_Items;
{$IFNDEF DISABLE_MEMORY_MANAGER}
  end;
{$ENDIF}
  Result := Data;
end;

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

procedure DeleteItem(Item: PNapCmd);
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 ExpireCmdLists;
var
  List: TNapCmdList;
  Item: PNapCmd;
begin
  if Lists.Count > 1000 then
    while (Lists.Count * 3) > Count_Napcmdlist 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_Napcmdlist_Items do
    try
      Item := Items.Items[Items.Count - 1];
      Items.Delete(Items.Count - 1);
      FreeItem(Item);
    except
    end;
end;
{$ENDIF}

{* * * * *  TNapCmdList  * * * * *}

function TNapCmdList.Add(Value: TNapCmd): Integer;
var
  Data: PNapCmd;
begin
  Data := CreateItem;
  with Data^ do
  begin
    Cmd := Value.Cmd;
    Id := Value.Id;
  end;
  Result := inherited Add(Data);
end;

procedure TNapCmdList.Insert(Index: Integer; Value: TNapCmd);
var
  Data: PNapCmd;
begin
  Data := CreateItem;
  with Data^ do
  begin
    Cmd := Value.Cmd;
    Id := Value.Id;
  end;
  inherited Insert(Index, Data);
end;

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

procedure TNapCmdList.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 TNapCmdList.AddCmd(Id: Integer; Cmd: string): Integer;
var
  Data: TNapCmd;
begin
  Data.Id := Id;
  Data.Cmd := Cmd;
  Result := Add(Data);
end;

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

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

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

function TNapCmdList.FindByCmd(Cmd: string; Ignore_case: Boolean): Integer;
var
  I, Len: Integer;
begin
  Len := Length(Cmd);
  if Ignore_case then
  begin
    Cmd := AnsiLowerCase(Cmd);
    for I := 0 to Count - 1 do
      if Length(PNapCmd(Items[I])^.Cmd) = Len then
        if AnsiLowerCase(PNapCmd(Items[I])^.Cmd) = Cmd then
        begin
          Result := I;
          Exit;
        end;
    Result := -1;
    Exit;
  end;
  for I := 0 to Count - 1 do
    if Length(PNapCmd(Items[I])^.Cmd) = Len then
      if PNapCmd(Items[I]).Cmd = Cmd then
      begin
        Result := I;
        Exit;
      end;
  Result := -1;
end;

function TNapCmdList.FindById(Id: Integer): Integer;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    if PNapCmd(Items[I]).Id = Id then
    begin
      Result := I;
      Exit;
    end;
  Result := -1;
end;

function TNapCmdList.FindItem(Id: Integer; Cmd: string): Integer;
var
  I, Len: Integer;
begin
  Len := Length(Cmd);
  for I := 0 to Count - 1 do
    if PNapCmd(Items[I])^.Id = Id then
      if Length(PNapCmd(Items[I])^.Cmd) = Len then
        if PNapCmd(Items[I]).Cmd = Cmd then
        begin
          Result := I;
          Exit;
        end;
  Result := -1;
end;

constructor TNapCmdList.Create;
begin
  Inc(Count_Napcmdlist);
  if Count_Napcmdlist > Count_Napcmdlist_Max then
    Count_Napcmdlist_Max := Count_Napcmdlist;
  inherited Create;
end;

destructor TNapCmdList.Destroy;
begin
  Clear;
  Dec(Count_Napcmdlist);
  inherited Destroy;
end;

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

procedure TNapCmdList.SaveToFile(FileName: string);
var
  List: TMyStringList;
  I: Integer;
begin
  List := CreateStringList;
  for I := 0 to Count - 1 do
  begin
    List.Add(IntToStr(Id(I)) + ' ' + Str(I));
    if (I mod 50) = 0 then
    begin
{$I CheckSync.pas}
    end;
  end;
  try
    List.SaveToFile(FileName);
  except
  end;
  FreeStringList(List);
end;

procedure TNapCmdList.LoadFromFile(FileName: string);
var
  List: TMyStringList;
  I, J: Integer;
  Str: string;
begin
  List := CreateStringList;
  Clear;
  try
    List.LoadFromFile(FileName);
  except
  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]);
      AddCmd(J, Str);
    end;
  end;
  FreeStringList(List);
end;

procedure TNapCmdList.Sort;
var
  C1, C2: PNapCmd;
  I, J: Integer;
begin
  for I := 1 to Count - 1 do
    for J := 0 to I - 1 do
    begin
      C1 := Items[I];
      C2 := Items[J];
      if C1^.Id > C2^.Id then
      begin
        Items[I] := C2;
        Items[J] := C1;
      end
      else if C1^.Id = C2^.Id then
        if C1^.Cmd > C2^.Cmd then
        begin
          Items[I] := C2;
          Items[J] := C1;
        end;
    end;
end;

{$IFNDEF DISABLE_MEMORY_MANAGER}

procedure ClearCmdLists;
var
  I: Integer;
  P: PNapCmd;
begin
  for I := 0 to Lists.Count - 1 do
    TNapCmdList(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}

initialization
  begin
    Count_Napcmdlist := 0;
    Count_Napcmdlist_Max := 0;
    Count_Napcmdlist_Items := 0;
    Count_Napcmdlist_Items_Max := 0;
{$IFNDEF DISABLE_MEMORY_MANAGER}
    Lists := TMyList.Create;
    Items := TMyList.Create;
{$ENDIF}
  end;

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