{
 ***************************************************************************
 *                                                                         *
 *   This source is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This code is distributed in the hope that it will be useful, but      *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   General Public License for more details.                              *
 *                                                                         *
 *   A copy of the GNU General Public License is available on the World    *
 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 *   obtain it by writing to the Free Software Foundation,                 *
 *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
 *                                                                         *
 ***************************************************************************

  Author: Mattias Gaertner

  Abstract:
    - tree storing identifiers of other languages (e.g. javascript)
    - identifier completion for other languages
}
unit OtherIdentifierTree;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileProcs, CodeTree;

type

  { TOtherIdentifierTreeNode }

  TOtherIdentifierTreeNode = class
  public
    Desc: TCodeTreeNodeDesc;
    SubDesc: TCodeTreeNodeSubDesc;
    Parent: TOtherIdentifierTreeNode;
    NextSibling, PrevSibling: TOtherIdentifierTreeNode;
    FirstChild, LastChild: TOtherIdentifierTreeNode;
    constructor Create; virtual;
    destructor Destroy; override;
    procedure ClearChilds;
    procedure AddLastChild(Child: TOtherIdentifierTreeNode); virtual;
    procedure RemoveChild(Child: TOtherIdentifierTreeNode); virtual;
    function Next: TOtherIdentifierTreeNode;
    function NextSkipChilds: TOtherIdentifierTreeNode;
    function Prev: TOtherIdentifierTreeNode;
    function PrevSkipChilds: TOtherIdentifierTreeNode;
    function HasAsParent(Node: TOtherIdentifierTreeNode): boolean;
    function HasAsChild(Node: TOtherIdentifierTreeNode): boolean;
    function HasParentOfType(ParentDesc: TCodeTreeNodeDesc): boolean;
    function HasAsRoot(RootNode: TOtherIdentifierTreeNode): boolean;
    function GetRootNode: TOtherIdentifierTreeNode;
    function GetParentOfType(ADesc: TCodeTreeNodeDesc): TOtherIdentifierTreeNode;
    function GetParentOfTypes(Descriptors: array of TCodeTreeNodeDesc): TOtherIdentifierTreeNode;
    function ChildCount: integer; virtual;
    function AsString: string; virtual;
    procedure ConsistencyCheck; virtual;
    procedure WriteDebugReport(const Prefix: string; WithChilds: boolean); virtual;
  end;

  TOtherIdentifierTree = class
  public
    Root: TOtherIdentifierTreeNode;
    constructor Create;
    destructor Destroy; override;
    procedure ClearNodes;
  end;

implementation

{ TOtherIdentifierTreeNode }

procedure TOtherIdentifierTreeNode.AddLastChild(Child: TOtherIdentifierTreeNode);
begin
  Child.Parent:=Self;
  if LastChild<>nil then
    LastChild.NextSibling:=Child;
  Child.PrevSibling:=LastChild;
  LastChild:=Child;
  if FirstChild=nil then FirstChild:=Child;
end;

procedure TOtherIdentifierTreeNode.RemoveChild(Child: TOtherIdentifierTreeNode);
begin
  if FirstChild=Child then FirstChild:=Child.NextSibling;
  if LastChild=Child then LastChild:=Child.PrevSibling;
  Child.Parent:=nil;
end;

constructor TOtherIdentifierTreeNode.Create;
begin

end;

destructor TOtherIdentifierTreeNode.Destroy;
begin
  ClearChilds;
  if Parent<>nil then Parent.RemoveChild(Self);
  if NextSibling<>nil then NextSibling.PrevSibling:=PrevSibling;
  if PrevSibling<>nil then PrevSibling.NextSibling:=NextSibling;
  PrevSibling:=nil;
  NextSibling:=nil;
  inherited Destroy;
end;

procedure TOtherIdentifierTreeNode.ClearChilds;
begin
  while LastChild<>nil do LastChild.Free;
end;

function TOtherIdentifierTreeNode.Next: TOtherIdentifierTreeNode;
begin
  Result:=FirstChild;
  if Result<>nil then exit;
  Result:=NextSkipChilds;
end;

function TOtherIdentifierTreeNode.NextSkipChilds: TOtherIdentifierTreeNode;
begin
  Result:=Self;
  while (Result<>nil) do begin
    if Result.NextSibling<>nil then
      exit(Result.NextSibling);
    Result:=Result.Parent;
  end;
end;

