{
    This file is part of the Web Service Toolkit
    Copyright (c) 2007 by Inoussa OUEDRAOGO

    This file is provide under modified LGPL licence
    ( the files COPYING.modifiedLGPL and COPYING.LGPL).


    This program 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.
}
unit uwsttypelibraryedit;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls,
  ExtCtrls, Menus, ActnList, Contnrs,
  pastree, pascal_parser_intf, logger_intf,
  SynHighlighterPas, SynEdit, StdCtrls, SynHighlighterXML
  {$IFDEF WST_IDE},ProjectIntf{$ENDIF};

type

  TSearchArea = ( saEditor, saTree );
  
  { TfWstTypeLibraryEdit }

  TfWstTypeLibraryEdit = class(TForm)
    actExit: TAction;
    actExport: TAction;
    actAbout: TAction;
    actEnumCreate: TAction;
    actCompoundCreate: TAction;
    actIntfCreate: TAction;
    actFullExpand: TAction;
    actFullCollapse: TAction;
    actDelete : TAction;
    actArrayCreate : TAction;
    actEditSearch : TAction;
    actClone : TAction;
    actSaveXSD : TAction;
    actTreeSearch : TAction;
    actRecordCreate : TAction;
    actTypeALiasCreate : TAction;
    actSave : TAction;
    actNewFile: TAction;
    actRefreshView: TAction;
    actUpdateObject: TAction;
    actSaveAs: TAction;
    actOpenFile: TAction;
    AL: TActionList;
    FD : TFindDialog;
    MainMenu1: TMainMenu;
    edtDocumentation : TMemo;
    MenuItem10: TMenuItem;
    MenuItem11: TMenuItem;
    MenuItem12: TMenuItem;
    MenuItem13: TMenuItem;
    MenuItem14: TMenuItem;
    MenuItem15: TMenuItem;
    MenuItem16: TMenuItem;
    MenuItem17: TMenuItem;
    MenuItem18: TMenuItem;
    MenuItem19: TMenuItem;
    MenuItem20: TMenuItem;
    MenuItem21: TMenuItem;
    MenuItem22: TMenuItem;
    MenuItem23: TMenuItem;
    MenuItem24: TMenuItem;
    MenuItem25: TMenuItem;
    MenuItem26: TMenuItem;
    MenuItem27: TMenuItem;
    MenuItem28: TMenuItem;
    MenuItem29: TMenuItem;
    MenuItem30: TMenuItem;
    MenuItem31: TMenuItem;
    MenuItem32: TMenuItem;
    MenuItem33 : TMenuItem;
    MenuItem34 : TMenuItem;
    MenuItem35 : TMenuItem;
    MenuItem36 : TMenuItem;
    MenuItem37 : TMenuItem;
    MenuItem38 : TMenuItem;
    MenuItem39 : TMenuItem;
    MenuItem40 : TMenuItem;
    MenuItem41 : TMenuItem;
    MenuItem42 : TMenuItem;
    MenuItem43 : TMenuItem;
    MenuItem44 : TMenuItem;
    MenuItem45 : TMenuItem;
    MenuItem46 : TMenuItem;
    MenuItem47 : TMenuItem;
    MenuItem48 : TMenuItem;
    MenuItem49 : TMenuItem;
    MenuItem5: TMenuItem;
    MenuItem50 : TMenuItem;
    MenuItem51 : TMenuItem;
    MenuItem52 : TMenuItem;
    MenuItem53 : TMenuItem;
    MenuItem54 : TMenuItem;
    MenuItem55 : TMenuItem;
    MenuItem6: TMenuItem;
    MenuItem7 : TMenuItem;
    MenuItem8: TMenuItem;
    MenuItem9: TMenuItem;
    mmoLog: TMemo;
    MenuItem1: TMenuItem;
    MenuItem2: TMenuItem;
    MenuItem3: TMenuItem;
    MenuItem4: TMenuItem;
    OD: TOpenDialog;
    PCInfos : TPageControl;
    PC: TPageControl;
    Panel1: TPanel;
    Panel2: TPanel;
    PopupMenu1: TPopupMenu;
    PopupMenu2: TPopupMenu;
    SD: TSaveDialog;
    Splitter1: TSplitter;
    Splitter2 : TSplitter;
    srcInterface: TSynEdit;
    SB: TStatusBar;
    srcImp: TSynEdit;
    srcBinder: TSynEdit;
    srcProxy: TSynEdit;
    srcWSDL: TSynEdit;
    edtXSD : TSynEdit;
    SynPasSyn1: TSynPasSyn;
    SynXMLSyn1: TSynXMLSyn;
    tvDependency : TTreeView;
    tsDependencies : TTabSheet;
    tsXSD : TTabSheet;
    tsDocumentation : TTabSheet;
    tsWSDL: TTabSheet;
    tsLog: TTabSheet;
    tsImp: TTabSheet;
    tsBinder: TTabSheet;
    tsInterface: TTabSheet;
    tsProxy: TTabSheet;
    trvSchema: TTreeView;
    procedure actAboutExecute(Sender: TObject);
    procedure actArrayCreateExecute(Sender : TObject);
    procedure actCloneExecute(Sender : TObject);
    procedure actCloneUpdate(Sender : TObject);
    procedure actCompoundCreateExecute(Sender: TObject);
    procedure actDeleteExecute (Sender : TObject );
    procedure actDeleteUpdate(Sender : TObject);
    procedure actEnumCreateExecute(Sender: TObject);
    procedure actEnumCreateUpdate(Sender : TObject);
    procedure actExitExecute(Sender: TObject);
    procedure actExportExecute(Sender: TObject);
    procedure actExportUpdate(Sender: TObject);
    procedure actFullCollapseExecute(Sender: TObject);
    procedure actFullExpandExecute(Sender: TObject);
    procedure actIntfCreateExecute(Sender: TObject);
    procedure actNewFileExecute(Sender: TObject);
    procedure actOpenFileExecute(Sender: TObject);
    procedure actRecordCreateExecute(Sender : TObject);
    procedure actRefreshViewExecute(Sender: TObject);
    procedure actSaveAsExecute(Sender: TObject);
    procedure actSaveExecute (Sender : TObject );
    procedure actEditSearchExecute(Sender : TObject);
    procedure actEditSearchUpdate(Sender : TObject);
    procedure actSaveXSDExecute(Sender : TObject);
    procedure actTreeSearchExecute(Sender : TObject);
    procedure actTreeSearchUpdate(Sender : TObject);
    procedure actTypeALiasCreateExecute(Sender : TObject);
    procedure actUpdateObjectExecute(Sender: TObject);
    procedure actUpdateObjectUpdate(Sender: TObject);
    procedure FDFind(Sender : TObject);
    procedure FDShow(Sender : TObject);
    procedure FormClose (Sender : TObject; var CloseAction : TCloseAction );
    procedure FormShow(Sender: TObject);
    procedure trvSchemaSelectionChanged(Sender : TObject);
  private
    FSymbolTable : TwstPasTreeContainer;
    FStatusMessageTag : PtrInt;
    FCurrentFileName : string;
{$IFDEF WST_IDE}
    FProjectLibrary : TLazProjectFile;
{$ENDIF}
    FSearchArea : TSearchArea;
    FFoundNode : TTreeNode;
    FDependencyList : TObjectList;
  private
    function FindCurrentEditor() : TSynEdit;
    function Search(const AText : string) : Boolean;
    function SearchInTree(const AText : string) : Boolean;
    function SearchInEditor(const AText : string) : Boolean;
  private
    function GetTypeNode() : TTreeNode;
    function GetInterfaceNode() : TTreeNode;
  private
    procedure ShowStatusMessage(const AMsgType : TMessageType;const AMsg : string);
    procedure RenderSymbols();
    procedure RenderSources();
    procedure RenderWSDL();
    procedure ShowDocumentation();
    procedure ShowSourceXSD();
    procedure ShowDependencies();
    procedure OpenFile(const AFileName : string; const AContent : TStream = nil);
    procedure SaveToFile(const AFileName : string);
  public
    constructor Create(AOwner : TComponent);override;
    destructor Destroy();override;
  end; 

