unit Scaffolding.Wizard;

{$I XData.inc}

interface

uses
  ToolsAPI,
  Vcl.Graphics,
  System.SysUtils,
  System.UITypes;

type
  TScaffoldingWizard = class(TNotifierObject, IOTAWizard,
    IOTARepositoryWizard, IOTARepositoryWizard60, IOTARepositoryWizard80,
    IOTARepositoryWizard160, {$IF COMPILERVERSION > 31}IOTARepositoryWizard190, {$IFEND}
    IOTAProjectWizard, IOTAProjectWizard100)
  private
    FIcon : TIcon;
    function GetInstallDir: string;
    function GetScaffoldingDir: string;
  protected
    function BeforeExecute: Boolean;
    function GetConnectionProject: IOTAProject;
    function UnitExists(AUnitIdent: string): Boolean;
    function FindNewProjectName(const AProjectGroup: IOTAProjectGroup; const AProjectPath: string): string;
    function ProjectExists(const AProjectGroup: IOTAProjectGroup; const AProjectPath, AProject: string): Boolean;
    function GetActiveProjectGroup: IOTAProjectGroup;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    // IOTAWizard
    procedure Execute;
    function GetIDString: string;
    function GetName: string;
    function GetState: TWizardState;
    procedure AfterSave;
    procedure BeforeSave;
    procedure Destroyed;
    procedure Modified;
    // IOTARepositoryWizard
    function GetAuthor: string;
    function GetComment: string;
    function GetGlyph: Cardinal;
    function GetPage: string;
    // IOTARepositoryWizard60
    function GetDesigner: string;
    // IOTARepositoryWizard80
    function GetGalleryCategory: IOTAGalleryCategory;
    function GetPersonality: string;
    // IOTARepositoryWizard160
    function GetFrameworkTypes: TArray<string>;
    function GetPlatforms: TArray<string>;
    // IOTARepositoryWizard190
    function GetSupportedPlatforms: TArray<string>;
    // IOTAProjectWizard
    // IOTAProjectWizard100
    function IsVisible(Project: IOTAProject): Boolean;
  end;

implementation

uses
  System.Classes, System.IOUtils,
  System.Win.Registry,
  Winapi.Windows,

  Vcl.Dialogs,

  Scaffolding.Types,
  Scaffolding.Wizard.Form,
  Scaffolding.SourceGenerator;

{ TScaffoldingWizard }

procedure TScaffoldingWizard.AfterSave;
begin

end;

function TScaffoldingWizard.BeforeExecute: Boolean;
begin
  Result := True; // Can always execute this wizard
end;

function TScaffoldingWizard.GetConnectionProject: IOTAProject;
var
  ModuleServices: IOTAModuleServices;
  Module: IOTAModule;
  OwnerModule: IOTAModule;
begin
  Result := nil;
  ModuleServices := (BorlandIDEServices as IOTAModuleServices);
  Module := ModuleServices.CurrentModule;

  if Module <> nil then
  begin
    if Module.OwnerModuleCount > 0 then
    begin
      OwnerModule := Module.OwnerModules[0];
      if OwnerModule <> nil then
        if OwnerModule.QueryInterface(IOTAProject, Result) <> S_OK then
          Result := nil;
    end;
  end;

  if Result = nil then
    Result := ToolsApi.GetActiveProject;
end;

function TScaffoldingWizard.UnitExists(AUnitIdent: string): Boolean;
var
  Services: IOTAModuleServices;
  Module: IOTAModule;
  I: integer;
begin
  Result := False;
  if Supports(BorlandIDEServices, IOTAModuleServices, Services) then
  begin
    for I := 0 to Services.ModuleCount - 1 do
    begin
      Module := Services.Modules[I];
      if SameText(AUnitIdent + '.pas', ExtractFileName(Module.FileName)) then
      begin
        Result := True;
        Break;
      end;
    end;
  end;
end;

procedure TScaffoldingWizard.BeforeSave;
begin

end;

constructor TScaffoldingWizard.Create;
var
  CategoryManager : IOTAGalleryCategoryManager;
begin
  CategoryManager := (BorlandIDEServices as IOTAGalleryCategoryManager);

  CategoryManager.AddCategory(CategoryManager.FindCategory(sCategoryDelphiNew),
    'TMS XData', 'TMS XData');

  FIcon := nil;
end;

destructor TScaffoldingWizard.Destroy;
begin
  FIcon.Free;
  inherited;
end;

procedure TScaffoldingWizard.Destroyed;
begin

end;

function TScaffoldingWizard.GetInstallDir: string;
var
  Reg: TRegistry;
begin
  Result := '';
  Reg := TRegistry.Create;
  try
    if Reg.OpenKey('software\tmssoftware\TMS XData', False) then
      if Reg.ValueExists('InstallDir') then
        Result := Reg.ReadString('InstallDir');
  finally
    Reg.Free;
  end;
end;

procedure TScaffoldingWizard.Execute;
const
  BDSPROJECTSDIR = 'BDSPROJECTSDIR';
var
  Wizard: TFScaffoldingWizard;
  Generator: TScaffoldingSourceGenerator;
  DestinationBasePath: string;
  Config: TScaffoldingConfig;
  lModuleServices: IOTAModuleServices;