function TOtherIdentifierTreeNode.Prev: TOtherIdentifierTreeNode;
begin
  if PrevSibling=nil then
    Result:=Parent
  else begin
    Result:=PrevSibling;
    while Result.LastChild<>nil do Result:=Result.LastChild;
  end;
end;

function TOtherIdentifierTreeNode.PrevSkipChilds: TOtherIdentifierTreeNode;
begin
  if PrevSibling<>nil then
    Result:=PrevSibling
  else
    Result:=Parent;
end;

function TOtherIdentifierTreeNode.HasAsParent(Node: TOtherIdentifierTreeNode
  ): boolean;
var
  CurNode: TOtherIdentifierTreeNode;
begin
  CurNode:=Parent;
  while CurNode<>nil do begin
    if CurNode=Node then exit(true);
    CurNode:=CurNode.Parent;
  end;
  Result:=false;
end;

function TOtherIdentifierTreeNode.HasAsChild(Node: TOtherIdentifierTreeNode
  ): boolean;
begin
  Result:=(Node<>nil) and Node.HasAsParent(Self);
end;

function TOtherIdentifierTreeNode.HasParentOfType(ParentDesc: TCodeTreeNodeDesc
  ): boolean;
begin
  Result:=GetParentOfType(ParentDesc)<>nil;
end;

function TOtherIdentifierTreeNode.HasAsRoot(RootNode: TOtherIdentifierTreeNode
  ): boolean;
begin
  Result:=GetRootNode=RootNode;
end;

function TOtherIdentifierTreeNode.GetRootNode: TOtherIdentifierTreeNode;
begin
  Result:=Self;
  while Result.Parent<>nil do Result:=Result.Parent;
end;

function TOtherIdentifierTreeNode.GetParentOfType(ADesc: TCodeTreeNodeDesc
  ): TOtherIdentifierTreeNode;
begin
  Result:=Parent;
  while (Result<>nil) do begin
    if Result.Desc=ADesc then exit;
    Result:=Result.Parent;
  end;
end;

function TOtherIdentifierTreeNode.GetParentOfTypes(
  Descriptors: array of TCodeTreeNodeDesc): TOtherIdentifierTreeNode;
var
  i: Integer;
begin
  Result:=Parent;
  while (Result<>nil) do begin
    for i:=low(Descriptors) to high(Descriptors) do
      if Result.Desc=Descriptors[i] then exit;
    Result:=Result.Parent;
  end;
end;

function TOtherIdentifierTreeNode.ChildCount: integer;
var
  Node: TOtherIdentifierTreeNode;
begin
  Result:=0;
  Node:=FirstChild;
  while Node<>nil do begin
    inc(Result);
    Node:=Node.NextSibling;
  end;
end;

function TOtherIdentifierTreeNode.AsString: string;
begin
  Result:=IntToStr(Desc);
end;

procedure TOtherIdentifierTreeNode.ConsistencyCheck;
var
  Node: TOtherIdentifierTreeNode;
begin
  if (NextSibling=Self) then
    raise Exception.Create('');
  if (PrevSibling=Self) then
    raise Exception.Create('');
  if (NextSibling<>nil) and (NextSibling.PrevSibling<>Self) then
    raise Exception.Create('');
  if (PrevSibling<>nil) and (PrevSibling.NextSibling<>Self) then
    raise Exception.Create('');
  if Parent=Self then
    raise Exception.Create('');

  if (FirstChild=nil)<>(LastChild=nil) then
    raise Exception.Create('');
  Node:=FirstChild;
  while Node<>nil do begin
    if Node.Parent<>Self then
      raise Exception.Create('');
    if (Node=FirstChild) and (Node.PrevSibling<>nil) then
      raise Exception.Create('');
    if (Node=LastChild) and (Node.NextSibling<>nil) then
      raise Exception.Create('');
    Node.ConsistencyCheck;
    Node:=Node.NextSibling;
  end;
end;

procedure TOtherIdentifierTreeNode.WriteDebugReport(const Prefix: string;
  WithChilds: boolean);
begin
  writeln(Prefix,AsString);
  if WithChilds then
    WriteDebugReport(Prefix+'  ',true);
end;

{ TOtherIdentifierTree }

constructor TOtherIdentifierTree.Create;
begin

end;

destructor TOtherIdentifierTree.Destroy;
begin
  ClearNodes;
  inherited Destroy;
end;

procedure TOtherIdentifierTree.ClearNodes;
begin
  FreeAndNil(Root);
end;

end.