var
  fWstTypeLibraryEdit: TfWstTypeLibraryEdit;

implementation
uses
  view_helper, DOM, XMLRead, XMLWrite, //HeapTrc,
  xsd_parser, wsdl_parser, source_utils, command_line_parser, generator, metadata_generator,
  binary_streamer, wst_resources_utils, xsd_generator, wsdl_generator,
  uabout, edit_helper, udm, ufrmsaveoption, pparser, SynEditTypes
  {$IFDEF WST_IDE},LazIDEIntf,IDEMsgIntf{$ENDIF}
  , xsd_consts, parserutils;

{$IFDEF WST_IDE}
function GetCurrentProjectLibraryFile():TLazProjectFile;
var
  i, c : Integer;
begin
  Result := nil;
  c := LazarusIDE.ActiveProject.FileCount;
  for i := 0 to Pred(c) do begin
    if AnsiSameText('.wsdl',ExtractFileExt(LazarusIDE.ActiveProject.Files[i].Filename)) then begin
      Result := LazarusIDE.ActiveProject.Files[i];
      Break;
    end;
  end;
end;
{$ENDIF}

const
  DEF_FILE_NAME = 'library1';

type
  TSourceOptions = set of TComandLineOption;

function ParsePascalFile(
  const AFileName : string;
  const ANotifier : TOnParserMessage
) : TwstPasTreeContainer;overload;
const
  s_ostype =
    {$IFDEF WINDOWS}
      'WINDOWS'
    {$ELSE}
      {$IFDEF LINUX}
        'LINUX'
      {$ELSE}
        ''
      {$ENDIF}
    {$ENDIF};
    
  procedure DoNotify(const AMsgType : TMessageType; const AMsg : string);
  begin
    if ( ANotifier <> nil ) then
      ANotifier(AMsgType,AMsg);
  end;
  
var
  symName : string;
begin
  symName := ChangeFileExt(ExtractFileName(AFileName),'');
  if ( symName[Length(symName)] = '.' ) then begin
    Delete(symName,Length(symName),1);
  end;
  Result := TwstPasTreeContainer.Create();
  try
    DoNotify(mtInfo,Format('Parsing file %s ...',[AFileName]));
    CreateWstInterfaceSymbolTable(Result);
    ParseSource(Result,AFileName,s_ostype,'');
    CreateDefaultBindingForIntf(Result);
    DoNotify(mtInfo,Format('File parsed %s .',[AFileName]));
  except
    on e : Exception do begin
      DoNotify(mtError,e.Message);
      FreeAndNil(Result);
      //raise;
    end;
  end;
