unit ASTree;

{ **************************************************************************** }
{ Rejbrand AlgoSim abstract tree data structure implementation                 }
{ Copyright © 2017-2018 Andreas Rejbrand                                       }
{ https://english.rejbrand.se/                                                 }
{ **************************************************************************** }

{$WARN SYMBOL_PLATFORM OFF}
{$WARN DUPLICATE_CTOR_DTOR OFF}

interface

uses
  SysUtils, Types, Classes, Generics.Defaults, Generics.Collections,
  ASKernelDefs;

type
  TTreeNode = class;
  TTreeNodeClass = class of TTreeNode;

  TTree = class
  strict private
    FRoot: TTreeNode;
  protected
    procedure DoCreateRoot(ARootNodeClass: TTreeNodeClass); virtual;
  public
    constructor Create(ARootNodeClass: TTreeNodeClass);
    destructor Destroy; override;
    property Root: TTreeNode read FRoot;
  end;

  TTreeNode = class
  strict private
    FTree: TTree;
    FParent: TTreeNode;
    FChildren: TObjectList<TTreeNode>;
    function GetChild(Index: Integer): TTreeNode; inline;
    function GetChildCount: Integer; inline;
    function GetCapacity: Integer; inline;
    procedure SetCapacity(const Value: Integer); inline;
  protected
    procedure InitNode; virtual;
    procedure ValidateParent(ATree: TTree; AParent: TTreeNode); virtual;
    function DoAddChild(ANodeClass: TTreeNodeClass): TTreeNode; virtual;
    constructor Create(ATree: TTree; AParent: TTreeNode); overload; virtual;
  public
    constructor Create; overload;
    destructor Destroy; override;
    property Parent: TTreeNode read FParent;
    property Tree: TTree read FTree;
    property Capacity: Integer read GetCapacity write SetCapacity;
    property Children[Index: Integer]: TTreeNode read GetChild;
    property ChildCount: Integer read GetChildCount;
    function AddChild(ANodeClass: TTreeNodeClass): TTreeNode; inline;
    procedure RemoveChild(Index: Integer); inline;
    procedure ClearChildren; inline;
    procedure Clear; virtual;
    function IsLeaf: Boolean; inline;
  end;

implementation


{ TTree }

constructor TTree.Create(ARootNodeClass: TTreeNodeClass);
begin
  DoCreateRoot(ARootNodeClass);
end;

destructor TTree.Destroy;
begin
  FreeAndNil(FRoot);
  inherited;
end;

procedure TTree.DoCreateRoot(ARootNodeClass: TTreeNodeClass);
begin
  FRoot := ARootNodeClass.Create(Self, nil);
end;

{ TTreeNode }

function TTreeNode.AddChild(ANodeClass: TTreeNodeClass): TTreeNode;
begin
  Result := DoAddChild(ANodeClass);
end;

procedure TTreeNode.Clear;
begin
  FChildren.Clear;
end;

procedure TTreeNode.ClearChildren;
begin
  FChildren.Clear;
end;

constructor TTreeNode.Create;
begin
  raise Exception.Create(SNoStandaloneNode);
end;

constructor TTreeNode.Create(ATree: TTree; AParent: TTreeNode);
begin
  ValidateParent(ATree, AParent);
  FTree := ATree;
  FParent := AParent;
  FChildren := TObjectList<TTreeNode>.Create(true);
  InitNode;
end;

destructor TTreeNode.Destroy;
begin
  FreeAndNil(FChildren);
  inherited;
end;

function TTreeNode.GetCapacity: Integer;
begin
  Result := FChildren.Capacity;
end;

function TTreeNode.GetChild(Index: Integer): TTreeNode;
begin
  Result := FChildren[Index];
end;

function TTreeNode.GetChildCount: Integer;
begin
  Result := FChildren.Count;
end;

function TTreeNode.DoAddChild(ANodeClass: TTreeNodeClass): TTreeNode;
begin
  Result := FChildren[FChildren.Add(ANodeClass.Create(FTree, Self))];
end;

procedure TTreeNode.InitNode;
begin

end;

function TTreeNode.IsLeaf: Boolean;
begin
  Result := FChildren.Count = 0;
end;

procedure TTreeNode.RemoveChild(Index: Integer);
begin
  FChildren.Delete(Index);
end;

procedure TTreeNode.SetCapacity(const Value: Integer);
begin
  FChildren.Capacity := Value;
end;

procedure TTreeNode.ValidateParent(ATree: TTree; AParent: TTreeNode);
begin

end;

end.
