unit TMS.ServerWizard.Files;

{$I bcl.inc}

interface

{$IFDEF DELPHIXE2_LVL}

uses
  ToolsAPI, TMS.ServerWizard.Types;

type
  TToolsAPIFile = class(TInterfacedObject, IOTAFile)
  private
    FIdentifierMap : TIdentifierMap;
  protected
    function ReplaceIdentifiers(Input : string) : string;
  public
    constructor Create(const IdentifierMap : TIdentifierMap);

    // IOTAFile
    function GetSource: string; virtual;
    function GetAge: TDateTime;
    property Source: string read GetSource;
    property Age: TDateTime read GetAge;
  end;

  TFileFromResource = class(TToolsAPIFile)
  private
    FResourceName : string;
    function LoadFromResource : string;
  public
    constructor Create(const ResourceName : string; const IdentifierMap : TIdentifierMap);
    function GetSource: string; override;
  end;

  TFileFromString = class(TToolsAPIFile)
  private
    FContents : string;
  public
    constructor Create(const Contents : string; const IdentifierMap : TIdentifierMap);
    function GetSource: string; override;
  end;

  function GetDefaultFolder : string;

{$ENDIF}

implementation

{$IFDEF DELPHIXE2_LVL}

uses
  System.Classes, Winapi.Windows, System.SysUtils, System.Generics.Collections,
  TMS.ServerWizard.ToolsAPI.Utils;

function ExpandEnvironmentVariable(const EnvVar: string): string;
var
  BufSize: Integer;
begin
  // Get required buffer size
  BufSize := ExpandEnvironmentStrings(PChar(EnvVar), nil, 0);
  if BufSize > 0 then begin
    SetLength(Result, BufSize - 1);
    ExpandEnvironmentStrings(PChar(EnvVar), PChar(Result), BufSize);
  end else
    Result := '';
end;

function GetDefaultFolder : string;
var
  PG : IOTAProjectGroup;
  Project : IOTAProject;
begin
  // Where should files be located by default?
  // If there is a project group with an active project, use that folder,
  // otherwise use the BDS default dir, BDSPROJECTSDIR - that is,
  // C:\Users\[PROFILE]\Documents\Embarcadero\Studio\Projects

  PG := CurrentProjectGroup;
  if Assigned(PG) then
    Project := PG.ActiveProject;

  if Assigned(Project) then
    Result := ExtractFilePath(Project.FileName)
  else
    Result := ExpandEnvironmentVariable('%BDSPROJECTSDIR%');
end;

{ TToolsAPIFile }

constructor TToolsAPIFile.Create(const IdentifierMap: TIdentifierMap);
begin
  inherited Create();
  FIdentifierMap := IdentifierMap;
end;

function TToolsAPIFile.GetAge: TDateTime;
begin
  Result := -1; // New;
end;

function TToolsAPIFile.GetSource: string;
begin
  Result := 'ERROR - File contents here';
end;

function TToolsAPIFile.ReplaceIdentifiers(Input: string): string;
var
  FromTo : TPair<string, string>;
begin
  if not Assigned(FIdentifierMap) then
    Exit(Input);

  // Do this twice, in case some identifiers refer to others. Eg, the XData pool declaration
  // (filled in from resource) refers to the connection module classname
  for FromTo in FIdentifierMap do
    Input := StringReplace(Input, FromTo.Key, FromTo.Value, [rfReplaceAll, rfIgnoreCase]);
  for FromTo in FIdentifierMap do
    Input := StringReplace(Input, FromTo.Key, FromTo.Value, [rfReplaceAll, rfIgnoreCase]);

  Result := Input;
end;

{ TFileFromResource }

constructor TFileFromResource.Create(const ResourceName: string;
  const IdentifierMap : TIdentifierMap);
begin
  inherited Create(IdentifierMap);
  FResourceName := ResourceName;
  FIdentifierMap := IdentifierMap;
end;

function TFileFromResource.GetSource: string;
begin
  // Load the source from a compiled resource, and then replace all identifiers
  Result := LoadFromResource;
  Result := ReplaceIdentifiers(Result);
end;

function TFileFromResource.LoadFromResource: string;
var
  Stream : TResourceStream;
  Bytes : TBytes;
  Encoding : TEncoding;
begin
  Encoding := nil;
  Stream := TResourceStream.Create(HInstance, FResourceName, RT_RCDATA);
  try
    if Stream.Size > 0 then begin
      SetLength(Bytes, Stream.Size);
      Stream.ReadBuffer(Bytes[0], Stream.Size);
      TEncoding.GetBufferEncoding(Bytes, Encoding);
      Result := Encoding.GetString(Bytes);
    end else
      raise Exception.Create(Format('Resource %s is empty', [FResourceName]));
  finally
    SetLength(Bytes, 0);
    Stream.Free;
  end;
end;

{ TFileFromString }

constructor TFileFromString.Create(const Contents: string;
  const IdentifierMap: TIdentifierMap);
begin
  inherited Create(IdentifierMap);
  FContents := Contents;
end;

function TFileFromString.GetSource: string;
begin
  Result := ReplaceIdentifiers(FContents);
end;

{$ENDIF}

end.