end;

function ParseWsdlFile(
  const AFileName : string;
        AContent  : TStream;
  const ANotifier : TOnParserMessage
):TwstPasTreeContainer;overload;
var
  locDoc : TXMLDocument;
  prsr : IParser;
  symName : string;
begin
  Result := nil;
  symName := ChangeFileExt(ExtractFileName(AFileName),'');
  if ( symName[Length(symName)] = '.' ) then begin
    Delete(symName,Length(symName),1);
  end;
  prsr := nil;
  ReadXMLFile(locDoc,AContent);
  try
    Result := TwstPasTreeContainer.Create();
    try
      prsr := TWsdlParser.Create(locDoc,Result,ANotifier);
      prsr.Execute(pmAllTypes,symName);
    except
      FreeAndNil(Result);
      raise;
    end;
  finally
    FreeAndNil(locDoc);
  end;
end;

function ParseWsdlFile(
  const AFileName : string;
  const ANotifier : TOnParserMessage
):TwstPasTreeContainer;overload;
var
  locContent : TMemoryStream;
begin
  Result := nil;
  if FileExists(AFileName) then begin
    locContent := TMemoryStream.Create();
    try
      locContent.LoadFromFile(AFileName);
      locContent.Position := 0;
      Result := ParseWsdlFile(AFileName,locContent,ANotifier);
    finally
      FreeAndNil(locContent);
    end;
  end;
end;

function ParseXsdFile(
  const AFileName : string;
        AContent  : TStream;
  const ANotifier : TOnParserMessage
):TwstPasTreeContainer;overload;
var
  locDoc : TXMLDocument;
  prsr : IXsdPaser;
  symName : string;
begin
  Result := nil;
  symName := ChangeFileExt(ExtractFileName(AFileName),'');
  if ( symName[Length(symName)] = '.' ) then begin
    Delete(symName,Length(symName),1);
  end;
  prsr := nil;
  ReadXMLFile(locDoc,AContent);
  try
    Result := TwstPasTreeContainer.Create();
    try
      prsr := TXsdParser.Create(locDoc,Result,'',ANotifier);
      prsr.ParseTypes();
    except
      FreeAndNil(Result);
      raise;
    end;
  finally
    FreeAndNil(locDoc);
  end;
end;

function ParseXsdFile(
  const AFileName : string;
  const ANotifier : TOnParserMessage
):TwstPasTreeContainer;overload;
var
  locContent : TMemoryStream;
begin
  Result := nil;
  if FileExists(AFileName) then begin
    locContent := TMemoryStream.Create();
    try
      locContent.LoadFromFile(AFileName);
      locContent.Position := 0;
      Result := ParseXsdFile(AFileName,locContent,ANotifier);
    finally
      FreeAndNil(locContent);
    end;
  end;
end;

type TOutputType = ( otMemory, otFileSystem );
function GenerateSource(
        ASymbolTable : TwstPasTreeContainer;
        AOptions     : TSourceOptions;
  const AOutputType  : TOutputType;
  const AOutPath     : string;
  const ANotifier    : TOnParserMessage
) : ISourceManager;

  procedure Notify(const AMsg : string);
  begin
    if Assigned(ANotifier) then begin
      ANotifier(mtInfo, AMsg);
    end;
  end;
  
var
  mtdaFS: TMemoryStream;
  g : TBaseGenerator;
  mg : TMetadataGenerator;
  rsrcStrm : TMemoryStream;
begin
  Result := CreateSourceManager();
  rsrcStrm := nil;
  mtdaFS := nil;
  mg := nil;
  g := Nil;
  try
  
    if ( cloInterface in AOptions ) then begin
      Notify('Interface file generation...');
      g := TInftGenerator.Create(ASymbolTable,Result);
      if ( cloGenerateDocAsComments in AOptions ) then
        g.Options := g.Options + [goGenerateDocAsComments];
      g.Execute();
      FreeAndNil(g);
    end;

    if ( cloProxy in AOptions ) then begin
      Notify('Proxy file generation...');
      g := TProxyGenerator.Create(ASymbolTable,Result);
      g.Execute();
      FreeAndNil(g);
    end;

    if ( cloBinder in AOptions ) then begin
      Notify('Binder file generation...');
      g := TBinderGenerator.Create(ASymbolTable,Result);
      g.Execute();
      FreeAndNil(g);
    end;

    if ( cloImp in AOptions ) then begin
      Notify('Implementation file generation...');
      g := TImplementationGenerator.Create(ASymbolTable,Result);
      g.Execute();
      FreeAndNil(g);
    end;

    if ( AOutputType = otFileSystem ) and ( [cloBinder,cloProxy]*AOptions  <> [] ) then begin
      Notify('Metadata file generation...');
      mtdaFS := TMemoryStream.Create();
      mg := TMetadataGenerator.Create(ASymbolTable,CreateBinaryWriter(mtdaFS));
      mg.Execute();
      //mtdaFS.SaveToFile(AOutPath + Format('%s.%s',[ASymbolTable.CurrentModule.Name,sWST_META]));
      rsrcStrm := TMemoryStream.Create();
      mtdaFS.Position := 0;
      BinToWstRessource(UpperCase(ASymbolTable.CurrentModule.Name),mtdaFS,rsrcStrm);
      rsrcStrm.SaveToFile(AOutPath + Format('%s.%s',[ASymbolTable.CurrentModule.Name,sWST_EXTENSION]));
    end;
    
    if ( AOutputType = otFileSystem ) then begin
      Result.SaveToFile(AOutPath);
    end;
  finally
    rsrcStrm.Free();
    mg.Free();;
    mtdaFS.Free();;
    g.Free();
  end;
