
{**
@abstract(Liste d'arbres.)
Classe pour créer et lire l'arbre d'un livre d'ouvertures, ou plus exactement la liste d'arbres.
}

unit TreeList;

interface

uses
  SysUtils, Classes, Tree;

type
  TTreeList = class
    private
      FList: TList;
    public
      constructor Create;
      destructor Destroy; override;
      function FindTree(const AData: string): integer;
      procedure AddLine(const ALine: TStrings);
      procedure AddLine(const ALine: string);
      function GetTreeCount: integer;
      function LoadFromFile(const AFilename: string): boolean;
      function LoadFromFileCompact(const AFilename: string): boolean;
      function GetTextCompact: string;
      function FindChild(const ALine: TStrings; const ARandom: boolean): string;
      function FindChild(const ALine: string; const ARandom: boolean = TRUE): string;
      procedure SaveToFile(const AFileName: TFileName);
      procedure SaveToFileCompact(const AFileName: TFileName); 
  end;
  
implementation

uses
  Extractor;
  
const
  CTreeNotFound = -1;
  
constructor TTreeList.Create;
begin
  inherited Create;
  FList := TList.Create;
end;

destructor TTreeList.Destroy;
var
  LIndex: integer;
begin
  for LIndex := 0 to Pred(FList.Count) do
    TTree(FList[LIndex]).Free;
  FList.Free;
  inherited Destroy;
end;

function TTreeList.FindTree(const AData: string): integer;
var
  LIndex: integer;
begin
  result := CTreeNotFound;
  LIndex := 0;
  while (result = CTreeNotFound) and (LIndex < FList.Count) do
    if TTree(FList.Items[LIndex]).FindNode(nil, AData) <> nil then
      result := LIndex
    else
      Inc(LIndex);
end;

procedure TTreeList.AddLine(const ALine: TStrings);
var
  LTree: TTree;
  LTreeIndex: integer;
begin
  if ALine.Count = 0 then
    Exit;
  LTreeIndex := FindTree(ALine.Strings[0]);
  if LTreeIndex = CTreeNotFound then
  begin
    LTree := TTree.Create;
    FList.Add(LTree);
    LTreeIndex := Pred(FList.Count);
  end;
  TTree(FList.Items[LTreeIndex]).AddLine(ALine);
end;

procedure TTreeList.AddLine(const ALine: string);
var
  LLine: TStringList;
begin
  LLine := TStringList.Create;
  LLine.DelimitedText := ALine;
  AddLine(LLine);
  LLine.Free;
end;

function TTreeList.GetTreeCount: integer;
begin
  result := FList.Count;
end;

function TTreeList.LoadFromFile(const AFilename: string): boolean;
var
  LFile, LLine: TStringList;
  i: integer;
begin
  result := TRUE;
  LFile := TStringList.Create;
  LLine := TStringList.Create;
  try
    LFile.LoadFromFile(AFilename);
    for i := 0 to Pred(LFile.Count) do
    begin
      LLine.DelimitedText := LFile[i];
      AddLine(LLine);
    end;
  except
    result := FALSE;
  end;
  LFile.Free;
  LLine.Free;
end;

function TTreeList.LoadFromFileCompact(const AFilename: string): boolean;
var
  LFile: TStringList;
  LLines: TStringListArray;
  i: integer;
begin
  result := TRUE;
  LFile := TStringList.Create;
  try
    LFile.LoadFromFile(AFilename);
    if LFile.Count >= 1 then
    begin
      
      LLines := Extractor.ExtractLines(LFile[0]);
      
      for i := 0 to High(LLines) do
      begin
        AddLine(LLines[i]);
        LLines[i].Free;
      end;
      
      SetLength(LLines, 0);
      
    end;
  except
    result := FALSE;
  end;
  LFile.Free;
end;

function TTreeList.GetTextCompact: string;
var
  LTreeIndex: integer;
begin
  result := '';
  for LTreeIndex := 0 to Pred(FList.Count) do
    result := Concat(
      result,
      TTree(FList[LTreeIndex]).GetTextCompact
    );
end;

function TTreeList.FindChild(const ALine: TStrings; const ARandom: boolean): string;
var
  LTreeIndex: integer;
begin
  if FList.Count = 0 then
    Exit('');
  if ALine.Count = 0 then
    if ARandom then
      Exit(TTree(FList[Random(FList.Count)]).GetRoot^.Data)
    else
      Exit(TTree(FList[0]).GetRoot^.Data);
  LTreeIndex := FindTree(ALine.Strings[0]);
  if LTreeIndex = CTreeNotFound then
    result := ''
  else
    result := TTree(FList[LTreeIndex]).FindChild(ALine, ARandom);
end;

function TTreeList.FindChild(const ALine: string; const ARandom: boolean): string;
var
  LLine: TStringList;
begin
  LLine := TStringList.Create;
  LLine.DelimitedText := ALine;
  result := FindChild(LLine, ARandom);
  LLine.Free;
end;

procedure TTreeList.SaveToFile(const AFileName: TFileName); 
var
  LFile: TStringList;
  LIndex: integer;
begin
  LFile := TStringList.Create;
  for LIndex := 0 to Pred(FList.Count) do
    TTree(FList[LIndex]).SaveToFile(LFile);
  LFile.SaveToFile(AFileName);
  LFile.Free;
end;

procedure TTreeList.SaveToFileCompact(const AFileName: TFileName); 
var
  LLine: string;
  LFile: TStringList;
begin
  LFile := TStringList.Create;
  LLine := GetTextCompact;
  LFile.Append(LLine);
  LFile.SaveToFile(AFileName);
  LFile.Free;
end;

end.