begin
  DestinationBasePath := GetEnvironmentVariable(BDSPROJECTSDIR);
  if DestinationBasePath = '' then
    DestinationBasePath := ExtractFilePath(ParamStr(0));

  Config := TScaffoldingConfig.Create;
  Wizard := TFScaffoldingWizard.Create(Config);
  try
    Config.ProjectDestinationBasePath := DestinationBasePath;
    Wizard.edServerUrl.Text := 'http://localhost:2001/tms/xdata';
    if Wizard.ShowModal = mrOk then
    begin
      Config.TemplatePath := GetScaffoldingDir;
      Config.AuthUrl := '';
      Generator := TScaffoldingSourceGenerator.Create(Config);
      try
        Generator.Execute;
        if Supports(BorlandIDEServices, IOTAModuleServices, lModuleServices) then
        begin
          lModuleServices.OpenModule(Generator.DprojFileName);
        end;
      finally
        Generator.Free;
      end;
    end;
  finally
    Wizard.Release;
    Config.Free;
  end;
end;

function TScaffoldingWizard.FindNewProjectName(
  const AProjectGroup: IOTAProjectGroup; const AProjectPath: string): string;
const
  cProject = 'Project%d';
var
  lCount: Integer;
begin
  lCount := 1;
  while ProjectExists(AProjectGroup, AProjectPath, Format(cProject, [lCount])) do
    Inc(lCount);

  Result := Format(cProject, [lCount]);
end;

function TScaffoldingWizard.GetActiveProjectGroup: IOTAProjectGroup;
var
  lServices: IOTAModuleServices;
begin
  if Supports(BorlandIDEServices, IOTAModuleServices, lServices) then
    Result := lServices.MainProjectGroup
  else
    Result := nil;
end;

function TScaffoldingWizard.GetAuthor: string;
begin
  Result := 'TMS Software';
end;

function TScaffoldingWizard.GetComment: string;
begin
  Result := 'Creates a new TMS XData Web Application';
end;

function TScaffoldingWizard.GetDesigner: string;
begin
  Result := dVCL;
end;

function TScaffoldingWizard.GetFrameworkTypes: TArray<string>;
begin
  Result := nil;
end;

function TScaffoldingWizard.GetGalleryCategory: IOTAGalleryCategory;
begin
  Result := (BorlandIDEServices as IOTAGalleryCategoryManager)
    .FindCategory('TMS XData');
end;

function TScaffoldingWizard.GetGlyph: Cardinal;
var
  ResName : string;
begin
  if not Assigned(FIcon) then begin
    FIcon := TIcon.Create;
    ResName := 'XDATAWEBGENERATORICON';
    FIcon.LoadFromResourceName(HInstance, ResName);
  end;

  Result := CopyIcon(FIcon.Handle);
end;

function TScaffoldingWizard.GetIDString: string;
begin
  Result := 'TMS.XData.Scaffolding.Web';
end;

function TScaffoldingWizard.GetName: string;
begin
  Result := 'TMS XData Web Application';
end;

function TScaffoldingWizard.GetPage: string;
begin
  Result := 'TMS XData';
end;

function TScaffoldingWizard.GetPersonality: string;
begin
  Result := sDelphiPersonality;
end;

function TScaffoldingWizard.GetPlatforms: TArray<string>;
begin
  Result := nil;
end;

function TScaffoldingWizard.GetScaffoldingDir: string;
const
  ENVVAR_SCAFFOLDINGDIR = 'XDATASCAFFOLDINGDIR';
var
  InstallDir: string;
begin
  Result := GetEnvironmentVariable(ENVVAR_SCAFFOLDINGDIR);
  if Result = '' then
  begin
    InstallDir := GetInstallDir;
    Result := TPath.Combine(InstallDir, 'scaffolding');
  end;
  Result := TPath.Combine(Result, 'templates\sbadmin\');
  if not TDirectory.Exists(Result) then
    raise EXDataScaffoldingException.CreateFmt('The scaffolding directory was not found: %s', [Result]);
end;

function TScaffoldingWizard.GetState: TWizardState;
begin
  Result := [wsEnabled];
end;

function TScaffoldingWizard.GetSupportedPlatforms: TArray<string>;
begin
  Result := nil;
end;

function TScaffoldingWizard.IsVisible(Project: IOTAProject): Boolean;
begin
  Result := True;
end;

procedure TScaffoldingWizard.Modified;
begin

end;

function TScaffoldingWizard.ProjectExists(const AProjectGroup: IOTAProjectGroup;
  const AProjectPath, AProject: string): Boolean;
const
  cDprojExt = '.dproj';
var
  lCount: Integer;
  lName: string;
  lProject: string;
begin
  Result := False;

  if TFile.Exists(TPath.Combine(AProjectPath, AProject) + cDprojExt) then
    Exit(True);

  if AProjectGroup <> nil then
  begin
    for lCount := 0 to AProjectGroup.ProjectCount - 1 do
    begin
      lName := AProjectGroup.Projects[lCount].FileName;
      lName := TPath.GetFileName(lName);
      lName := UpperCase(lName);

      lProject := AProject + TPath.GetExtension(lName);
      lProject := UpperCase(lProject);

      if lProject = lName then
        Exit(True);
    end;
  end;
end;

end.