end;

procedure GenerateWSDL_ToStream(ASymbol : TwstPasTreeContainer; ADest : TStream);
var
  g : IGenerator;
  doc : TXMLDocument;
begin
  doc := TXMLDocument.Create();
  try
    g := TWsdlGenerator.Create(doc);
    g.Execute(ASymbol,ASymbol.CurrentModule.Name);
    WriteXML(doc,ADest);
  finally
    FreeAndNil(doc);
  end;
end;

procedure GenerateXSD_ToStream(ASymbol : TwstPasTreeContainer; ADest : TStream);
var
  g : IGenerator;
  doc : TXMLDocument;
begin
  doc := TXMLDocument.Create();
  try
    g := TXsdGenerator.Create(doc);
    g.Execute(ASymbol,ASymbol.CurrentModule.Name);
    WriteXML(doc,ADest);
  finally
    FreeAndNil(doc);
  end;
end;

function CreateSymbolTable(const AName : string):TwstPasTreeContainer ;
var
  mdl : TPasModule;
begin
  Result := TwstPasTreeContainer.Create();
  try
    CreateWstInterfaceSymbolTable(Result);
    mdl := TPasModule(Result.CreateElement(TPasModule,AName,Result.Package,visDefault,'',0));
    mdl.InterfaceSection := TInterfaceSection(Result.CreateElement(TInterfaceSection,'',mdl,visDefault,'',0));
    Result.Package.Modules.Add(mdl);
  except
    FreeAndNil(Result);
    raise;
  end;
end;

{ TfWstTypeLibraryEdit }

procedure TfWstTypeLibraryEdit.actOpenFileExecute(Sender: TObject);
begin
{$IFNDEF WST_IDE}
  OD.InitialDir := DM.Options.ReadString(ClassName(),sLAST_PATH,OD.InitialDir);
{$ENDIF WST_IDE}
  if OD.Execute() then begin
    OpenFile(OD.FileName);
  end;
end;

procedure TfWstTypeLibraryEdit.actRecordCreateExecute(Sender : TObject);
var
  e : TPasRecordType;
begin
  e := CreateRecordObject(FSymbolTable);
  if Assigned(e) then begin
    FindPainter(e).Paint(FSymbolTable,e,GetTypeNode());
  end;
end;

procedure TfWstTypeLibraryEdit.actRefreshViewExecute(Sender: TObject);
begin
  RenderSymbols();
end;

procedure TfWstTypeLibraryEdit.actSaveAsExecute(Sender: TObject);
begin
{$IFNDEF WST_IDE}
  SD.InitialDir := DM.Options.ReadString(ClassName(),sLAST_PATH,SD.InitialDir);
{$ENDIF WST_IDE}
  if SD.Execute() then begin
    SaveToFile(SD.FileName);
    FCurrentFileName := SD.FileName;
  end;
end;

procedure TfWstTypeLibraryEdit.actSaveExecute (Sender : TObject );
begin
  if FileExists(FCurrentFileName) then
    SaveToFile(FCurrentFileName)
  else
    actSaveAs.Execute() ;
end;

procedure TfWstTypeLibraryEdit.actEditSearchExecute(Sender : TObject);
begin
  FSearchArea := saEditor;
  FD.Execute();
end;

procedure TfWstTypeLibraryEdit.actEditSearchUpdate(Sender : TObject);
begin
  TAction(Sender).Enabled := ( PC.ActivePageIndex in [0..4] );
end;

procedure TfWstTypeLibraryEdit.actSaveXSDExecute(Sender : TObject);
var
  oldFilter : string;
begin
  oldFilter := SD.Filter;
  SD.Filter := 'XSD files ( *.xsd )|*.xsd';
  try
    if SD.Execute() then begin
      SaveToFile(ChangeFileExt(SD.FileName,'.xsd'));
    end;
  finally
    SD.Filter := oldFilter;
  end;
end;

procedure TfWstTypeLibraryEdit.actTreeSearchExecute(Sender : TObject);
begin
  FSearchArea := saTree;
  FD.Execute();
end;

procedure TfWstTypeLibraryEdit.actTreeSearchUpdate(Sender : TObject);
begin
  TAction(Sender).Enabled := ( GetTypeNode().Count > 0 );
end;

procedure TfWstTypeLibraryEdit.actTypeALiasCreateExecute(Sender : TObject);
var
  e : TPasAliasType;
begin
  e := CreateAliasType(FSymbolTable);
  if Assigned(e) then begin
    FindPainter(e).Paint(FSymbolTable,e,GetTypeNode());
  end;
end;

procedure TfWstTypeLibraryEdit.actUpdateObjectExecute(Sender: TObject);
var
  o : TPasElement;
  nd, nd_1, locTypeNode : TTreeNode;
begin
  nd := trvSchema.Selected;
  if Assigned(nd) and Assigned(nd.Data) then begin
    o := TPasElement(nd.Data);
    if HasEditor(o,etUpdate) and UpdateObject(o,FSymbolTable) then begin
      nd_1  := nd;
      locTypeNode := GetTypeNode();
      trvSchema.BeginUpdate();
      try
        nd := FindPainter(o).Paint(FSymbolTable,o,locTypeNode);
        nd.MoveTo(nd_1,naInsertBehind);
        FreeAndNil(nd_1);
      finally
        trvSchema.EndUpdate();
      end;
    end;
  end;
end;

procedure TfWstTypeLibraryEdit.actUpdateObjectUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled :=
    Assigned(trvSchema.Selected) and
    Assigned(trvSchema.Selected.Data) and
    HasEditor(TPasElement(trvSchema.Selected.Data),etUpdate);
end;

procedure TfWstTypeLibraryEdit.FDFind(Sender : TObject);
begin
  if not Search(FD.FindText) then
    ShowMessageFmt('Unable to find "%s".',[FD.FindText]);
end;

procedure TfWstTypeLibraryEdit.FDShow(Sender : TObject);
var
  edt : TSynEdit;
begin
  FFoundNode := nil;
  edt := FindCurrentEditor();
  if Assigned(edt) and edt.SelAvail then
    FD.FindText := edt.SelText;
end;

procedure TfWstTypeLibraryEdit.FormClose (Sender : TObject; var CloseAction : TCloseAction );
var
  dlgRes : Integer;
  {$IFDEF WST_IDE}
  prjFile : TLazProjectFile;
  {$ENDIF}
begin
  dlgRes := MessageDlg(Self.Caption,'Save the file before exit ?',mtConfirmation,mbYesNoCancel,0);
  if ( dlgRes = mrCancel ) then begin
    CloseAction := caNone;
  end else begin
    if ( dlgRes = mrYes ) then begin
      actSave.Execute();
    end;
{$IFDEF WST_IDE}
    if ( FProjectLibrary = nil ) then begin
      prjFile := GetCurrentProjectLibraryFile();
      if ( prjFile = nil ) then begin
        dlgRes := MessageDlg(Self.Caption,'Add this type library to the current project ?',mtConfirmation,mbYesNo,0);
        if ( dlgRes = mrYes ) then begin
          LazarusIDE.DoOpenEditorFile(FCurrentFileName,-1,[ofAddToProject]);
        end;
      end;
    end;
{$ENDIF}
  end;
end;

procedure TfWstTypeLibraryEdit.FormShow(Sender: TObject);
{$IFDEF WST_IDE}
var
  prjFile : TLazProjectFile;
  locContent : TMemoryStream;
{$ENDIF}
begin
{$IFDEF WST_IDE}
  prjFile := GetCurrentProjectLibraryFile();
  if ( prjFile <> nil ) then begin
    locContent := TMemoryStream.Create();
    try
      locContent.LoadFromFile(prjFile.FileName);
      if ( locContent.Size > 0 ) then begin
        locContent.Position := 0;
        IDEMessagesWindow.AddMsg(Format('Parsing %s...',[prjFile.Filename]),ExtractFileDir(prjFile.Filename),0);
        OpenFile(prjFile.Filename,locContent);
        IDEMessagesWindow.AddMsg(Format('File Parsed %s.',[prjFile.Filename]),ExtractFileDir(prjFile.Filename),0);
        FProjectLibrary := prjFile;
      end;
    finally
      FreeAndNil(locContent);
    end;
  end;
{$ENDIF}
  RenderSymbols();
end;

procedure TfWstTypeLibraryEdit.trvSchemaSelectionChanged(Sender : TObject);
begin
  ShowDocumentation();
  ShowSourceXSD();
  ShowDependencies();
end;

function TfWstTypeLibraryEdit.FindCurrentEditor() : TSynEdit;
var
  edt : TSynEdit;
begin
  case PC.ActivePageIndex of
    0 : edt := srcInterface;
    1 : edt := srcWSDL;
    2 : edt := srcProxy;
    3 : edt := srcImp;
    4 : edt := srcBinder;
    else
      edt := nil;
  end;
  Result := edt;
end;

function TfWstTypeLibraryEdit.Search(const AText : string) : Boolean;
begin
  if ( FSearchArea = saEditor ) then
    Result := SearchInEditor(AText)
  else
    Result := SearchInTree(AText);
end;

function TfWstTypeLibraryEdit.SearchInTree(const AText : string) : Boolean;
var
  curLock : IInterface;
  searchDir : TSearchDirection;
begin
  curLock := SetCursorHourGlass();
  if ( FFoundNode <> nil ) then
    FFoundNode := FFoundNode.GetNext();
  if ( frDown in FD.Options ) then
    searchDir := sdForward
  else
    searchDir := sdbackward;
  FFoundNode := SearchItem(AText,GetTypeNode(),FFoundNode,searchDir);
  Result := ( FFoundNode <> nil );
  if Result then begin
    FFoundNode.Selected := True ;
    FFoundNode.Focused := True ;
  end;
  curLock := nil;
end;

function TfWstTypeLibraryEdit.SearchInEditor(const AText : string) : Boolean;
var
  edt : TSynEdit;
  opts : TSynSearchOptions;
begin
  Result := False;
  edt := FindCurrentEditor();
  if Assigned(edt) then begin
    opts := [];
    if not ( frDown in FD.Options ) then
      Include(opts,ssoBackwards);
    if ( frMatchCase in FD.Options ) then
      Include(opts,ssoMatchCase);
    if ( frWholeWord in FD.Options ) then
      Include(opts,ssoWholeWord);
    Result := edt.SearchReplace(AText,'',opts) <> 0;
  end;
end;

function TfWstTypeLibraryEdit.GetTypeNode(): TTreeNode;
begin
  Result := trvSchema.Items[0].GetFirstChild().Items[0];
end;

function TfWstTypeLibraryEdit.GetInterfaceNode(): TTreeNode;
begin
  Result := trvSchema.TopItem.GetFirstChild().Items[1];
end;

procedure TfWstTypeLibraryEdit.ShowStatusMessage(const AMsgType : TMessageType;const AMsg: string);
begin
  mmoLog.Lines.Add(Format('%s : %s',[MessageTypeNames[AMsgType],AMsg]));
  SB.Panels[1].Text := AMsg;
  Inc(FStatusMessageTag);
  if ( (FStatusMessageTag) > 23 ) then begin
    FStatusMessageTag := 0;
    Application.ProcessMessages();
  end;
end;

procedure TfWstTypeLibraryEdit.actExitExecute(Sender: TObject);
begin
  Close();
end;

procedure TfWstTypeLibraryEdit.actAboutExecute(Sender: TObject);
var
  fa : TfAbout;
begin
  fa := TfAbout.Create(Self);
  try
    fa.ShowModal();
  finally
    fa.Release();
  end;
end;

procedure TfWstTypeLibraryEdit.actArrayCreateExecute(Sender : TObject);
var
  e : TPasArrayType;
begin
  e := CreateArray(FSymbolTable);
  if Assigned(e) then begin
    FindPainter(e).Paint(FSymbolTable,e,GetTypeNode());
  end;
end;

procedure TfWstTypeLibraryEdit.actCloneExecute(Sender : TObject);
var
  o : TPasElement;
  nd, nd_1 : TTreeNode;
  locHandler : TObjectUpdaterClass;
  locNewItem : TPasElement;
begin
  nd := trvSchema.Selected;
  if Assigned(nd) and Assigned(nd.Data) then begin
    o := TPasElement(nd.Data);
    if HasEditor(o,etClone,locHandler) then begin
      locNewItem := locHandler.CloneObject(o,FSymbolTable);
      if ( locNewItem <> nil ) then begin
        trvSchema.BeginUpdate();
        try
          nd_1 := FindPainter(locNewItem).Paint(FSymbolTable,locNewItem,GetTypeNode());   
          if ( nd_1 <> nil ) then begin
            nd_1.Expand(True);
            trvSchema.Selected := nd_1;
            trvSchema.MakeSelectionVisible();
          end;
        finally
          trvSchema.EndUpdate();
        end;
      end;  
    end;
  end; 
end;

procedure TfWstTypeLibraryEdit.actCloneUpdate(Sender : TObject);
begin
  TAction(Sender).Enabled :=
    Assigned(trvSchema.Selected) and
    Assigned(trvSchema.Selected.Data) and
    HasEditor(TPasElement(trvSchema.Selected.Data),etClone);
end;

procedure TfWstTypeLibraryEdit.actCompoundCreateExecute(Sender: TObject);
var
  e : TPasClassType;
begin
  e := CreateClassObject(FSymbolTable);
  if Assigned(e) then begin
    FindPainter(e).Paint(FSymbolTable,e,GetTypeNode());
  end;
end;

procedure TfWstTypeLibraryEdit.actDeleteExecute (Sender : TObject );
var
  o : TPasElement;
  nd : TTreeNode;
begin
  nd := trvSchema.Selected;
  if Assigned(nd) and Assigned(nd.Data) then begin
    o := TPasElement(nd.Data);
    if HasEditor(o,etDelete) then begin
      if ( MessageDlg(Format('Delete this object "%s" ?',[o.Name]),mtConfirmation,mbYesNo,0) = mrYes ) then begin
        DeleteObject(o,FSymbolTable);
        trvSchema.BeginUpdate();
        try
          FreeAndNil(nd);
        finally
          trvSchema.EndUpdate();
        end;
      end;
    end;
  end;
end;

procedure TfWstTypeLibraryEdit.actDeleteUpdate(Sender : TObject);
begin
  TAction(Sender).Enabled :=
    Assigned(trvSchema.Selected) and
    Assigned(trvSchema.Selected.Data) and
    HasEditor(TPasElement(trvSchema.Selected.Data),etDelete);
end;

procedure TfWstTypeLibraryEdit.actEnumCreateExecute(Sender: TObject);
var
  e : TPasEnumType;
begin
  e := CreateEnum(FSymbolTable);
  if Assigned(e) then begin
    FindPainter(e).Paint(FSymbolTable,e,GetTypeNode());
  end;
end;

procedure TfWstTypeLibraryEdit.actEnumCreateUpdate(Sender : TObject);
begin
  TAction(Sender).Enabled := ( FSymbolTable <> nil );
end;

procedure TfWstTypeLibraryEdit.actExportExecute(Sender: TObject);
var
  curLok : IInterface;
  saveOpts : TComandLineOptions;
  f : TfrmSaveOptions;
begin
  f := TfrmSaveOptions.Create(nil);
  try
    if ( f.ShowModal() = mrOK ) then begin
      saveOpts := [];
      if f.edtBinder.Checked then
        Include(saveOpts,cloBinder);
      if f.edtInterface.Checked then
        Include(saveOpts,cloInterface);
      if f.edtImplementation.Checked then
        Include(saveOpts,cloImp);
      if f.edtProxy.Checked then
        Include(saveOpts,cloProxy);
      if f.edtWrappedParams.Checked then
        Include(saveOpts,cloHandleWrappedParameters);
      if f.edtDocAsComments.Checked then
        Include(saveOpts,cloGenerateDocAsComments);

      curLok := SetCursorHourGlass();
      GenerateSource(
        FSymbolTable,
        saveOpts,
        otFileSystem,
        IncludeTrailingBackslash(f.edtOutputDir.Text),
        nil
      );
      curLok := nil;
    end;
  finally
    f.Release();
  end;
end;

procedure TfWstTypeLibraryEdit.actExportUpdate(Sender: TObject);
begin
  //TAction(Sender).Enabled := Assigned(FSymbolTable) and ( FSymbolTable.CurrentModule.InterfaceSection.Declarations.Count > 0 );
end;

procedure TfWstTypeLibraryEdit.actFullCollapseExecute(Sender: TObject);
begin
  trvSchema.FullCollapse();
end;

procedure TfWstTypeLibraryEdit.actFullExpandExecute(Sender: TObject);
begin
  trvSchema.FullExpand();
end;

procedure TfWstTypeLibraryEdit.actIntfCreateExecute(Sender: TObject);
var
  e : TPasClassType;
begin
  e := CreateInterface(FSymbolTable);
  if Assigned(e) then begin
    FindPainter(e).Paint(FSymbolTable,e,GetInterfaceNode());
  end;
end;

procedure TfWstTypeLibraryEdit.actNewFileExecute(Sender: TObject);
var
  res : Integer;
begin
  res := MessageDlg(Application.Title,'Save the current file before ?',mtConfirmation,mbYesNoCancel,0,mbYes);
  if ( res = mrCancel ) then begin
    Exit;
  end;
  if ( res = mrYes ) then begin
    actSave.Execute();
  end;
  FCurrentFileName := DEF_FILE_NAME;
  {$IFDEF WST_IDE}
  FProjectLibrary := nil;
  {$ENDIF}
  FreeAndNil(FSymbolTable);
  FSymbolTable := CreateSymbolTable(ExtractFileName(FCurrentFileName));
  RenderSymbols();
end;

procedure TfWstTypeLibraryEdit.RenderSymbols();
var
  objPtr : ISymbolPainter;
  nd : TTreeNode;
begin
  mmoLog.Clear();
  trvSchema.BeginUpdate();
  try
    trvSchema.Items.Clear();
    srcInterface.ClearAll();
    nd := trvSchema.Items.AddFirst(nil,'Type Library Editor');
    nd.ImageIndex := -1;
    nd.StateIndex := -1;
    nd.SelectedIndex := -1;
    if Assigned(FSymbolTable) then begin
      objPtr := FindPainter(FSymbolTable.Package);
      if Assigned(objPtr) then begin
        objPtr.Paint(FSymbolTable,FSymbolTable.Package,nd);
      end;
      RenderSources();
      RenderWSDL();
    end;
    trvSchema.Items[0].Expand(False);
    trvSchema.Items[0].Items[0].Expand(False);
  finally
    trvSchema.EndUpdate();
  end;
  ShowStatusMessage(mtInfo,'');
end;

procedure TfWstTypeLibraryEdit.RenderSources();

  procedure LoadText(const AList : TStrings; ASrc : ISourceStream);
  var
    srcItemSV : ISavableSourceStream;
    strm : TStream;
  begin
    if Supports(ASrc,ISavableSourceStream,srcItemSV) then begin
      strm := srcItemSV.GetStream();
      strm.Position := 0;
      AList.LoadFromStream(strm);
    end;
  end;

var
  srcMngr : ISourceManager;
begin
  if Assigned(FSymbolTable) then begin
    if ( FSymbolTable.CurrentModule.InterfaceSection.Declarations.Count > 0 ) then begin
      srcMngr := GenerateSource(FSymbolTable,[cloInterface,cloProxy,cloBinder,cloImp,cloGenerateDocAsComments],otMemory,'',@ShowStatusMessage);
      if Assigned(srcMngr) and ( srcMngr.GetCount() > 0 ) then begin
        LoadText(srcInterface.Lines,srcMngr.GetItem(0));
        LoadText(srcProxy.Lines,srcMngr.GetItem(1));
        LoadText(srcBinder.Lines,srcMngr.GetItem(2));
        LoadText(srcImp.Lines,srcMngr.GetItem(3));
      end;
    end else begin
      srcInterface.ClearAll();
      srcProxy.ClearAll();
      srcBinder.ClearAll();
      srcImp.ClearAll();
    end;
  end;
  ShowStatusMessage(mtInfo,'');
end;

procedure TfWstTypeLibraryEdit.RenderWSDL();
var
  mstrm : TMemoryStream;
begin
  mstrm := TMemoryStream.Create();
  try
    GenerateWSDL_ToStream(FSymbolTable,mstrm);
    mstrm.Position := 0;
    srcWSDL.Lines.LoadFromStream(mstrm);
  finally
    FreeAndNil(mstrm);
  end;
end;

procedure TfWstTypeLibraryEdit.ShowDocumentation();
var
  docString : string;
  props : TStrings;
  elt : TPasElement;
begin
  docString := '';
  if ( trvSchema.Selected <> nil ) and
     ( trvSchema.Selected.Data <> nil ) and
     TObject(trvSchema.Selected.Data).InheritsFrom(TPasElement)
  then begin
    elt := TPasElement(trvSchema.Selected.Data);
    props := FSymbolTable.Properties.FindList(elt);
    if ( props <> nil ) then
      docString :=
        StringReplace(DecodeLineBreak(props.Values[s_documentation]),#10,sLineBreak,[rfReplaceAll]);
    if IsStrEmpty(docString) then
      docString := '< No documentation available for this item. >';
    docString := Format('%s : %s%s',[FSymbolTable.GetExternalName(elt),sLineBreak,docString]);
  end;
  edtDocumentation.Lines.Text := docString;
end;

procedure TfWstTypeLibraryEdit.ShowSourceXSD();
var
  docString : string;
  elt : TPasElement;
begin
  docString := '';
  if ( trvSchema.Selected <> nil ) and
     ( trvSchema.Selected.Data <> nil ) and
     TObject(trvSchema.Selected.Data).InheritsFrom(TPasType)
  then begin
    elt := TPasElement(trvSchema.Selected.Data);
    docString := XsdGenerateSourceForObject(elt,FSymbolTable);
  end;
  edtXSD.Lines.Text := docString;
end;

procedure TfWstTypeLibraryEdit.ShowDependencies();
var
  elt : TPasType;
begin
  if ( csDestroying in Self.ComponentState ) then
    Exit;
  if ( FDependencyList = nil ) then
    FDependencyList := TObjectList.Create(True);
  FDependencyList.Clear();
  if ( trvSchema.Selected <> nil ) and
     ( trvSchema.Selected.Data <> nil ) and
     TObject(trvSchema.Selected.Data).InheritsFrom(TPasType)
  then begin
    elt := TPasType(trvSchema.Selected.Data);
    FindDependencies(elt,FSymbolTable,FDependencyList);
  end;
  DrawDependencies(tvDependency,FDependencyList);
end;

procedure TfWstTypeLibraryEdit.OpenFile (const AFileName : string; const AContent : TStream);
var
  tmpTable : TwstPasTreeContainer;
  curLok : IInterface;
begin
  {$IFDEF WST_IDE}
  FProjectLibrary := nil;
  {$ENDIF}
  FCurrentFileName := '';
  mmoLog.Clear();
  PC.ActivePage := tsLog;
  curLok := SetCursorHourGlass();
  if SameText('.pas',ExtractFileExt(AFileName)) then begin
    tmpTable := ParsePascalFile(AFileName,@ShowStatusMessage);
  end else if SameText('.xsd',ExtractFileExt(AFileName)) then begin
    if ( AContent = nil ) then
      tmpTable := ParseXsdFile(AFileName,@ShowStatusMessage)
    else
      tmpTable := ParseXsdFile(AFileName,AContent,@ShowStatusMessage);
  end else begin
    if ( AContent = nil ) then
      tmpTable := ParseWsdlFile(AFileName,@ShowStatusMessage)
    else
      tmpTable := ParseWsdlFile(AFileName,AContent,@ShowStatusMessage);
  end;
  if Assigned(tmpTable) then begin
    if AnsiSameText('.pas',ExtractFileExt(AFileName)) then
      FCurrentFileName := ChangeFileExt(AFileName,'.wsdl')
    else
      FCurrentFileName := AFileName;
    trvSchema.Items.Clear();
    FreeAndNil(FSymbolTable);
    FSymbolTable := tmpTable;
    RenderSymbols();
    PC.ActivePage := tsInterface;
  end;
  curLok := nil;
  SB.Panels[0].Text := FCurrentFileName;
{$IFNDEF WST_IDE}
  DM.Options.WriteString(ClassName(),sLAST_PATH,ExtractFilePath(FCurrentFileName));
{$ENDIF WST_IDE}
end;

procedure TfWstTypeLibraryEdit.SaveToFile (const AFileName : string );
var
  mstrm : TMemoryStream;
begin
  mstrm := TMemoryStream.Create();
  try
    if SameText('.xsd',ExtractFileExt(AFileName)) then
      GenerateXSD_ToStream(FSymbolTable,mstrm)
    else
      GenerateWSDL_ToStream(FSymbolTable,mstrm);
    mstrm.SaveToFile(AFileName);
  finally
    FreeAndNil(mstrm);
  end;
{$IFNDEF WST_IDE}
  DM.Options.WriteString(ClassName(),sLAST_PATH,ExtractFilePath(AFileName));
{$ENDIF WST_IDE}
end;

constructor TfWstTypeLibraryEdit.Create(AOwner: TComponent);
begin
  if ( DM = nil ) then begin
    DM := TDM.Create(Application);
  end;
  inherited Create(AOwner);
  FSymbolTable := CreateSymbolTable(ExtractFileName(DEF_FILE_NAME));
  trvSchema.Images := DM.IM;
end;

destructor TfWstTypeLibraryEdit.Destroy();
begin
  FDependencyList.Free();
  trvSchema.Items.Clear();
  FreeAndNil(FSymbolTable);
  inherited Destroy();
  FreeAndNil(DM);
end;

initialization
  //SetHeapTraceOutput('heap_trace.txt');
  
  {$I uwsttypelibraryedit.lrs}

end.

